Compare commits
1 Commits
feat/invit
...
fix/171-cr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6614f18ba5 |
@@ -1,140 +0,0 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
description: "Overview and quick start to run Kan on your own infrastructure using Docker Compose."
|
||||
mode: "wide"
|
||||
tag: "NEW"
|
||||
---
|
||||
|
||||
This guide introduces how to self-host Kan. It starts with the minimal Docker Compose setup (web + PostgreSQL) and points you to optional features like email and S3-based file storage.
|
||||
|
||||
## What you’ll set up
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Kan" icon="globe" color="#0284c7" horizontal>
|
||||
Next.js application served on port 3000.
|
||||
</Card>
|
||||
<Card title="PostgreSQL 15" icon="database" color="#65a30d" horizontal>
|
||||
Primary database for Kan data.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
For file uploads (avatars), OAuth, and other advanced options, see the
|
||||
Environment Variables section in the README and the dedicated [S3
|
||||
guide](/guides/self-hosting/s3). The [full
|
||||
compose](https://github.com/kanbn/kan/blob/main/docker-compose.yml) in the
|
||||
repo includes a richer configuration via <code>.env</code>.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker and Docker Compose
|
||||
- A long random string for <code>BETTER_AUTH_SECRET</code> (32+ chars)
|
||||
|
||||
## Quick start
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a docker-compose.yml">
|
||||
Paste the following minimal configuration into a new <code>docker-compose.yml</code> file:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
image: ghcr.io/kanbn/kan:latest
|
||||
container_name: kan-web
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
- kan-network
|
||||
environment:
|
||||
NEXT_PUBLIC_BASE_URL: http://localhost:3000
|
||||
BETTER_AUTH_SECRET: your_auth_secret
|
||||
POSTGRES_URL: postgresql://kan:your_postgres_password@postgres:5432/kan_db
|
||||
NEXT_PUBLIC_ALLOW_CREDENTIALS: true
|
||||
depends_on:
|
||||
- postgres
|
||||
restart: unless-stopped
|
||||
|
||||
postgres:
|
||||
image: postgres:15
|
||||
container_name: kan-db
|
||||
environment:
|
||||
POSTGRES_DB: kan_db
|
||||
POSTGRES_USER: kan
|
||||
POSTGRES_PASSWORD: your_postgres_password
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
- kan_postgres_data:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- kan-network
|
||||
|
||||
networks:
|
||||
kan-network:
|
||||
|
||||
volumes:
|
||||
kan_postgres_data:
|
||||
```
|
||||
|
||||
<Tip>
|
||||
The example above is intentionally minimal. The repository provides a more feature-complete compose file at [docker-compose.yml](https://github.com/kanbn/kan/blob/main/docker-compose.yml) if you want environment-based configuration, OAuth, S3, and more.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Start the stack">
|
||||
Bring everything up in detached mode:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Once started, open [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Manage the containers">
|
||||
Useful commands while developing or testing:
|
||||
|
||||
- Stop the containers: <code>docker compose down</code>
|
||||
- View logs: <code>docker compose logs -f</code>
|
||||
- Restart: <code>docker compose restart</code>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Configure environment (optional)">
|
||||
For a production-like setup and more features (email, OAuth, file uploads, etc.), create a <code>.env</code> file and set the relevant variables shown in the README’s Environment Variables section.
|
||||
|
||||
<Accordion title="Common variables">
|
||||
```bash
|
||||
# Required
|
||||
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
||||
BETTER_AUTH_SECRET=replace_with_long_random_string
|
||||
POSTGRES_URL=postgresql://kan:your_postgres_password@postgres:5432/kan_db
|
||||
|
||||
# Optional: Email
|
||||
EMAIL_FROM="Kan <hello@mail.kan.bn>"
|
||||
SMTP_HOST=smtp.resend.com
|
||||
SMTP_PORT=465
|
||||
SMTP_USER=resend
|
||||
SMTP_PASSWORD=re_xxxx
|
||||
SMTP_SECURE=true
|
||||
|
||||
# Optional: Auth toggles
|
||||
NEXT_PUBLIC_ALLOW_CREDENTIALS=true
|
||||
NEXT_PUBLIC_DISABLE_SIGN_UP=false
|
||||
```
|
||||
|
||||
<Note type="warning">
|
||||
If you plan to enable file uploads (avatars, etc.), you’ll also need S3 variables (<code>S3_ENDPOINT</code>, <code>S3_ACCESS_KEY_ID</code>, <code>S3_SECRET_ACCESS_KEY</code>, <code>NEXT_PUBLIC_STORAGE_URL</code>, <code>NEXT_PUBLIC_STORAGE_DOMAIN</code>, …). See the S3 guide linked at the top.
|
||||
</Note>
|
||||
</Accordion>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Reference
|
||||
|
||||
- [GitHub README](https://github.com/kanbn/kan/blob/main/README.md#self-hosting-)
|
||||
- [GitHub docker-compose.yml](https://github.com/kanbn/kan/blob/main/docker-compose.yml)
|
||||
@@ -1,397 +0,0 @@
|
||||
---
|
||||
title: "Kan + MinIO (S3)"
|
||||
mode: "wide"
|
||||
tag: "NEW"
|
||||
---
|
||||
|
||||
Deploy Kan with PostgreSQL and MinIO (S3-compatible storage) using Docker Compose, with clear steps and production notes.
|
||||
|
||||
## What you’ll set up
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Kan" icon="globe" color="#0284c7" horizontal>
|
||||
Kan web app (Next.js), on port 3000.
|
||||
</Card>
|
||||
<Card title="PostgreSQL 15" icon="database" color="#65a30d" horizontal>
|
||||
PostgreSQL database for Kan.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
<Card title="MinIO (S3-compatible)" icon="cloud" color="#ca8a04" horizontal>
|
||||
MinIO object storage (console port 9001, S3 API port 9000).
|
||||
</Card>
|
||||
|
||||
## How it works
|
||||
|
||||
- Kan stores data in PostgreSQL.
|
||||
- Kan uploads files (e.g., avatars) to MinIO over the S3 API.
|
||||
- The browser fetches public files directly from MinIO’s public URL.
|
||||
- The Next.js image optimizer in Kan must be explicitly allowed to fetch from your storage host.
|
||||
|
||||
Key domain settings:
|
||||
|
||||
- <code>NEXT_PUBLIC_BASE_URL</code> → the Kan site
|
||||
- <code>NEXT_PUBLIC_STORAGE_URL</code> → the public S3 base URL
|
||||
- <code>NEXT_PUBLIC_STORAGE_DOMAIN</code> → the exact S3 hostname (no scheme)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker and Docker Compose
|
||||
- Open local ports: 3000 (Kan), 5432 (Postgres), 9000/9001 (MinIO)
|
||||
- A long random string for <code>BETTER_AUTH_SECRET</code> (32+ chars)
|
||||
|
||||
<Note type="warning">
|
||||
For production you’ll want a reverse proxy (Traefik/Nginx/Caddy), valid TLS
|
||||
certificates, and DNS for your domains (e.g., <code>kan.example.com</code>,{" "}
|
||||
<code>s3.example.com</code>).
|
||||
</Note>
|
||||
|
||||
## Quick start
|
||||
|
||||
<Tip type="info">
|
||||
Why <code>localtest.me</code>? It resolves to <code>127.0.0.1</code>{" "}
|
||||
automatically, so you can test domain-based configs locally without editing
|
||||
hosts.
|
||||
</Tip>
|
||||
|
||||
<Steps>
|
||||
<Step title="Set environment variables">
|
||||
Provide the minimum required configuration (local example):
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_BASE_URL=http://kan.localtest.me:3000
|
||||
BETTER_AUTH_SECRET=<long random string>
|
||||
POSTGRES_URL=postgresql://kan:<password>@postgres:5432/kan_db
|
||||
|
||||
# MinIO/S3
|
||||
S3_ENDPOINT=http://s3.localtest.me:9000
|
||||
S3_ACCESS_KEY_ID=<minio-access-key>
|
||||
S3_SECRET_ACCESS_KEY=<minio-secret-key>
|
||||
S3_REGION=none
|
||||
S3_FORCE_PATH_STYLE=true
|
||||
|
||||
# Public storage access
|
||||
NEXT_PUBLIC_STORAGE_URL=http://s3.localtest.me:9000
|
||||
NEXT_PUBLIC_STORAGE_DOMAIN=s3.localtest.me
|
||||
NEXT_PUBLIC_AVATAR_BUCKET_NAME=kan
|
||||
```
|
||||
|
||||
<Note type="info">
|
||||
Issue #109 fix: make sure <code>NEXT_PUBLIC_STORAGE_DOMAIN</code> exactly
|
||||
equals the hostname that serves your images (no scheme, no port).
|
||||
</Note>
|
||||
|
||||
Optional (see README for full list): Email (`EMAIL_FROM`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_SECURE`), OAuth/OIDC (`GOOGLE_*`, `GITHUB_*`, `OIDC_*`), auth toggles, Trello import, etc.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Create or review Docker Compose files">
|
||||
You can start from the minimal compose at the repository root (<code>docker-compose.yml</code>) and review the production-oriented settings in <code>cloud/docker-compose.yml</code>.
|
||||
|
||||
Start with the minimal setup (web + postgres + minio) and ensure environment variables are passed to the web service.
|
||||
|
||||
<Accordion title="Docker Compose example">
|
||||
|
||||
```yaml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
container_name: kan-db
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_USER: kan
|
||||
POSTGRES_PASSWORD: changeme
|
||||
POSTGRES_DB: kan_db
|
||||
volumes:
|
||||
- pg_data:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: kan-minio
|
||||
command: server /data --console-address ":9001"
|
||||
ports:
|
||||
- "9000:9000" # S3 API
|
||||
- "9001:9001" # Console
|
||||
environment:
|
||||
# Use the same credentials in your .env
|
||||
# as S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY
|
||||
MINIO_ROOT_USER: minio
|
||||
MINIO_ROOT_PASSWORD: minio123456789
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
restart: unless-stopped
|
||||
|
||||
web:
|
||||
image: ghcr.io/kanbn/kan:latest
|
||||
container_name: kan-web
|
||||
depends_on:
|
||||
- postgres
|
||||
- minio
|
||||
ports:
|
||||
- "3000:3000"
|
||||
# Load variables from .env
|
||||
# (see the "Set environment variables" step)
|
||||
env_file:
|
||||
- .env
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
pg_data:
|
||||
minio_data:
|
||||
```
|
||||
|
||||
<Note type="info">
|
||||
Ensure your <code>.env</code> contains values that match this compose file.
|
||||
For example:
|
||||
<ul>
|
||||
<li>
|
||||
<code>POSTGRES_URL=postgresql://kan:changeme@postgres:5432/kan_db</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>S3_ENDPOINT=http://s3.localtest.me:9000</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>S3_ACCESS_KEY_ID=minio</code> and{" "}
|
||||
<code>S3_SECRET_ACCESS_KEY=minio123456789</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>S3_FORCE_PATH_STYLE=true</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>NEXT_PUBLIC_STORAGE_URL=http://s3.localtest.me:9000</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>NEXT_PUBLIC_STORAGE_DOMAIN=s3.localtest.me</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>NEXT_PUBLIC_AVATAR_BUCKET_NAME=kan</code>
|
||||
</li>
|
||||
</ul>
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Start services">
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Then open:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Kan: <a href="http://kan.localtest.me:3000">http://kan.localtest.me:3000</a>
|
||||
</li>
|
||||
<li>
|
||||
MinIO Console:{" "}
|
||||
<a href="http://minio.localtest.me:9001">http://minio.localtest.me:9001</a>
|
||||
</li>
|
||||
<li>
|
||||
MinIO S3 API:{" "}
|
||||
<a href="http://s3.localtest.me:9000">http://s3.localtest.me:9000</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Initialize MinIO">
|
||||
1) Log into the MinIO Console (http://minio.localtest.me:9001).
|
||||
|
||||
2. Create a bucket (e.g., <code>kan</code>).
|
||||
|
||||
3. For simple public avatars, apply a read-only policy so GET requests are allowed for objects:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": { "AWS": ["*"] },
|
||||
"Action": ["s3:GetBucketLocation", "s3:ListBucket"],
|
||||
"Resource": ["arn:aws:s3:::kan"]
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": { "AWS": ["*"] },
|
||||
"Action": ["s3:GetObject"],
|
||||
"Resource": ["arn:aws:s3:::kan/*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<Note type="warning">
|
||||
Alternatively, keep the bucket private and use presigned URLs. In that case,
|
||||
ensure your server and browser access paths are correctly configured.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the setup">
|
||||
<ul>
|
||||
<li>Sign in to Kan and upload an avatar (Settings).</li>
|
||||
<li>Confirm the object is created in your MinIO bucket.</li>
|
||||
<li>The avatar should render without errors.</li>
|
||||
</ul>
|
||||
|
||||
If you see a 400 from <code>/\_next/image</code> with “url parameter is not allowed”:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<code>NEXT_PUBLIC_STORAGE_DOMAIN</code> must exactly match the S3 hostname
|
||||
that serves images.
|
||||
</li>
|
||||
<li>
|
||||
<code>NEXT_PUBLIC_STORAGE_URL</code> should use the same host (with
|
||||
scheme/port).
|
||||
</li>
|
||||
<li>Ensure you’re using the latest Kan image.</li>
|
||||
</ul>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Production setup
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Local">
|
||||
|
||||
<ul>
|
||||
<li><code>NEXT_PUBLIC_BASE_URL=http://kan.localtest.me:3000</code></li>
|
||||
<li><code>S3_ENDPOINT=http://s3.localtest.me:9000</code></li>
|
||||
<li><code>NEXT_PUBLIC_STORAGE_URL=http://s3.localtest.me:9000</code></li>
|
||||
<li><code>NEXT_PUBLIC_STORAGE_DOMAIN=s3.localtest.me</code></li>
|
||||
<li>Keep <code>S3_FORCE_PATH_STYLE=true</code> for MinIO.</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
<Tab title="Production">
|
||||
|
||||
<ul>
|
||||
<li><code>NEXT_PUBLIC_BASE_URL=https://kan.example.com</code></li>
|
||||
<li><code>S3_ENDPOINT=https://s3.example.com</code></li>
|
||||
<li><code>NEXT_PUBLIC_STORAGE_URL=https://s3.example.com</code></li>
|
||||
<li><code>NEXT_PUBLIC_STORAGE_DOMAIN=s3.example.com</code></li>
|
||||
<li>Keep <code>S3_FORCE_PATH_STYLE=true</code> for MinIO.</li>
|
||||
<li>Put Kan and MinIO behind HTTPS with a reverse proxy (Traefik/Nginx) and valid TLS.</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Files upload but don’t display">
|
||||
<ul>
|
||||
<li>If public: confirm GET is allowed on objects (bucket policy).</li>
|
||||
<li>If private: ensure presigned URLs are generated and valid.</li>
|
||||
<li>403 AccessDenied indicates permissions, not CORS. CORS is not required for simple <code><img></code> GETs.</li>
|
||||
</ul>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Make the bucket public (read-only) with mc" defaultOpen>
|
||||
Use your MinIO root credentials to allow anonymous reads:
|
||||
|
||||
```bash
|
||||
# Replace with your MINIO_ROOT_PASSWORD
|
||||
MINIO_PASS='<your-minio-password>'
|
||||
|
||||
# Point mc at MinIO via the container network (no ports required)
|
||||
docker run --rm --network container:kan-minio minio/mc \
|
||||
mc alias set local http://127.0.0.1:9000 minio "$MINIO_PASS"
|
||||
|
||||
# Allow public downloads from the bucket
|
||||
docker run --rm --network container:kan-minio minio/mc \
|
||||
mc anonymous set download local/kan
|
||||
|
||||
# Optional: verify anonymous status
|
||||
docker run --rm --network container:kan-minio minio/mc \
|
||||
mc anonymous get local/kan
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Alternative: S3 bucket policy (AWS CLI)">
|
||||
If you prefer a bucket policy, apply a public-read policy for objects:
|
||||
|
||||
```json policy.json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "PublicReadGetObject",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": ["s3:GetObject"],
|
||||
"Resource": ["arn:aws:s3:::kan/*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# Use your MinIO root credentials
|
||||
MINIO_PASS='<your-minio-password>'
|
||||
|
||||
docker run --rm --network container:kan-minio \
|
||||
-e AWS_ACCESS_KEY_ID=minio \
|
||||
-e AWS_SECRET_ACCESS_KEY="$MINIO_PASS" \
|
||||
-e AWS_DEFAULT_REGION=us-east-1 -e AWS_S3_FORCE_PATH_STYLE=true \
|
||||
-v "$PWD:/work" amazon/aws-cli \
|
||||
s3api put-bucket-policy --bucket kan --policy file:///work/policy.json \
|
||||
--endpoint-url http://127.0.0.1:9000
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Next.js optimizer 400 — url parameter is not allowed">
|
||||
<ul>
|
||||
<li>
|
||||
Exact match on <code>NEXT_PUBLIC_STORAGE_DOMAIN</code> with your storage
|
||||
host.
|
||||
</li>
|
||||
<li>
|
||||
Same host in <code>NEXT_PUBLIC_STORAGE_URL</code>.
|
||||
</li>
|
||||
<li>Update to the latest Kan image.</li>
|
||||
</ul>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Next/Image: “url parameter is valid but upstream response is invalid”">
|
||||
<ul>
|
||||
<li>This means Next.js accepted the URL, but the upstream returned a non-image (e.g., 403 HTML/XML).</li>
|
||||
<li>Fix: make the bucket/object publicly readable (see above), or use presigned URLs.</li>
|
||||
<li>Sanity test from the web network (replace with your image URL):</li>
|
||||
</ul>
|
||||
|
||||
```bash
|
||||
IMG_URL="https://s3.example.com/kan/path/to/avatar.jpg"
|
||||
|
||||
# Headers/content-type as seen from the app network
|
||||
docker run --rm --network container:kan-web curlimages/curl:8.9.1 \
|
||||
-I -L --max-redirs 5 "$IMG_URL"
|
||||
|
||||
# Quick status + content-type summary
|
||||
docker run --rm --network container:kan-web curlimages/curl:8.9.1 \
|
||||
-s -o /dev/null -w "HTTP:%{http_code} CT:%{content_type} URL:%{url_effective}\n" -L "$IMG_URL"
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Connectivity checks">
|
||||
<ul>
|
||||
<li>The Kan container must reach <code>S3_ENDPOINT</code>.</li>
|
||||
<li>Verify DNS/ports inside the container (e.g., <code>docker exec -it <kan-container> sh</code>).</li>
|
||||
</ul>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## References
|
||||
|
||||
- [Kan README](https://github.com/kanbn/kan/blob/main/README.md)
|
||||
- [Cloud compose reference](https://github.com/kanbn/kan/blob/main/cloud/docker-compose.yml)
|
||||
- [Kan #109 Issue](https://github.com/kanbn/kan/issues/109)
|
||||
@@ -44,18 +44,6 @@
|
||||
"group": "Get Started",
|
||||
"pages": ["introduction"]
|
||||
},
|
||||
{
|
||||
"group": "Guides",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Self-Hosting",
|
||||
"pages": [
|
||||
"guides/self-hosting/introduction",
|
||||
"guides/self-hosting/s3"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Import",
|
||||
"pages": ["imports/trello"]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"version": 0,
|
||||
"locale": {
|
||||
"source": "en",
|
||||
"targets": ["fr", "de", "es", "it", "nl", "ru"]
|
||||
"targets": ["fr", "de", "es", "it", "nl"]
|
||||
},
|
||||
"buckets": {
|
||||
"po": {
|
||||
|
||||
@@ -10,7 +10,6 @@ checksums:
|
||||
"%248%2Fmonth/singular": 4667034934bb2bc3d569c70b94205b51
|
||||
1%20user/singular: 3b547431ab12f0fba84307e6a81109d8
|
||||
A%20powerful%2C%20flexible%20kanban%20app%20that%20helps%20you%20organise%20work%2C%20track%20progress%2C%20and%20deliver%20results%E2%80%94all%20in%20one%20place./singular: d405b83b0d631cb72f4347c10bcbb643
|
||||
Account/singular: 01215c12fb1cdb93bd0c84c1382bef56
|
||||
Account%20deleted/singular: a25da96a1579c4491be0a95669ef18a4
|
||||
Activity/singular: 1948763de8e531483a798b68195e297e
|
||||
Activity%20logs/singular: 8b1f0bb96a905646ecfad1cfdfa42168
|
||||
@@ -30,18 +29,11 @@ checksums:
|
||||
added%20checklist%20item%20%3C0%3E%7B0%7D%3C%2F0%3E/singular: bdd202da20b1fffbec21792c5453f90c
|
||||
added%20label%20%3C0%3E%7B0%7D%3C%2F0%3E/singular: b32be052b3d57de0c9120fa7f9fc86ee
|
||||
Adding%20a%20new%20member%20will%20cost%20an%20additional%20%7Bprice%7D%20(%7BbillingType%7D)%20per%20seat./singular: 12e88573028306110fbc15ef1e714892
|
||||
Adjust%20the%20square%20crop%20to%20fit%20your%20avatar./singular: a4df26bbce6f14c6962fac1324db00a8
|
||||
Admin%20roles/singular: 32a5d78073b9bb9a246773afba8831df
|
||||
All%20systems%20operational/singular: ee943a4046b09e6334cceeea9fda2bfc
|
||||
Already%20have%20an%20account%3F%20%3C0%3E%3C1%3ESign%20in%3C%2F1%3E%3C%2F0%3E/singular: 2959fd276248208b65cb27ed46b20135
|
||||
An%20error%20occurred%20while%20disconnecting%20your%20Trello%20account./singular: 0aa3973b860c1faf8d9123aebf567e40
|
||||
An%20unexpected%20error%20occurred.%20Please%20try%20again%20later./singular: 1b5749b0cca6a62d75a577bee6804074
|
||||
Anyone%20with%20this%20link%20can%20join%20your%20workspace/singular: 2366ed295eb2c03c425559c24cb31606
|
||||
API/singular: 01d9819514e27056dcc69463194b63d2
|
||||
API%20key%20created/singular: 8dbb2b60a719b0d120e774d6666c8c45
|
||||
API%20key%20name/singular: 2d8aeb08b2cce3b750a584bbc5ce6d1d
|
||||
API%20key%20name%20cannot%20exceed%2030%20characters/singular: 3ea1c7d68e1074a75128017b097b727a
|
||||
API%20key%20name%20is%20required/singular: 870970743b14cb56e7fb7b410af0e933
|
||||
API%20keys/singular: 07f3620a30e08136f0b072c9af8d1eef
|
||||
API%20Reference/singular: 7dcc877064bfdf889ec55dbc9e06a242
|
||||
Applicants/singular: 4babd8331a1441e91087f855f43e57f8
|
||||
@@ -82,12 +74,11 @@ checksums:
|
||||
Card%20not%20found/singular: 91509e2f92b0b3b11330b6983139fdbf
|
||||
Card%20title/singular: 7c34f59f4005e6cb3a6ff546ea0b96e3
|
||||
Change%20Password/singular: a552fc5c4189ebc3e2e6018edda7d18f
|
||||
Change%20your%20language%20preferences./singular: 293d49fc3c75e9c425b64bd7126e6b46
|
||||
Change%20the%20language%20of%20the%20app./singular: fb20db145e28ed44aba89c146ded7bfc
|
||||
Check%20your%20inbox/singular: e9a430fcd298def74212238df0f680d6
|
||||
Checklist%20name/singular: 5eb5de823f7ca5a4d97bb41e6a3f675a
|
||||
Clear%20filters/singular: 8f40ab5af527e4b190da94e7b6221379
|
||||
Click%20on%20the%20link%20we've%20sent%20to%20%7BmagicLinkRecipient%7D%20to%20sign%20in./singular: 210b6ff8727f976182ec3f29ea3c7667
|
||||
Close/singular: 2c2e22f8424a1031de89063bd0022e16
|
||||
Code%20Review/singular: a2da6c2339301e7c3ddf068c4ff9f7e8
|
||||
Collaborate%20seamlessly%20with%20your%20team./singular: c5f10e431aaf8b51519bc009a4a4080c
|
||||
Coming%20soon/singular: ee2b0671e00972773210c5be5a9ccb89
|
||||
@@ -108,7 +99,6 @@ checksums:
|
||||
Continue%20with%20%7B0%7D/singular: 2eaf6e1da91e208f7c5fb6bf862fe8a6
|
||||
Control%20who%20can%20view%20and%20edit%20your%20boards./singular: 2a7e0bec29bac26280de707e2fe8bce5
|
||||
Create%20another/singular: 2de8a82a416eb78c0462aa36278edc9a
|
||||
Create%20API%20key/singular: 70ed8431c6ed5f7fdef122cc34f75b41
|
||||
Create%20board/singular: 155b62818bfab0e34f0089e1b34a32f3
|
||||
Create%20card/singular: 32792935dd5837a9433909b04021c22b
|
||||
Create%20checklist/singular: 5cbca15a7004558c4e6d381f83a34da2
|
||||
@@ -121,7 +111,6 @@ checksums:
|
||||
Create%20workspace/singular: 2e6718e79964ea5ce22d76c2189c77ca
|
||||
created%20the%20card/singular: 605475f5aaeb4dbccbf7c4eb9107b43d
|
||||
Critical/singular: eb327cd411b50aee954f8d1d215d003a
|
||||
Crop%20your%20avatar/singular: eb25e2d5972ec36a0b40481c8136ab15
|
||||
Current%20password%20is%20required/singular: 72536bca9598680027f2be8ce80ac280
|
||||
Custom%20domain/singular: b09e7a9c187b7163b4a6cfc78042fe42
|
||||
Custom%20URLs%20require%20upgrading%20to%20a%20Pro%20plan/singular: f7275e3b473b8f7b39dab6b37eb26fea
|
||||
@@ -166,10 +155,7 @@ checksums:
|
||||
Enter%20your%20name/singular: cd95fbdd0533f2c2e8edf9d9bd9aa8df
|
||||
Enter%20your%20new%20password/singular: c67251e3002b68bc20a7cf5de23e43ac
|
||||
Enter%20your%20password/singular: ea4fdd034522dead21bae0c0abb52eae
|
||||
Error/singular: 3c95bcb32c2104b99a46f5b3dd015248
|
||||
Error%20Changing%20Password/singular: ebecb5c1b72ba4b063117241f5ba4f2d
|
||||
Error%20creating%20invite%20link/singular: cbedc3f3213dfc4fdc8b7503ae1a5cd6
|
||||
Error%20deactivating%20invite%20link/singular: ccf42cd5aa8481692003e87e836de66c
|
||||
Error%20deleting%20account/singular: d42965a9bc9e5ec4ed57890268924643
|
||||
Error%20deleting%20label/singular: 94387e3a45ec768ae7715701ae00136e
|
||||
Error%20deleting%20workspace/singular: 0aec9bd8170bc84f5ea5c9a47c52ed26
|
||||
@@ -186,8 +172,6 @@ checksums:
|
||||
Everything%20in%20the%20free%20plan%2C%20plus%3A/singular: 62b44c4973b92b806c69a4b15e0256dc
|
||||
Everything%20you%20need%2C%20free%20forever.%20Unlimited%20boards%2C%20unlimited%20lists%2C%20unlimited%20cards.%20Upgrade%20any%20time./singular: fa21632ab1468edf10acda2fe7b71323
|
||||
Execution/singular: cbac4a3c721123cbc6a883560bf29800
|
||||
Failed%20to%20accept%20invitation.%20Please%20try%20again%20later%2C%20or%20contact%20customer%20support./singular: e4505a9df3a81e93a8a8b103c6e3ebc4
|
||||
Failed%20to%20copy%20invite%20link/singular: 635884d5ed8d6ee20b85a003939b4ae7
|
||||
Failed%20to%20login%20with%20%7B0%7D.%20Please%20try%20again./singular: 669a4b4247a73f53fb9b8b16e42d166f
|
||||
FAQs/singular: dc36d7992372ccd419b39ef51cf5b16c
|
||||
Feature/singular: 58f5f3f37862b6312a2f20ec1a1fd0e8
|
||||
@@ -212,7 +196,6 @@ checksums:
|
||||
Get%20started%20on%20Cloud/singular: ed926f526266ad2063283c58e6f4284a
|
||||
Getting%20started/singular: 8e5e7bd026b5bec46bbfdce02ab9e0b8
|
||||
GitHub/singular: 6e1cf3c00fa6fbe24afcc78ea3b5f3e4
|
||||
Go%20Home/singular: 6251589da1964d55afabdfd64c84c335
|
||||
Go%20to%20app/singular: 896d0441384dcd2bfb3b23d61ff1944d
|
||||
High%20Priority/singular: 5d231ff8254aabc875f194c4b4f49c97
|
||||
Hired/singular: e5a9b1bd409b007141fe3d7890022f9a
|
||||
@@ -238,14 +221,10 @@ checksums:
|
||||
Integrations/singular: 0ccce343287704cd90150c32e2fcad36
|
||||
Interviewing/singular: 4ccdcdc784547e925077c3297bddee95
|
||||
Invalid%20email%20address/singular: b2d9f25626f2d15c7c63e0281bccc247
|
||||
Invalid%20invitation/singular: 4b936a8811a2295a5f58b41473c608f3
|
||||
Invite/singular: 181884cea804cbde665f160811ee7ad0
|
||||
Invite%20link%20copied/singular: 4046f23a78e1cd5166671c3fb8a7ea6e
|
||||
Invite%20link%20copied%20to%20clipboard/singular: 6fc055a0ea0ed1aa58c5e0c502efe17f
|
||||
Invite%20another/singular: acb543563dab7edbcf46060a53ade3a3
|
||||
Invite%20member/singular: ade922db1be6b26bc979565ce5de2bc7
|
||||
Inviting%20members%20requires%20a%20Team%20Plan.%20You'll%20be%20redirected%20to%20upgrade%20your%20workspace./singular: 03eeed2d715e770259f722ba48a61ab3
|
||||
Join%20workspace/singular: f5d035df672b05abd760bd022309c719
|
||||
Join%20workspace%20%7C%20kan.bn/singular: 97855216a0e00214b2dcf917e93164f2
|
||||
Junior/singular: ed1bd2c59a824fdcdd56fc8a0660fe9f
|
||||
Kanban%20is%20better%20with%20a%20team.%20Perfect%20for%20small%20and%20growing%20teams%20looking%20to%20collaborate./singular: a77bee43046b260797c8936ad23e9223
|
||||
Kanban%20reimagined/singular: 613ccfdd9f54c66cbf68cfa313498766
|
||||
@@ -280,7 +259,6 @@ checksums:
|
||||
Name/singular: 9368b5a047572b6051f334af5aa76819
|
||||
Need%20help%3F/singular: 04e7322f2d3ffb2d73ff2f64b71637c8
|
||||
New/singular: 126d036fae5fb6b629728ecb97e6195b
|
||||
New%20API%20key/singular: db3088aedba6e4a99b46451c5b3d36ed
|
||||
New%20board/singular: 63f4e979e29a7fc2f5c09ff91fa75966
|
||||
New%20card/singular: a33f6219a756127f91c2523cfe845a19
|
||||
New%20checklist/singular: 58252b71e9693ae0f4d2d0b72108a569
|
||||
@@ -308,7 +286,6 @@ checksums:
|
||||
Password%20Changed/singular: 1fcebe9ddb46f722a57f195efddc695d
|
||||
Password%20must%20be%20at%20least%208%20characters/singular: 4c30501d085eaccea47af34212bb26a7
|
||||
Passwords%20do%20not%20match/singular: 37ca1f4e0afc9a0b8e9617f767103c92
|
||||
Paused/singular: edb1f7b7219e1c9b7aa67159090d6991
|
||||
Payment%20frequency/singular: 63ded0e4ffb462ca8bd33d38e4691d86
|
||||
Pending/singular: 030a6f3395d5d4efddd3cc67d6009039
|
||||
per%20user%2Fmonth/singular: 72af182c1ba6df6732640f4d8a78d360
|
||||
@@ -320,7 +297,6 @@ checksums:
|
||||
Please%20enter%20a%20valid%20password/singular: 4b32c17e19b79bcbf0bb092c06ba310f
|
||||
Please%20select%20a%20file%20to%20upload./singular: de315bf594047f8ef9307a7fa9285844
|
||||
Please%20try%20again%20later%2C%20or%20contact%20customer%20support./singular: 21ffcf0b00e7cd7b64f7454a95762e1d
|
||||
Please%20try%20again%20later./singular: 325dea6dd0348a27a6818db2c1340c98
|
||||
Pricing/singular: ce27f1aeacccc542a174c4b2bce022b0
|
||||
Priority%20email%20support/singular: 678538c912a770b1e1416ecdb8e299b1
|
||||
Privacy%20policy/singular: 462c6a536b52873e4498785c66dd48c8
|
||||
@@ -348,6 +324,7 @@ checksums:
|
||||
Resolution/singular: 6d8bd9e1bd7dae5ae38c93061d32990e
|
||||
Resources/singular: ec7fb05ed963bb6781a35782b3475502
|
||||
Review/singular: 299f75db25382980b2895622d7712927
|
||||
Revoke/singular: be57685a85b6dfeaeb6eab4e9560b520
|
||||
Roadmap/singular: c60f4a1acf30e566861bf130f13b9ae7
|
||||
Role/singular: 53743bbb6ca938f5b893552e839d067f
|
||||
Run%20on%20your%20own%20infrastructure/singular: eba804911562b8dbf9d69c3e27f1d708
|
||||
@@ -362,15 +339,8 @@ checksums:
|
||||
Send%20feedback/singular: 9631cc08d49da04475b30a0d320ce97c
|
||||
Senior/singular: 3fff865dc00435f82896fc302ea45630
|
||||
Settings/singular: 8df6777277469c1fd88cc18dde2f1cc3
|
||||
Settings%20%7C%20Account/singular: 050e18406849ec057edac877c297c3e1
|
||||
Settings%20%7C%20API/singular: 85101e4b802a09ad9e3f01ff116f0894
|
||||
Settings%20%7C%20Billing/singular: e44cba741d5414035a0b499c5766c203
|
||||
Settings%20%7C%20Integrations/singular: d04992e28016452f6d3d7dcc0b592415
|
||||
Settings%20%7C%20Workspace/singular: 5d0bacf7ff696da940f232df45edfd39
|
||||
Share%20invite%20link/singular: ec5081a1f4e49fd9d782e770438f703b
|
||||
Settings%20%7C%20%7B0%7D/singular: b8fc73080bc9c8f4f1403b2a69bd1ac5
|
||||
Sign%20in/singular: cb8757c7450e17de1e226e82fb0fa4a2
|
||||
Sign%20In/singular: ec7b8f314fe9bc6591006707484ede61
|
||||
Sign%20Up/singular: 0dd2ae69be4618c1f9e615774a4509ca
|
||||
Sign%20up%20%7C%20kan.bn/singular: f3de2a110c90358e6eac07d0b2f663a6
|
||||
Sign%20up%20disabled/singular: 9581b1f75b404ac0ecb7e603e0d4189c
|
||||
Sign%20up%20is%20currently%20disabled.%20Please%20try%20again%20later./singular: c6cb7c455ec053b351a27029158ff166
|
||||
@@ -397,10 +367,8 @@ checksums:
|
||||
Theme/singular: 21fe00b7a518089576fb83c08631107a
|
||||
They%20won't%20be%20able%20to%20access%20this%20workspace./singular: 93b740350fe3430319fbca85349e41d9
|
||||
This%20action%20can't%20be%20undone./singular: cb222ff89715d8c971e8c25d121e1dbd
|
||||
This%20API%20key%20will%20only%20be%20shown%20once.%20Please%20save%20it%20in%20a%20secure%20location./singular: 7df18d2978d317375f780822f8321c4d
|
||||
This%20board%20is%20private%20or%20does%20not%20exist/singular: a217ff3f04463b4df8c86adb6f83c6bc
|
||||
This%20board%20URL%20has%20already%20been%20taken/singular: 1d8b40332a031b5b77a3658e48dd51ca
|
||||
This%20invitation%20link%20is%20invalid%20or%20has%20expired./singular: 11cc7ef8f1512e7e058e1fbbe5644001
|
||||
This%20will%20result%20in%20the%20permanent%20deletion%20of%20all%20data%20associated%20with%20this%20workspace./singular: a31141558af793635c1ddd2fa0a33499
|
||||
This%20will%20result%20in%20the%20permanent%20deletion%20of%20all%20data%20associated%20with%20your%20account./singular: b49224632bd6c3b7f5e462912aeb1081
|
||||
This%20workspace%20URL%20has%20already%20been%20taken/singular: b455329e2a71da677acab91d3a00bad6
|
||||
@@ -409,7 +377,6 @@ checksums:
|
||||
To%20Do/singular: d60813ea824f373462471e092d136eed
|
||||
Toggle%20menu/singular: 29dea3e0b6238874f8c7a27619df8e36
|
||||
Track%20all%20card%20changes%20with%20detailed%20activity%20history./singular: 0d3bac559c71ec4b8734f9f212320de5
|
||||
Trello/singular: b5131f6488b5a439d58db3e22d6de45b
|
||||
Trello%20disconnected/singular: 54b24a3e6c9a7eedd8c8d1060ab1175d
|
||||
Trello%20imports/singular: 6827eca403faa8f89891d17827e72af9
|
||||
Triaging/singular: 1d40799fcae53a8a27688fdae2a48dee
|
||||
@@ -501,8 +468,6 @@ checksums:
|
||||
You%20have%20been%20logged%20in%20successfully./singular: ef8fad1dce13ae4112f17c5258655fea
|
||||
You%20have%20been%20signed%20up%20successfully./singular: f614a6e3b45f5ffb9a3b0fb420fef84b
|
||||
You%20have%20unlimited%20seats%20with%20your%20Pro%20Plan.%20There%20is%20no%20additional%20charge%20for%20new%20members!/singular: e3dc59a5ba7211cd3d8516b3a79d85ca
|
||||
You've%20been%20invited%20to%20join%20a%20workspace%20on%20kan.bn./singular: 257b840726f972f384243a72767f880f
|
||||
You've%20been%20invited%20to%20join%20a%20workspace./singular: 24fc6cdc8740f37a83df85f582f03293
|
||||
Your%20account%20has%20been%20deleted./singular: 8c8d944e07388c5877effdb2c2803dcf
|
||||
Your%20boards%20have%20been%20imported./singular: 403972e7a25afc2415762c1c2b1ec868
|
||||
Your%20display%20name%20has%20been%20updated./singular: 15e5fff36c554c16ec5214427fae1bf4
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { LinguiConfig } from "@lingui/conf";
|
||||
|
||||
const config: LinguiConfig = {
|
||||
locales: ["en", "fr", "de", "es", "it", "nl", "ru"],
|
||||
locales: ["en", "fr", "de", "es", "it", "nl"],
|
||||
sourceLocale: "en",
|
||||
catalogs: [
|
||||
{
|
||||
|
||||
@@ -52,15 +52,6 @@ const config = {
|
||||
// instrumentationHook: true,
|
||||
swcPlugins: [["@lingui/swc-plugin", {}]],
|
||||
},
|
||||
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: "/settings",
|
||||
destination: "/settings/account",
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
// Only allow external images when OIDC is configured (for OIDC provider avatars)
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"react-dom": "catalog:react18",
|
||||
"react-hook-form": "^7.51.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-image-crop": "^11.0.10",
|
||||
"react-lottie-player": "^1.5.5",
|
||||
"react-markdown": "^10.1.0",
|
||||
"superjson": "2.2.1",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { SocialProvider } from "better-auth/social-providers";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
@@ -161,9 +160,6 @@ export function Auth({ setIsMagicLinkSent, isSignUp }: AuthProps) {
|
||||
const { showPopup } = usePopup();
|
||||
const oidcProviderName = "OIDC";
|
||||
|
||||
const redirect = useSearchParams().get("next");
|
||||
const callbackURL = redirect ?? "/boards";
|
||||
|
||||
// Safely get environment variables on client side to avoid hydration mismatch
|
||||
useEffect(() => {
|
||||
const credentialsAllowed =
|
||||
@@ -199,7 +195,7 @@ export function Auth({ setIsMagicLinkSent, isSignUp }: AuthProps) {
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
callbackURL,
|
||||
callbackURL: "/boards",
|
||||
},
|
||||
{
|
||||
onSuccess: () =>
|
||||
@@ -216,7 +212,7 @@ export function Auth({ setIsMagicLinkSent, isSignUp }: AuthProps) {
|
||||
{
|
||||
email,
|
||||
password,
|
||||
callbackURL,
|
||||
callbackURL: "/boards",
|
||||
},
|
||||
{
|
||||
onSuccess: () =>
|
||||
@@ -233,7 +229,7 @@ export function Auth({ setIsMagicLinkSent, isSignUp }: AuthProps) {
|
||||
await authClient.signIn.magicLink(
|
||||
{
|
||||
email,
|
||||
callbackURL,
|
||||
callbackURL: "/boards",
|
||||
},
|
||||
{
|
||||
onSuccess: () => setIsMagicLinkSent(true, email),
|
||||
@@ -254,14 +250,14 @@ export function Auth({ setIsMagicLinkSent, isSignUp }: AuthProps) {
|
||||
// Use oauth2 signin for OIDC provider
|
||||
const result = await authClient.signIn.oauth2({
|
||||
providerId: "oidc",
|
||||
callbackURL,
|
||||
callbackURL: "/boards",
|
||||
});
|
||||
error = result.error;
|
||||
} else {
|
||||
// Use social signin for traditional social providers
|
||||
const result = await authClient.signIn.social({
|
||||
provider,
|
||||
callbackURL,
|
||||
callbackURL: "/boards",
|
||||
});
|
||||
error = result.error;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function Dropdown({
|
||||
<div>
|
||||
<Menu.Button
|
||||
disabled={disabled}
|
||||
className="flex h-7 w-7 items-center justify-center rounded-[5px] hover:bg-light-200 focus:outline-none dark:hover:bg-dark-200"
|
||||
className="flex h-7 w-7 items-center justify-center rounded-[5px] hover:bg-light-200 dark:hover:bg-dark-200"
|
||||
>
|
||||
{children}
|
||||
</Menu.Button>
|
||||
@@ -30,7 +30,7 @@ export default function Dropdown({
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 z-50 mt-2 w-56 origin-top-right rounded-md border border-light-200 bg-light-50 p-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-400 dark:bg-dark-300">
|
||||
<Menu.Items className="absolute right-0 z-30 mt-2 w-56 origin-top-right rounded-md border border-light-200 bg-light-50 p-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-400 dark:bg-dark-300">
|
||||
<div className="flex flex-col">
|
||||
{items.map((item) => (
|
||||
<Menu.Item key={item.label}>
|
||||
|
||||
@@ -13,7 +13,7 @@ export function LanguageSelector() {
|
||||
id="language-select"
|
||||
value={locale}
|
||||
onChange={(e) => setLocale(e.target.value as any)}
|
||||
className="mt-8 block w-full max-w-[180px] rounded-lg border-0 bg-light-50 pl-10 text-sm shadow-sm ring-1 ring-inset ring-light-300 focus:ring-2 focus:ring-inset focus:ring-light-400 dark:bg-dark-50 dark:text-dark-1000 dark:ring-dark-300 dark:focus:ring-dark-500"
|
||||
className="mt-8 block w-full max-w-[180px] rounded-lg border-0 bg-light-50 pl-10 shadow-sm ring-1 ring-inset ring-light-300 focus:ring-2 focus:ring-inset focus:ring-light-400 dark:bg-dark-50 dark:text-dark-1000 dark:ring-dark-300 dark:focus:ring-dark-500 sm:text-sm"
|
||||
>
|
||||
{availableLocales.map((loc) => (
|
||||
<option key={loc} value={loc}>
|
||||
|
||||
@@ -123,7 +123,7 @@ export function NewWorkspaceForm() {
|
||||
body: JSON.stringify({
|
||||
slug: slug || undefined,
|
||||
workspacePublicId: values.publicId,
|
||||
cancelUrl: "/settings/workspace?upgrade=pro",
|
||||
cancelUrl: "/settings?upgrade=pro",
|
||||
successUrl: "/boards",
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxOption,
|
||||
ListboxOptions,
|
||||
} from "@headlessui/react";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
HiChevronDown,
|
||||
HiOutlineBanknotes,
|
||||
HiOutlineCodeBracketSquare,
|
||||
HiOutlineRectangleGroup,
|
||||
HiOutlineUser,
|
||||
} from "react-icons/hi2";
|
||||
|
||||
interface SettingsLayoutProps {
|
||||
children: React.ReactNode;
|
||||
currentTab: string;
|
||||
}
|
||||
|
||||
export function SettingsLayout({ children, currentTab }: SettingsLayoutProps) {
|
||||
const router = useRouter();
|
||||
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||
|
||||
const settingsTabs = [
|
||||
{
|
||||
key: "account",
|
||||
icon: <HiOutlineUser />,
|
||||
label: t`Account`,
|
||||
condition: true,
|
||||
},
|
||||
{
|
||||
key: "workspace",
|
||||
icon: <HiOutlineRectangleGroup />,
|
||||
label: t`Workspace`,
|
||||
condition: true,
|
||||
},
|
||||
{
|
||||
key: "billing",
|
||||
label: t`Billing`,
|
||||
icon: <HiOutlineBanknotes />,
|
||||
condition: env("NEXT_PUBLIC_KAN_ENV") === "cloud",
|
||||
},
|
||||
{
|
||||
key: "api",
|
||||
icon: <HiOutlineCodeBracketSquare />,
|
||||
label: t`API`,
|
||||
condition: true,
|
||||
},
|
||||
{
|
||||
key: "integrations",
|
||||
icon: <HiOutlineCodeBracketSquare />,
|
||||
label: t`Integrations`,
|
||||
condition: true,
|
||||
},
|
||||
];
|
||||
|
||||
const availableTabs = settingsTabs.filter((tab) => tab.condition);
|
||||
|
||||
// Update selected tab when currentTab prop changes
|
||||
useEffect(() => {
|
||||
const tabIndex = availableTabs.findIndex((tab) => tab.key === currentTab);
|
||||
if (tabIndex !== -1) {
|
||||
setSelectedTabIndex(tabIndex);
|
||||
}
|
||||
}, [currentTab, availableTabs]);
|
||||
|
||||
const isTabActive = (tabKey: string) => {
|
||||
return currentTab === tabKey;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||
<div className="h-full max-h-[calc(100vdh-3rem)] overflow-y-auto md:max-h-[calc(100vdh-4rem)]">
|
||||
<div className="m-auto max-w-[1100px] px-5 py-6 md:px-28 md:py-12">
|
||||
<div className="mb-8 flex w-full justify-between">
|
||||
<h1 className="font-bold tracking-tight text-neutral-900 dark:text-dark-1000 sm:text-[1.2rem]">
|
||||
{t`Settings`}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="focus:outline-none">
|
||||
<div className="sm:hidden">
|
||||
{/* Mobile dropdown */}
|
||||
<Listbox
|
||||
value={selectedTabIndex}
|
||||
onChange={(index) => {
|
||||
const tabKey = availableTabs[index]?.key;
|
||||
if (tabKey) {
|
||||
void router.push(`/settings/${tabKey}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="relative mb-4">
|
||||
<ListboxButton className="w-full appearance-none rounded-lg border-0 bg-light-50 py-2 pl-3 pr-10 text-left text-sm text-light-1000 shadow-sm ring-1 ring-inset ring-light-300 focus:ring-2 focus:ring-inset focus:ring-light-400 dark:bg-dark-50 dark:text-dark-1000 dark:ring-dark-300 dark:focus:ring-dark-500">
|
||||
{availableTabs[selectedTabIndex]?.label || "Select a tab"}
|
||||
<HiChevronDown
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-light-900 dark:text-dark-900"
|
||||
/>
|
||||
</ListboxButton>
|
||||
<ListboxOptions className="absolute z-10 mt-1 w-full rounded-lg bg-light-50 py-1 text-sm shadow-lg ring-1 ring-inset ring-light-300 dark:bg-dark-50 dark:ring-dark-300">
|
||||
{availableTabs.map((tab) => (
|
||||
<ListboxOption
|
||||
key={tab.key}
|
||||
value={availableTabs.indexOf(tab)}
|
||||
className="relative cursor-pointer select-none py-2 pl-3 pr-9 text-light-1000 dark:text-dark-1000"
|
||||
>
|
||||
{tab.label}
|
||||
</ListboxOption>
|
||||
))}
|
||||
</ListboxOptions>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
<div className="hidden sm:block">
|
||||
<div className="border-b border-gray-200 dark:border-white/10">
|
||||
<nav
|
||||
aria-label="Tabs"
|
||||
className="-mb-px flex space-x-8 focus:outline-none"
|
||||
>
|
||||
{availableTabs.map((tab) => (
|
||||
<Link
|
||||
key={tab.key}
|
||||
href={`/settings/${tab.key}`}
|
||||
className={`whitespace-nowrap border-b-2 px-1 py-4 text-sm font-medium transition-colors focus:outline-none ${
|
||||
isTabActive(tab.key)
|
||||
? "border-light-1000 text-light-1000 dark:border-dark-1000 dark:text-dark-1000"
|
||||
: "border-transparent text-light-900 hover:border-light-950 hover:text-light-950 dark:text-dark-900 dark:hover:border-white/20 dark:hover:text-dark-950"
|
||||
}`}
|
||||
>
|
||||
{tab.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div className="focus:outline-none">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ export default function WorkspaceMenu({
|
||||
</span>
|
||||
<span
|
||||
className={twMerge(
|
||||
"ml-2 truncate text-sm font-bold text-neutral-900 dark:text-dark-1000",
|
||||
"ml-2 text-sm font-bold text-neutral-900 dark:text-dark-1000",
|
||||
isCollapsed && "md:hidden",
|
||||
)}
|
||||
>
|
||||
@@ -87,17 +87,17 @@ export default function WorkspaceMenu({
|
||||
onClick={() => switchWorkspace(availableWorkspace)}
|
||||
className="flex w-full items-center justify-between rounded-[5px] px-3 py-2 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 items-center">
|
||||
<span className="inline-flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-[5px] bg-indigo-700">
|
||||
<div>
|
||||
<span className="inline-flex h-5 w-5 items-center justify-center rounded-[5px] bg-indigo-700">
|
||||
<span className="text-xs font-medium leading-none text-white">
|
||||
{availableWorkspace.name.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
</span>
|
||||
<span className="ml-2 truncate text-xs font-medium">
|
||||
<span className="ml-2 text-xs font-medium">
|
||||
{availableWorkspace.name}
|
||||
</span>
|
||||
</div>
|
||||
{workspace.publicId === availableWorkspace.publicId && (
|
||||
{workspace.name === availableWorkspace.name && (
|
||||
<span>
|
||||
<HiCheck className="h-4 w-4" aria-hidden="true" />
|
||||
</span>
|
||||
|
||||
@@ -34,7 +34,7 @@ const Modal: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<Transition.Root show={shouldShow} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-50" onClose={closeModal}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
@@ -47,7 +47,7 @@ const Modal: React.FC<Props> = ({
|
||||
<div className="fixed inset-0 bg-light-50 bg-opacity-40 transition-opacity dark:bg-dark-50 dark:bg-opacity-40" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-50 w-screen overflow-y-auto">
|
||||
<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
|
||||
<div className="flex min-h-full items-start justify-center p-4 text-center sm:items-start sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { useState } from "react";
|
||||
|
||||
export function useClipboard({ timeout = 500 } = {}) {
|
||||
const [error, setError] = useState<string | Error | null | undefined>(null);
|
||||
const [copied, setCopied] = useState<boolean>(false);
|
||||
const [copyTimeout, setCopyTimeout] = useState<number | undefined>(undefined);
|
||||
|
||||
const handleCopyResult = (hasError: boolean) => {
|
||||
clearTimeout(copyTimeout);
|
||||
|
||||
setCopyTimeout(
|
||||
setTimeout(() => setCopied(false), timeout) as unknown as number,
|
||||
);
|
||||
|
||||
setCopied(hasError);
|
||||
};
|
||||
|
||||
const copy = (value: string) => {
|
||||
if ("clipboard" in navigator) {
|
||||
navigator.clipboard
|
||||
.writeText(value)
|
||||
.then(() => handleCopyResult(true))
|
||||
.catch((err) => setError(err));
|
||||
} else {
|
||||
setError(new Error("Error: navigator.clipboard is not supported"));
|
||||
}
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
setError(null);
|
||||
setCopied(false);
|
||||
clearTimeout(copyTimeout);
|
||||
};
|
||||
|
||||
return { copy, reset, error, copied };
|
||||
}
|
||||
@@ -36,12 +36,12 @@ msgstr "{0} Labels"
|
||||
msgid "{boardCount, plural, one {Import board (1)} other {Import boards ({boardCount})}}"
|
||||
msgstr "{boardCount, plural, one {Board importieren (1)} other {Boards importieren ({boardCount})}}"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:146
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:92
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$10/month"
|
||||
msgstr "$10/Monat"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$8/month"
|
||||
msgstr "$8/Monat"
|
||||
|
||||
@@ -53,10 +53,6 @@ msgstr "1 Benutzer"
|
||||
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
|
||||
msgstr "Eine leistungsstarke, flexible Kanban-App, die dir hilft, Arbeit zu organisieren, Fortschritte zu verfolgen und Ergebnisse zu liefern – alles an einem Ort."
|
||||
|
||||
#: src/components/SettingsLayout.tsx:33
|
||||
msgid "Account"
|
||||
msgstr "Konto"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:27
|
||||
msgid "Account deleted"
|
||||
msgstr "Konto gelöscht"
|
||||
@@ -95,13 +91,13 @@ msgstr "Beschreibung hinzufügen... (tippe '/' um Befehle zu öffnen oder '@' um
|
||||
msgid "Add details..."
|
||||
msgstr "Details hinzufügen..."
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:109
|
||||
#: src/views/card/components/LabelSelector.tsx:114
|
||||
#: src/views/card/components/LabelSelector.tsx:110
|
||||
#: src/views/card/components/LabelSelector.tsx:118
|
||||
msgid "Add label"
|
||||
msgstr "Label hinzufügen"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:130
|
||||
#: src/views/members/components/InviteMemberForm.tsx:238
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
msgid "Add member"
|
||||
msgstr "Mitglied hinzufügen"
|
||||
|
||||
@@ -136,14 +132,10 @@ msgstr "hat Checklistenelement <0>{0}</0> hinzugefügt"
|
||||
msgid "added label <0>{0}</0>"
|
||||
msgstr "hat Label <0>{0}</0> hinzugefügt"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:306
|
||||
#: src/views/members/components/InviteMemberForm.tsx:187
|
||||
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
|
||||
msgstr "Das Hinzufügen eines neuen Mitglieds kostet zusätzlich {price} ({billingType}) pro Platz."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:275
|
||||
msgid "Adjust the square crop to fit your avatar."
|
||||
msgstr "Passe den quadratischen Zuschnitt an deinen Avatar an."
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:55
|
||||
msgid "Admin roles"
|
||||
msgstr "Administratorrollen"
|
||||
@@ -152,11 +144,11 @@ msgstr "Administratorrollen"
|
||||
msgid "All systems operational"
|
||||
msgstr "Alle Systeme funktionieren"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:88
|
||||
#: src/views/auth/signup/index.tsx:86
|
||||
msgid "Already have an account? <0><1>Sign in</1></0>"
|
||||
msgstr "Du hast bereits ein Konto? <0><1>Anmelden</1></0>"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:61
|
||||
#: src/views/settings/index.tsx:127
|
||||
msgid "An error occurred while disconnecting your Trello account."
|
||||
msgstr "Beim Trennen deines Trello-Kontos ist ein Fehler aufgetreten."
|
||||
|
||||
@@ -164,31 +156,7 @@ msgstr "Beim Trennen deines Trello-Kontos ist ein Fehler aufgetreten."
|
||||
msgid "An unexpected error occurred. Please try again later."
|
||||
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:290
|
||||
msgid "Anyone with this link can join your workspace"
|
||||
msgstr "Jeder mit diesem Link kann deinem Arbeitsbereich beitreten"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:51
|
||||
msgid "API"
|
||||
msgstr "API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:91
|
||||
msgid "API key created"
|
||||
msgstr "API-Schlüssel erstellt"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:161
|
||||
msgid "API key name"
|
||||
msgstr "API-Schlüsselname"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:25
|
||||
msgid "API key name cannot exceed 30 characters"
|
||||
msgstr "Der API-Schlüsselname darf 30 Zeichen nicht überschreiten"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:24
|
||||
msgid "API key name is required"
|
||||
msgstr "API-Schlüsselname ist erforderlich"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:22
|
||||
#: src/views/settings/index.tsx:296
|
||||
msgid "API keys"
|
||||
msgstr "API-Schlüssel"
|
||||
|
||||
@@ -250,21 +218,20 @@ msgstr "Backlog"
|
||||
msgid "Basic Kanban"
|
||||
msgstr "Einfaches Kanban"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed annually"
|
||||
msgstr "jährlich abgerechnet"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed monthly"
|
||||
msgstr "monatlich abgerechnet"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:44
|
||||
#: src/views/boards/components/TemplateBoards.tsx:55
|
||||
#: src/views/settings/BillingSettings.tsx:39
|
||||
#: src/views/settings/index.tsx:235
|
||||
msgid "Billing"
|
||||
msgstr "Abrechnung"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:49
|
||||
#: src/views/settings/index.tsx:245
|
||||
msgid "Billing portal"
|
||||
msgstr "Abrechnungsportal"
|
||||
|
||||
@@ -319,12 +286,12 @@ msgid "Board visibility updated"
|
||||
msgstr "Board-Sichtbarkeit aktualisiert"
|
||||
|
||||
#: src/components/SideNavigation.tsx:68
|
||||
#: src/views/boards/index.tsx:32
|
||||
#: src/views/boards/index.tsx:27
|
||||
msgid "Boards"
|
||||
msgstr "Boards"
|
||||
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/boards/index.tsx:28
|
||||
#: src/views/boards/index.tsx:23
|
||||
msgid "Boards | {0}"
|
||||
msgstr "Boards | {0}"
|
||||
|
||||
@@ -346,7 +313,6 @@ msgstr "Fehlerbericht"
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:63
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:76
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:55
|
||||
#: src/views/settings/components/Avatar.tsx:306
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
|
||||
@@ -362,19 +328,19 @@ msgstr "Karte nicht gefunden"
|
||||
msgid "Card title"
|
||||
msgstr "Kartentitel"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:70
|
||||
#: src/views/settings/AccountSettings.tsx:80
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:109
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:178
|
||||
#: src/views/settings/index.tsx:328
|
||||
#: src/views/settings/index.tsx:338
|
||||
msgid "Change Password"
|
||||
msgstr "Passwort ändern"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:45
|
||||
msgid "Change your language preferences."
|
||||
msgstr "Ändern Sie Ihre Spracheinstellungen."
|
||||
#: src/views/settings/index.tsx:227
|
||||
msgid "Change the language of the app."
|
||||
msgstr "Ändere die Sprache der App."
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
msgid "Check your inbox"
|
||||
msgstr "Überprüfe deinen Posteingang"
|
||||
|
||||
@@ -386,15 +352,11 @@ msgstr "Checklistenname"
|
||||
msgid "Clear filters"
|
||||
msgstr "Filter löschen"
|
||||
|
||||
#: src/views/auth/login/index.tsx:48
|
||||
#: src/views/auth/signup/index.tsx:74
|
||||
#: src/views/auth/login/index.tsx:46
|
||||
#: src/views/auth/signup/index.tsx:72
|
||||
msgid "Click on the link we've sent to {magicLinkRecipient} to sign in."
|
||||
msgstr "Klicke auf den Link, den wir an {magicLinkRecipient} gesendet haben, um dich anzumelden."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:136
|
||||
msgid "Close"
|
||||
msgstr "Schließen"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:22
|
||||
msgid "Code Review"
|
||||
msgstr "Code-Review"
|
||||
@@ -439,7 +401,7 @@ msgid "Confirm your new password"
|
||||
msgstr "Bestätigen Sie Ihr neues Passwort"
|
||||
|
||||
#: src/views/boards/components/ImportBoardsForm.tsx:157
|
||||
#: src/views/settings/IntegrationsSettings.tsx:93
|
||||
#: src/views/settings/index.tsx:272
|
||||
msgid "Connect Trello"
|
||||
msgstr "Trello verbinden"
|
||||
|
||||
@@ -447,7 +409,7 @@ msgstr "Trello verbinden"
|
||||
msgid "Connect your favorite tools to streamline your workflow."
|
||||
msgstr "Verbinde deine Lieblingstools, um deinen Arbeitsablauf zu optimieren."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:80
|
||||
#: src/views/settings/index.tsx:259
|
||||
msgid "Connect your Trello account to import boards."
|
||||
msgstr "Verbinde dein Trello-Konto, um Boards zu importieren."
|
||||
|
||||
@@ -463,12 +425,12 @@ msgstr "Kontaktiere uns"
|
||||
msgid "Content Creation"
|
||||
msgstr "Content-Erstellung"
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Continue with "
|
||||
msgstr "Fortfahren mit "
|
||||
|
||||
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
|
||||
#: src/components/AuthForm.tsx:301
|
||||
#: src/components/AuthForm.tsx:297
|
||||
msgid "Continue with {0}"
|
||||
msgstr "Fortfahren mit {0}"
|
||||
|
||||
@@ -482,10 +444,6 @@ msgstr "Kontrolliere, wer deine Boards ansehen und bearbeiten kann."
|
||||
msgid "Create another"
|
||||
msgstr "Weitere erstellen"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:175
|
||||
msgid "Create API key"
|
||||
msgstr "API-Schlüssel erstellen"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:128
|
||||
msgid "Create board"
|
||||
msgstr "Board erstellen"
|
||||
@@ -510,12 +468,12 @@ msgstr "Liste erstellen"
|
||||
msgid "Create new board"
|
||||
msgstr "Neues Board erstellen"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:30
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:54
|
||||
msgid "Create new key"
|
||||
msgstr "Neuen Schlüssel erstellen"
|
||||
|
||||
#: src/views/board/components/NewCardForm.tsx:394
|
||||
#: src/views/card/components/LabelSelector.tsx:97
|
||||
#: src/views/card/components/LabelSelector.tsx:98
|
||||
msgid "Create new label"
|
||||
msgstr "Neues Label erstellen"
|
||||
|
||||
@@ -536,10 +494,6 @@ msgstr "hat die Karte erstellt"
|
||||
msgid "Critical"
|
||||
msgstr "Kritisch"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:272
|
||||
msgid "Crop your avatar"
|
||||
msgstr "Schneide deinen Avatar zu"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:19
|
||||
msgid "Current password is required"
|
||||
msgstr "Aktuelles Passwort ist erforderlich"
|
||||
@@ -573,9 +527,9 @@ msgstr "Dunkel"
|
||||
msgid "Delete"
|
||||
msgstr "Löschen"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:52
|
||||
#: src/views/settings/AccountSettings.tsx:62
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:96
|
||||
#: src/views/settings/index.tsx:346
|
||||
#: src/views/settings/index.tsx:356
|
||||
msgid "Delete account"
|
||||
msgstr "Konto löschen"
|
||||
|
||||
@@ -596,8 +550,8 @@ msgid "Delete list"
|
||||
msgstr "Liste löschen"
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
|
||||
#: src/views/settings/WorkspaceSettings.tsx:96
|
||||
#: src/views/settings/WorkspaceSettings.tsx:107
|
||||
#: src/views/settings/index.tsx:309
|
||||
#: src/views/settings/index.tsx:320
|
||||
msgid "Delete workspace"
|
||||
msgstr "Workspace löschen"
|
||||
|
||||
@@ -623,7 +577,7 @@ msgstr "hat Checklistenelement <0>{0}</0> gelöscht"
|
||||
msgid "Design"
|
||||
msgstr "Design"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:108
|
||||
#: src/views/settings/index.tsx:287
|
||||
msgid "Disconnect Trello"
|
||||
msgstr "Trello trennen"
|
||||
|
||||
@@ -631,7 +585,7 @@ msgstr "Trello trennen"
|
||||
msgid "Discuss and collaborate on cards."
|
||||
msgstr "Diskutiere und arbeite gemeinsam an Karten."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:35
|
||||
#: src/views/settings/index.tsx:176
|
||||
msgid "Display name"
|
||||
msgstr "Anzeigename"
|
||||
|
||||
@@ -665,7 +619,7 @@ msgstr "Dokumente"
|
||||
msgid "Documentation"
|
||||
msgstr "Dokumentation"
|
||||
|
||||
#: src/views/auth/login/index.tsx:63
|
||||
#: src/views/auth/login/index.tsx:61
|
||||
msgid "Don't have an account? <0><1>Sign up</1></0>"
|
||||
msgstr "Du hast noch kein Konto? <0><1>Registrieren</1></0>"
|
||||
|
||||
@@ -697,11 +651,11 @@ msgstr "Workspace-URL bearbeiten"
|
||||
msgid "Editing"
|
||||
msgstr "Bearbeitung"
|
||||
|
||||
#: src/components/AuthForm.tsx:372
|
||||
#: src/components/AuthForm.tsx:368
|
||||
msgid "email"
|
||||
msgstr "E-Mail"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:252
|
||||
#: src/views/members/components/InviteMemberForm.tsx:161
|
||||
msgid "Email"
|
||||
msgstr "E-Mail"
|
||||
|
||||
@@ -717,11 +671,11 @@ msgstr "Geben Sie Ihr aktuelles Passwort ein"
|
||||
msgid "Enter your current password and choose a new secure password."
|
||||
msgstr "Geben Sie Ihr aktuelles Passwort ein und wählen Sie ein neues sicheres Passwort."
|
||||
|
||||
#: src/components/AuthForm.tsx:337
|
||||
#: src/components/AuthForm.tsx:333
|
||||
msgid "Enter your email address"
|
||||
msgstr "Gib deine E-Mail-Adresse ein"
|
||||
|
||||
#: src/components/AuthForm.tsx:325
|
||||
#: src/components/AuthForm.tsx:321
|
||||
msgid "Enter your name"
|
||||
msgstr "Gib deinen Namen ein"
|
||||
|
||||
@@ -729,26 +683,14 @@ msgstr "Gib deinen Namen ein"
|
||||
msgid "Enter your new password"
|
||||
msgstr "Geben Sie Ihr neues Passwort ein"
|
||||
|
||||
#: src/components/AuthForm.tsx:350
|
||||
#: src/components/AuthForm.tsx:346
|
||||
msgid "Enter your password"
|
||||
msgstr "Gib dein Passwort ein"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:89
|
||||
msgid "Error Changing Password"
|
||||
msgstr "Fehler beim Ändern des Passworts"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:117
|
||||
msgid "Error creating invite link"
|
||||
msgstr "Fehler beim Erstellen des Einladungslinks"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:132
|
||||
msgid "Error deactivating invite link"
|
||||
msgstr "Fehler beim Deaktivieren des Einladungslinks"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:39
|
||||
msgid "Error deleting account"
|
||||
msgstr "Fehler beim Löschen des Kontos"
|
||||
@@ -761,12 +703,12 @@ msgstr "Fehler beim Löschen des Labels"
|
||||
msgid "Error deleting workspace"
|
||||
msgstr "Fehler beim Löschen des Arbeitsbereichs"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:60
|
||||
#: src/views/settings/index.tsx:126
|
||||
msgid "Error disconnecting Trello"
|
||||
msgstr "Fehler beim Trennen von Trello"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:95
|
||||
#: src/views/members/components/InviteMemberForm.tsx:101
|
||||
#: src/views/members/components/InviteMemberForm.tsx:71
|
||||
#: src/views/members/components/InviteMemberForm.tsx:77
|
||||
msgid "Error inviting member"
|
||||
msgstr "Fehler beim Einladen des Mitglieds"
|
||||
|
||||
@@ -774,7 +716,7 @@ msgstr "Fehler beim Einladen des Mitglieds"
|
||||
msgid "Error updating display name"
|
||||
msgstr "Fehler beim Aktualisieren des Anzeigenamens"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:77
|
||||
#: src/views/settings/components/Avatar.tsx:39
|
||||
msgid "Error updating profile image"
|
||||
msgstr "Fehler beim Aktualisieren des Profilbilds"
|
||||
|
||||
@@ -790,7 +732,7 @@ msgstr "Fehler beim Aktualisieren des Arbeitsbereichsnamens"
|
||||
msgid "Error updating workspace URL"
|
||||
msgstr "Fehler beim Aktualisieren der Workspace-URL"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:221
|
||||
#: src/views/members/components/InviteMemberForm.tsx:130
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
|
||||
msgid "Error upgrading subscription"
|
||||
msgstr "Fehler beim Upgrade des Abonnements"
|
||||
@@ -799,8 +741,8 @@ msgstr "Fehler beim Upgrade des Abonnements"
|
||||
msgid "Error upgrading to Pro"
|
||||
msgstr "Fehler beim Upgrade auf Pro"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:91
|
||||
#: src/views/settings/components/Avatar.tsx:218
|
||||
#: src/views/settings/components/Avatar.tsx:56
|
||||
#: src/views/settings/components/Avatar.tsx:97
|
||||
msgid "Error uploading profile image"
|
||||
msgstr "Fehler beim Hochladen des Profilbilds"
|
||||
|
||||
@@ -816,16 +758,8 @@ msgstr "Alles was du brauchst, für immer kostenlos. Unbegrenzte Boards, unbegre
|
||||
msgid "Execution"
|
||||
msgstr "Ausführung"
|
||||
|
||||
#: src/views/invite/index.tsx:41
|
||||
msgid "Failed to accept invitation. Please try again later, or contact customer support."
|
||||
msgstr "Einladung konnte nicht angenommen werden. Bitte versuche es später erneut oder kontaktiere den Kundensupport."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:197
|
||||
msgid "Failed to copy invite link"
|
||||
msgstr "Einladungslink konnte nicht kopiert werden"
|
||||
|
||||
#. placeholder {0}: provider.at(0)?.toUpperCase() + provider.slice(1)
|
||||
#: src/components/AuthForm.tsx:273
|
||||
#: src/components/AuthForm.tsx:269
|
||||
msgid "Failed to login with {0}. Please try again."
|
||||
msgstr "Anmeldung mit {0} fehlgeschlagen. Bitte versuche es erneut."
|
||||
|
||||
@@ -873,7 +807,7 @@ msgstr "Für langfristige Nachhaltigkeit erkennen wir an, dass alle guten Open-S
|
||||
msgid "Free"
|
||||
msgstr "Kostenlos"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:312
|
||||
#: src/views/members/components/InviteMemberForm.tsx:193
|
||||
#: src/views/members/index.tsx:208
|
||||
msgid "Free Plan"
|
||||
msgstr "Kostenloser Plan"
|
||||
@@ -890,7 +824,7 @@ msgstr "Vollzeit"
|
||||
msgid "Fun"
|
||||
msgstr "Spaß"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
#: src/views/home/components/Cta.tsx:61
|
||||
#: src/views/home/components/Header.tsx:102
|
||||
#: src/views/home/components/Header.tsx:141
|
||||
@@ -930,13 +864,8 @@ msgstr "Erste Schritte"
|
||||
msgid "GitHub"
|
||||
msgstr "GitHub"
|
||||
|
||||
#: src/views/invite/index.tsx:113
|
||||
msgid "Go Home"
|
||||
msgstr "Zur Startseite"
|
||||
|
||||
#: src/views/home/components/Header.tsx:96
|
||||
#: src/views/home/components/Header.tsx:133
|
||||
#: src/views/invite/index.tsx:144
|
||||
msgid "Go to app"
|
||||
msgstr "Zur App"
|
||||
|
||||
@@ -988,7 +917,7 @@ msgstr "Ideen"
|
||||
msgid "Ideas to improve this page..."
|
||||
msgstr "Ideen zur Verbesserung dieser Seite..."
|
||||
|
||||
#: src/views/boards/index.tsx:43
|
||||
#: src/views/boards/index.tsx:38
|
||||
msgid "Import"
|
||||
msgstr "Importieren"
|
||||
|
||||
@@ -1026,7 +955,6 @@ msgstr "In Bearbeitung"
|
||||
msgid "Individuals"
|
||||
msgstr "Einzelpersonen"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:57
|
||||
#: src/views/home/components/Features.tsx:114
|
||||
msgid "Integrations"
|
||||
msgstr "Integrationen"
|
||||
@@ -1035,44 +963,27 @@ msgstr "Integrationen"
|
||||
msgid "Interviewing"
|
||||
msgstr "Vorstellungsgespräch"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:49
|
||||
#: src/views/members/components/InviteMemberForm.tsx:40
|
||||
msgid "Invalid email address"
|
||||
msgstr "Ungültige E-Mail-Adresse"
|
||||
|
||||
#: src/views/invite/index.tsx:105
|
||||
msgid "Invalid invitation"
|
||||
msgstr "Ungültige Einladung"
|
||||
|
||||
#: src/views/members/index.tsx:221
|
||||
msgid "Invite"
|
||||
msgstr "Einladen"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:190
|
||||
msgid "Invite link copied"
|
||||
msgstr "Einladungslink kopiert"
|
||||
#: src/views/members/components/InviteMemberForm.tsx:208
|
||||
msgid "Invite another"
|
||||
msgstr "Weitere Person einladen"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:191
|
||||
msgid "Invite link copied to clipboard"
|
||||
msgstr "Einladungslink in die Zwischenablage kopiert"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:111
|
||||
#: src/views/members/components/InviteMemberForm.tsx:350
|
||||
#: src/views/card/components/MemberSelector.tsx:112
|
||||
#: src/views/members/components/InviteMemberForm.tsx:233
|
||||
msgid "Invite member"
|
||||
msgstr "Mitglied einladen"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:315
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
|
||||
msgstr "Das Einladen von Mitgliedern erfordert einen Team-Plan. Sie werden weitergeleitet, um Ihren Workspace zu upgraden."
|
||||
|
||||
#: src/views/invite/index.tsx:79
|
||||
#: src/views/invite/index.tsx:129
|
||||
msgid "Join workspace"
|
||||
msgstr "Arbeitsbereich beitreten"
|
||||
|
||||
#: src/views/invite/index.tsx:91
|
||||
msgid "Join workspace | kan.bn"
|
||||
msgstr "Arbeitsbereich beitreten | kan.bn"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:69
|
||||
msgid "Junior"
|
||||
msgstr "Junior"
|
||||
@@ -1100,7 +1011,7 @@ msgstr "Labels"
|
||||
msgid "Labels & Filters"
|
||||
msgstr "Labels & Filter"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:224
|
||||
msgid "Language"
|
||||
msgstr "Sprache"
|
||||
|
||||
@@ -1145,7 +1056,7 @@ msgstr "Liste"
|
||||
msgid "List name"
|
||||
msgstr "Listenname"
|
||||
|
||||
#: src/views/auth/login/index.tsx:33
|
||||
#: src/views/auth/login/index.tsx:31
|
||||
msgid "Login | kan.bn"
|
||||
msgstr "Login | kan.bn"
|
||||
|
||||
@@ -1161,7 +1072,7 @@ msgstr "Langfristig"
|
||||
msgid "Low Priority"
|
||||
msgstr "Niedrige Priorität"
|
||||
|
||||
#: src/components/AuthForm.tsx:373
|
||||
#: src/components/AuthForm.tsx:369
|
||||
msgid "magic link"
|
||||
msgstr "Magic Link"
|
||||
|
||||
@@ -1195,7 +1106,7 @@ msgstr "Mitglieder | {0}"
|
||||
msgid "Monthly"
|
||||
msgstr "Monatlich"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
#: src/views/members/components/InviteMemberForm.tsx:93
|
||||
msgid "monthly billing"
|
||||
msgstr "monatliche Abrechnung"
|
||||
|
||||
@@ -1218,14 +1129,10 @@ msgstr "Name"
|
||||
msgid "Need help?"
|
||||
msgstr "Brauchst du Hilfe?"
|
||||
|
||||
#: src/views/boards/index.tsx:53
|
||||
#: src/views/boards/index.tsx:48
|
||||
msgid "New"
|
||||
msgstr "Neu"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:147
|
||||
msgid "New API key"
|
||||
msgstr "Neuer API-Schlüssel"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:85
|
||||
msgid "New board"
|
||||
msgstr "Neues Board"
|
||||
@@ -1299,15 +1206,15 @@ msgstr "Angebot"
|
||||
msgid "Onboarding"
|
||||
msgstr "Einarbeitung"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:55
|
||||
#: src/views/settings/index.tsx:349
|
||||
msgid "Once you delete your account, there is no going back. This action cannot be undone."
|
||||
msgstr "Sobald Sie Ihr Konto löschen, gibt es kein Zurück mehr. Diese Aktion kann nicht rückgängig gemacht werden."
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:99
|
||||
#: src/views/settings/index.tsx:312
|
||||
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
|
||||
msgstr "Sobald Sie Ihren Arbeitsbereich löschen, gibt es kein Zurück mehr. Diese Aktion kann nicht rückgängig gemacht werden."
|
||||
|
||||
#: src/components/AuthForm.tsx:315
|
||||
#: src/components/AuthForm.tsx:311
|
||||
msgid "or"
|
||||
msgstr "oder"
|
||||
|
||||
@@ -1335,10 +1242,6 @@ msgstr "Passwort muss mindestens 8 Zeichen lang sein"
|
||||
msgid "Passwords do not match"
|
||||
msgstr "Passwörter stimmen nicht überein"
|
||||
|
||||
#: src/views/members/index.tsx:134
|
||||
msgid "Paused"
|
||||
msgstr "Pausiert"
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:102
|
||||
msgid "Payment frequency"
|
||||
msgstr "Zahlungshäufigkeit"
|
||||
@@ -1364,19 +1267,19 @@ msgstr "Planung"
|
||||
msgid "Please confirm your new password"
|
||||
msgstr "Bitte bestätigen Sie Ihr neues Passwort"
|
||||
|
||||
#: src/components/AuthForm.tsx:341
|
||||
#: src/components/AuthForm.tsx:337
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Bitte gib eine gültige E-Mail-Adresse ein"
|
||||
|
||||
#: src/components/AuthForm.tsx:329
|
||||
#: src/components/AuthForm.tsx:325
|
||||
msgid "Please enter a valid name"
|
||||
msgstr "Bitte gib einen gültigen Namen ein"
|
||||
|
||||
#: src/components/AuthForm.tsx:354
|
||||
#: src/components/AuthForm.tsx:350
|
||||
msgid "Please enter a valid password"
|
||||
msgstr "Bitte gib ein gültiges Passwort ein"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:92
|
||||
#: src/views/settings/components/Avatar.tsx:57
|
||||
msgid "Please select a file to upload."
|
||||
msgstr "Bitte wähle eine Datei zum Hochladen aus."
|
||||
|
||||
@@ -1397,18 +1300,18 @@ msgstr "Bitte wähle eine Datei zum Hochladen aus."
|
||||
#: src/views/card/components/DeleteCardConfirmation.tsx:52
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:37
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:45
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
#: src/views/card/components/LabelSelector.tsx:73
|
||||
#: src/views/card/components/ListSelector.tsx:53
|
||||
#: src/views/card/components/MemberSelector.tsx:80
|
||||
#: src/views/card/components/NewChecklistForm.tsx:70
|
||||
#: src/views/card/components/NewChecklistItemForm.tsx:89
|
||||
#: src/views/card/components/NewCommentForm.tsx:31
|
||||
#: src/views/card/index.tsx:173
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
|
||||
#: src/views/members/components/InviteMemberForm.tsx:102
|
||||
#: src/views/members/components/InviteMemberForm.tsx:222
|
||||
#: src/views/settings/components/Avatar.tsx:78
|
||||
#: src/views/settings/components/Avatar.tsx:219
|
||||
#: src/views/members/components/InviteMemberForm.tsx:78
|
||||
#: src/views/members/components/InviteMemberForm.tsx:131
|
||||
#: src/views/settings/components/Avatar.tsx:40
|
||||
#: src/views/settings/components/Avatar.tsx:98
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:46
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:55
|
||||
@@ -1419,11 +1322,6 @@ msgstr "Bitte wähle eine Datei zum Hochladen aus."
|
||||
msgid "Please try again later, or contact customer support."
|
||||
msgstr "Bitte versuche es später noch einmal oder kontaktiere den Kundensupport."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:118
|
||||
#: src/views/members/components/InviteMemberForm.tsx:133
|
||||
msgid "Please try again later."
|
||||
msgstr "Bitte versuche es später erneut."
|
||||
|
||||
#: src/views/home/components/Footer.tsx:50
|
||||
#: src/views/home/components/Header.tsx:15
|
||||
#: src/views/home/components/Pricing.tsx:85
|
||||
@@ -1447,15 +1345,15 @@ msgstr "Privat"
|
||||
msgid "Pro Plan"
|
||||
msgstr "Pro-Plan"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
msgid "Pro Plan ∞"
|
||||
msgstr "Pro-Plan ∞"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:64
|
||||
#: src/views/settings/components/Avatar.tsx:26
|
||||
msgid "Profile image updated"
|
||||
msgstr "Profilbild aktualisiert"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:29
|
||||
#: src/views/settings/index.tsx:171
|
||||
msgid "Profile picture"
|
||||
msgstr "Profilbild"
|
||||
|
||||
@@ -1538,6 +1436,10 @@ msgstr "Ressourcen"
|
||||
msgid "Review"
|
||||
msgstr "Überprüfung"
|
||||
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:49
|
||||
msgid "Revoke"
|
||||
msgstr "Widerrufen"
|
||||
|
||||
#: src/views/home/components/Footer.tsx:36
|
||||
#: src/views/home/components/Header.tsx:13
|
||||
msgid "Roadmap"
|
||||
@@ -1552,7 +1454,6 @@ msgid "Run on your own infrastructure"
|
||||
msgstr "Auf eigener Infrastruktur betreiben"
|
||||
|
||||
#: src/views/card/components/Comment.tsx:165
|
||||
#: src/views/settings/components/Avatar.tsx:309
|
||||
msgid "Save"
|
||||
msgstr "Speichern"
|
||||
|
||||
@@ -1592,62 +1493,35 @@ msgstr "Feedback senden"
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:82
|
||||
#: src/components/SideNavigation.tsx:78
|
||||
#: src/views/settings/index.tsx:165
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:25
|
||||
msgid "Settings | Account"
|
||||
msgstr "Einstellungen | Konto"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:18
|
||||
msgid "Settings | API"
|
||||
msgstr "Einstellungen | API"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:35
|
||||
msgid "Settings | Billing"
|
||||
msgstr "Einstellungen | Abrechnung"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:69
|
||||
msgid "Settings | Integrations"
|
||||
msgstr "Einstellungen | Integrationen"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:54
|
||||
msgid "Settings | Workspace"
|
||||
msgstr "Einstellungen | Arbeitsbereich"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:327
|
||||
msgid "Share invite link"
|
||||
msgstr "Einladungslink teilen"
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/settings/index.tsx:161
|
||||
msgid "Settings | {0}"
|
||||
msgstr "Einstellungen | {0}"
|
||||
|
||||
#: src/views/home/components/Header.tsx:100
|
||||
#: src/views/home/components/Header.tsx:138
|
||||
msgid "Sign in"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: src/views/invite/index.tsx:154
|
||||
msgid "Sign In"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: src/views/invite/index.tsx:162
|
||||
msgid "Sign Up"
|
||||
msgstr "Registrieren"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:34
|
||||
#: src/views/auth/signup/index.tsx:59
|
||||
#: src/views/auth/signup/index.tsx:32
|
||||
#: src/views/auth/signup/index.tsx:57
|
||||
msgid "Sign up | kan.bn"
|
||||
msgstr "Registrieren | kan.bn"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:44
|
||||
#: src/views/auth/signup/index.tsx:42
|
||||
msgid "Sign up disabled"
|
||||
msgstr "Registrierung deaktiviert"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:47
|
||||
#: src/views/auth/signup/index.tsx:45
|
||||
msgid "Sign up is currently disabled. Please try again later."
|
||||
msgstr "Die Registrierung ist derzeit deaktiviert. Bitte versuche es später erneut."
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Sign up with "
|
||||
msgstr "Registrieren mit "
|
||||
|
||||
@@ -1671,8 +1545,8 @@ msgstr "Softwareentwicklung"
|
||||
msgid "Star on Github"
|
||||
msgstr "Stern auf Github"
|
||||
|
||||
#: src/components/AuthForm.tsx:207
|
||||
#: src/components/AuthForm.tsx:224
|
||||
#: src/components/AuthForm.tsx:203
|
||||
#: src/components/AuthForm.tsx:220
|
||||
msgid "Success"
|
||||
msgstr "Erfolg"
|
||||
|
||||
@@ -1692,7 +1566,7 @@ msgstr "Unterstütze die Entwicklung des Projekts"
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
#: src/views/members/index.tsx:207
|
||||
msgid "Team Plan"
|
||||
msgstr "Team-Plan"
|
||||
@@ -1745,10 +1619,6 @@ msgstr "Sie werden keinen Zugriff mehr auf diesen Workspace haben."
|
||||
msgid "This action can't be undone."
|
||||
msgstr "Diese Aktion kann nicht rückgängig gemacht werden."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:129
|
||||
msgid "This API key will only be shown once. Please save it in a secure location."
|
||||
msgstr "Dieser API-Schlüssel wird nur einmal angezeigt. Bitte speichern Sie ihn an einem sicheren Ort."
|
||||
|
||||
#: src/views/public/board/index.tsx:151
|
||||
msgid "This board is private or does not exist"
|
||||
msgstr "Dieses Board ist privat oder existiert nicht"
|
||||
@@ -1757,10 +1627,6 @@ msgstr "Dieses Board ist privat oder existiert nicht"
|
||||
msgid "This board URL has already been taken"
|
||||
msgstr "Diese Board-URL ist bereits vergeben"
|
||||
|
||||
#: src/views/invite/index.tsx:108
|
||||
msgid "This invitation link is invalid or has expired."
|
||||
msgstr "Dieser Einladungslink ist ungültig oder abgelaufen."
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:70
|
||||
msgid "This will result in the permanent deletion of all data associated with this workspace."
|
||||
msgstr "Dies führt zur permanenten Löschung aller mit diesem Workspace verbundenen Daten."
|
||||
@@ -1794,11 +1660,7 @@ msgstr "Menü umschalten"
|
||||
msgid "Track all card changes with detailed activity history."
|
||||
msgstr "Verfolge alle kartenänderungen mit detaillierter aktivitätshistorie."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:73
|
||||
msgid "Trello"
|
||||
msgstr "Trello"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:53
|
||||
#: src/views/settings/index.tsx:119
|
||||
msgid "Trello disconnected"
|
||||
msgstr "Trello getrennt"
|
||||
|
||||
@@ -1883,16 +1745,16 @@ msgstr "Checklistenelement kann nicht aktualisiert werden"
|
||||
msgid "Unable to update comment"
|
||||
msgstr "Kommentar konnte nicht aktualisiert werden"
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:71
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
msgid "Unable to update labels"
|
||||
msgstr "Labels konnten nicht aktualisiert werden"
|
||||
|
||||
#: src/views/board/index.tsx:133
|
||||
#: src/views/card/components/ListSelector.tsx:51
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
msgid "Unable to update list"
|
||||
msgstr "Liste konnte nicht aktualisiert werden"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:78
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
msgid "Unable to update members"
|
||||
msgstr "Mitglieder konnten nicht aktualisiert werden"
|
||||
|
||||
@@ -1935,9 +1797,9 @@ msgid "Unlimited members"
|
||||
msgstr "Unbegrenzte Mitglieder"
|
||||
|
||||
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:79
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:90
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:82
|
||||
#: src/views/settings/components/UpdateWorkspaceUrlForm.tsx:154
|
||||
msgid "Update"
|
||||
msgstr "Aktualisieren"
|
||||
@@ -1968,7 +1830,7 @@ msgid "Upgrade"
|
||||
msgstr "Upgrade"
|
||||
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
|
||||
#: src/views/settings/WorkspaceSettings.tsx:89
|
||||
#: src/views/settings/index.tsx:216
|
||||
msgid "Upgrade to Pro"
|
||||
msgstr "Upgrade auf Pro"
|
||||
|
||||
@@ -1976,7 +1838,7 @@ msgstr "Upgrade auf Pro"
|
||||
msgid "Upgrade to Pro ($29/month)"
|
||||
msgstr "Upgrade auf Pro ($29/Monat)"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:341
|
||||
#: src/views/members/components/InviteMemberForm.tsx:224
|
||||
msgid "Upgrade to Team Plan"
|
||||
msgstr "Upgrade auf Team-Plan"
|
||||
|
||||
@@ -2008,7 +1870,7 @@ msgstr "Vorlage verwenden"
|
||||
msgid "User"
|
||||
msgstr "Benutzer"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:96
|
||||
#: src/views/members/components/InviteMemberForm.tsx:72
|
||||
msgid "User is already a member of this workspace"
|
||||
msgstr "Benutzer ist bereits Mitglied dieses Arbeitsbereichs"
|
||||
|
||||
@@ -2016,11 +1878,11 @@ msgstr "Benutzer ist bereits Mitglied dieses Arbeitsbereichs"
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:25
|
||||
#: src/views/settings/index.tsx:299
|
||||
msgid "View and manage your API keys."
|
||||
msgstr "API-Schlüssel anzeigen und verwalten."
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:238
|
||||
msgid "View and manage your billing and subscription."
|
||||
msgstr "Verwalte deine Abrechnung und dein Abonnement."
|
||||
|
||||
@@ -2052,7 +1914,7 @@ msgstr "Wir verwenden die <0>AGPL-3.0 lizenz</0>."
|
||||
msgid "We're just getting started. "
|
||||
msgstr "Wir stehen erst am anfang. "
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
msgid "Welcome back"
|
||||
msgstr "Willkommen zurück"
|
||||
|
||||
@@ -2072,7 +1934,6 @@ msgstr "Als Trello 2011 auf den markt kam, beeindruckte es alle mit seiner sorgf
|
||||
msgid "Why make an open source Trello?"
|
||||
msgstr "Warum ein open source Trello entwickeln?"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:39
|
||||
#: src/views/board/index.tsx:331
|
||||
msgid "Workspace"
|
||||
msgstr "Arbeitsbereich"
|
||||
@@ -2085,7 +1946,7 @@ msgstr "Workspace erfolgreich erstellt. Du kannst später in den Einstellungen u
|
||||
msgid "Workspace deleted"
|
||||
msgstr "Workspace gelöscht"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:75
|
||||
#: src/views/settings/index.tsx:202
|
||||
msgid "Workspace description"
|
||||
msgstr "Workspace-Beschreibung"
|
||||
|
||||
@@ -2107,7 +1968,7 @@ msgid "Workspace members"
|
||||
msgstr "Workspace-mitglieder"
|
||||
|
||||
#: src/components/NewWorkspaceForm.tsx:259
|
||||
#: src/views/settings/WorkspaceSettings.tsx:58
|
||||
#: src/views/settings/index.tsx:183
|
||||
msgid "Workspace name"
|
||||
msgstr "Name des Workspaces"
|
||||
|
||||
@@ -2131,7 +1992,7 @@ msgstr "Workspace-Name aktualisiert"
|
||||
msgid "Workspace slug updated"
|
||||
msgstr "Workspace-Slug aktualisiert"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:66
|
||||
#: src/views/settings/index.tsx:192
|
||||
msgid "Workspace URL"
|
||||
msgstr "Workspace-URL"
|
||||
|
||||
@@ -2151,7 +2012,7 @@ msgstr "Jährlich"
|
||||
msgid "Yes, we offer an forever free plan for individual use. No restrictions, no paywalls, no limits."
|
||||
msgstr "Ja, wir bieten einen dauerhaft kostenlosen plan für die individuelle nutzung an. Keine einschränkungen, keine paywalls, keine limits."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:73
|
||||
#: src/views/settings/index.tsx:331
|
||||
msgid "You are about to change your password."
|
||||
msgstr "Sie sind dabei, Ihr Passwort zu ändern."
|
||||
|
||||
@@ -2167,26 +2028,18 @@ msgstr "Du kannst teammitglieder einladen, indem du auf die schaltfläche \"Einl
|
||||
msgid "You can self-host by following the instructions in our <0>repo</0>."
|
||||
msgstr "Sie können selbst hosten, indem sie den anweisungen in unserem <0>repo</0> folgen."
|
||||
|
||||
#: src/components/AuthForm.tsx:225
|
||||
#: src/components/AuthForm.tsx:221
|
||||
msgid "You have been logged in successfully."
|
||||
msgstr "Sie haben sich erfolgreich angemeldet."
|
||||
|
||||
#: src/components/AuthForm.tsx:208
|
||||
#: src/components/AuthForm.tsx:204
|
||||
msgid "You have been signed up successfully."
|
||||
msgstr "Sie haben sich erfolgreich registriert."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:305
|
||||
#: src/views/members/components/InviteMemberForm.tsx:186
|
||||
msgid "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
|
||||
msgstr "Sie haben unbegrenzte Plätze mit Ihrem Pro-Plan. Für neue Mitglieder fallen keine zusätzlichen Kosten an!"
|
||||
|
||||
#: src/views/invite/index.tsx:134
|
||||
msgid "You've been invited to join a workspace on kan.bn."
|
||||
msgstr "Du wurdest eingeladen, einem Arbeitsbereich auf kan.bn beizutreten."
|
||||
|
||||
#: src/views/invite/index.tsx:135
|
||||
msgid "You've been invited to join a workspace."
|
||||
msgstr "Du wurdest eingeladen, einem Arbeitsbereich beizutreten."
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:28
|
||||
msgid "Your account has been deleted."
|
||||
msgstr "Dein Konto wurde gelöscht."
|
||||
@@ -2203,15 +2056,15 @@ msgstr "Dein Anzeigename wurde aktualisiert."
|
||||
msgid "Your password has been changed."
|
||||
msgstr "Ihr Passwort wurde geändert."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:65
|
||||
#: src/views/settings/components/Avatar.tsx:27
|
||||
msgid "Your profile image has been updated."
|
||||
msgstr "Dein Profilbild wurde aktualisiert."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:54
|
||||
#: src/views/settings/index.tsx:120
|
||||
msgid "Your Trello account has been disconnected."
|
||||
msgstr "Dein Trello-Konto wurde getrennt."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:102
|
||||
#: src/views/settings/index.tsx:281
|
||||
msgid "Your Trello account is connected."
|
||||
msgstr "Dein Trello-Konto ist verbunden."
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -40,12 +40,12 @@ msgstr "{boardCount, plural, one {Import board (1)} other {Import boards ({board
|
||||
#~ msgid "#1 Hacker News"
|
||||
#~ msgstr "#1 Hacker News"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:146
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:92
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$10/month"
|
||||
msgstr "$10/month"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$8/month"
|
||||
msgstr "$8/month"
|
||||
|
||||
@@ -65,10 +65,6 @@ msgstr "1 user"
|
||||
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
|
||||
msgstr "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
|
||||
|
||||
#: src/components/SettingsLayout.tsx:33
|
||||
msgid "Account"
|
||||
msgstr "Account"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:27
|
||||
msgid "Account deleted"
|
||||
msgstr "Account deleted"
|
||||
@@ -111,13 +107,13 @@ msgstr "Add description... (type '/' to open commands or '@' to mention)"
|
||||
msgid "Add details..."
|
||||
msgstr "Add details..."
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:109
|
||||
#: src/views/card/components/LabelSelector.tsx:114
|
||||
#: src/views/card/components/LabelSelector.tsx:110
|
||||
#: src/views/card/components/LabelSelector.tsx:118
|
||||
msgid "Add label"
|
||||
msgstr "Add label"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:130
|
||||
#: src/views/members/components/InviteMemberForm.tsx:238
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
msgid "Add member"
|
||||
msgstr "Add member"
|
||||
|
||||
@@ -156,14 +152,10 @@ msgstr "added label <0>{0}</0>"
|
||||
#~ msgid "added label <0>{label}</0>"
|
||||
#~ msgstr "added label <0>{label}</0>"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:306
|
||||
#: src/views/members/components/InviteMemberForm.tsx:187
|
||||
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
|
||||
msgstr "Adding a new member will cost an additional {price} ({billingType}) per seat."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:275
|
||||
msgid "Adjust the square crop to fit your avatar."
|
||||
msgstr "Adjust the square crop to fit your avatar."
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:55
|
||||
msgid "Admin roles"
|
||||
msgstr "Admin roles"
|
||||
@@ -176,11 +168,11 @@ msgstr "Admin roles"
|
||||
msgid "All systems operational"
|
||||
msgstr "All systems operational"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:88
|
||||
#: src/views/auth/signup/index.tsx:86
|
||||
msgid "Already have an account? <0><1>Sign in</1></0>"
|
||||
msgstr "Already have an account? <0><1>Sign in</1></0>"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:61
|
||||
#: src/views/settings/index.tsx:127
|
||||
msgid "An error occurred while disconnecting your Trello account."
|
||||
msgstr "An error occurred while disconnecting your Trello account."
|
||||
|
||||
@@ -188,31 +180,7 @@ msgstr "An error occurred while disconnecting your Trello account."
|
||||
msgid "An unexpected error occurred. Please try again later."
|
||||
msgstr "An unexpected error occurred. Please try again later."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:290
|
||||
msgid "Anyone with this link can join your workspace"
|
||||
msgstr "Anyone with this link can join your workspace"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:51
|
||||
msgid "API"
|
||||
msgstr "API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:91
|
||||
msgid "API key created"
|
||||
msgstr "API key created"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:161
|
||||
msgid "API key name"
|
||||
msgstr "API key name"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:25
|
||||
msgid "API key name cannot exceed 30 characters"
|
||||
msgstr "API key name cannot exceed 30 characters"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:24
|
||||
msgid "API key name is required"
|
||||
msgstr "API key name is required"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:22
|
||||
#: src/views/settings/index.tsx:296
|
||||
msgid "API keys"
|
||||
msgstr "API keys"
|
||||
|
||||
@@ -286,21 +254,20 @@ msgstr "Backlog"
|
||||
msgid "Basic Kanban"
|
||||
msgstr "Basic Kanban"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed annually"
|
||||
msgstr "billed annually"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed monthly"
|
||||
msgstr "billed monthly"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:44
|
||||
#: src/views/boards/components/TemplateBoards.tsx:55
|
||||
#: src/views/settings/BillingSettings.tsx:39
|
||||
#: src/views/settings/index.tsx:235
|
||||
msgid "Billing"
|
||||
msgstr "Billing"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:49
|
||||
#: src/views/settings/index.tsx:245
|
||||
msgid "Billing portal"
|
||||
msgstr "Billing portal"
|
||||
|
||||
@@ -364,12 +331,12 @@ msgid "Board visibility updated"
|
||||
msgstr "Board visibility updated"
|
||||
|
||||
#: src/components/SideNavigation.tsx:68
|
||||
#: src/views/boards/index.tsx:32
|
||||
#: src/views/boards/index.tsx:27
|
||||
msgid "Boards"
|
||||
msgstr "Boards"
|
||||
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/boards/index.tsx:28
|
||||
#: src/views/boards/index.tsx:23
|
||||
msgid "Boards | {0}"
|
||||
msgstr "Boards | {0}"
|
||||
|
||||
@@ -395,7 +362,6 @@ msgstr "Bug Report"
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:63
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:76
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:55
|
||||
#: src/views/settings/components/Avatar.tsx:306
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
|
||||
@@ -411,23 +377,19 @@ msgstr "Card not found"
|
||||
msgid "Card title"
|
||||
msgstr "Card title"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:70
|
||||
#: src/views/settings/AccountSettings.tsx:80
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:109
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:178
|
||||
#: src/views/settings/index.tsx:328
|
||||
#: src/views/settings/index.tsx:338
|
||||
msgid "Change Password"
|
||||
msgstr "Change Password"
|
||||
|
||||
#: src/views/settings/index.tsx:227
|
||||
#~ msgid "Change the language of the app."
|
||||
#~ msgstr "Change the language of the app."
|
||||
msgid "Change the language of the app."
|
||||
msgstr "Change the language of the app."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:45
|
||||
msgid "Change your language preferences."
|
||||
msgstr "Change your language preferences."
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
msgid "Check your inbox"
|
||||
msgstr "Check your inbox"
|
||||
|
||||
@@ -439,15 +401,11 @@ msgstr "Checklist name"
|
||||
msgid "Clear filters"
|
||||
msgstr "Clear filters"
|
||||
|
||||
#: src/views/auth/login/index.tsx:48
|
||||
#: src/views/auth/signup/index.tsx:74
|
||||
#: src/views/auth/login/index.tsx:46
|
||||
#: src/views/auth/signup/index.tsx:72
|
||||
msgid "Click on the link we've sent to {magicLinkRecipient} to sign in."
|
||||
msgstr "Click on the link we've sent to {magicLinkRecipient} to sign in."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:136
|
||||
msgid "Close"
|
||||
msgstr "Close"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:22
|
||||
msgid "Code Review"
|
||||
msgstr "Code Review"
|
||||
@@ -496,7 +454,7 @@ msgid "Confirm your new password"
|
||||
msgstr "Confirm your new password"
|
||||
|
||||
#: src/views/boards/components/ImportBoardsForm.tsx:157
|
||||
#: src/views/settings/IntegrationsSettings.tsx:93
|
||||
#: src/views/settings/index.tsx:272
|
||||
msgid "Connect Trello"
|
||||
msgstr "Connect Trello"
|
||||
|
||||
@@ -504,7 +462,7 @@ msgstr "Connect Trello"
|
||||
msgid "Connect your favorite tools to streamline your workflow."
|
||||
msgstr "Connect your favorite tools to streamline your workflow."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:80
|
||||
#: src/views/settings/index.tsx:259
|
||||
msgid "Connect your Trello account to import boards."
|
||||
msgstr "Connect your Trello account to import boards."
|
||||
|
||||
@@ -520,12 +478,12 @@ msgstr "Contact us"
|
||||
msgid "Content Creation"
|
||||
msgstr "Content Creation"
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Continue with "
|
||||
msgstr "Continue with "
|
||||
|
||||
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
|
||||
#: src/components/AuthForm.tsx:301
|
||||
#: src/components/AuthForm.tsx:297
|
||||
msgid "Continue with {0}"
|
||||
msgstr "Continue with {0}"
|
||||
|
||||
@@ -539,9 +497,9 @@ msgstr "Control who can view and edit your boards."
|
||||
msgid "Create another"
|
||||
msgstr "Create another"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:175
|
||||
msgid "Create API key"
|
||||
msgstr "Create API key"
|
||||
#: src/views/settings/index.tsx:238
|
||||
#~ msgid "Create API key"
|
||||
#~ msgstr "Create API key"
|
||||
|
||||
#: src/views/settings/index.tsx:232
|
||||
#~ msgid "Create API keys to access the Kan API."
|
||||
@@ -571,12 +529,12 @@ msgstr "Create list"
|
||||
msgid "Create new board"
|
||||
msgstr "Create new board"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:30
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:54
|
||||
msgid "Create new key"
|
||||
msgstr "Create new key"
|
||||
|
||||
#: src/views/board/components/NewCardForm.tsx:394
|
||||
#: src/views/card/components/LabelSelector.tsx:97
|
||||
#: src/views/card/components/LabelSelector.tsx:98
|
||||
msgid "Create new label"
|
||||
msgstr "Create new label"
|
||||
|
||||
@@ -601,10 +559,6 @@ msgstr "created the card"
|
||||
msgid "Critical"
|
||||
msgstr "Critical"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:272
|
||||
msgid "Crop your avatar"
|
||||
msgstr "Crop your avatar"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:19
|
||||
msgid "Current password is required"
|
||||
msgstr "Current password is required"
|
||||
@@ -646,9 +600,9 @@ msgstr "Dark"
|
||||
msgid "Delete"
|
||||
msgstr "Delete"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:52
|
||||
#: src/views/settings/AccountSettings.tsx:62
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:96
|
||||
#: src/views/settings/index.tsx:346
|
||||
#: src/views/settings/index.tsx:356
|
||||
msgid "Delete account"
|
||||
msgstr "Delete account"
|
||||
|
||||
@@ -669,8 +623,8 @@ msgid "Delete list"
|
||||
msgstr "Delete list"
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
|
||||
#: src/views/settings/WorkspaceSettings.tsx:96
|
||||
#: src/views/settings/WorkspaceSettings.tsx:107
|
||||
#: src/views/settings/index.tsx:309
|
||||
#: src/views/settings/index.tsx:320
|
||||
msgid "Delete workspace"
|
||||
msgstr "Delete workspace"
|
||||
|
||||
@@ -696,7 +650,7 @@ msgstr "deleted checklist item <0>{0}</0>"
|
||||
msgid "Design"
|
||||
msgstr "Design"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:108
|
||||
#: src/views/settings/index.tsx:287
|
||||
msgid "Disconnect Trello"
|
||||
msgstr "Disconnect Trello"
|
||||
|
||||
@@ -704,7 +658,7 @@ msgstr "Disconnect Trello"
|
||||
msgid "Discuss and collaborate on cards."
|
||||
msgstr "Discuss and collaborate on cards."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:35
|
||||
#: src/views/settings/index.tsx:176
|
||||
msgid "Display name"
|
||||
msgstr "Display name"
|
||||
|
||||
@@ -738,7 +692,7 @@ msgstr "Docs"
|
||||
msgid "Documentation"
|
||||
msgstr "Documentation"
|
||||
|
||||
#: src/views/auth/login/index.tsx:63
|
||||
#: src/views/auth/login/index.tsx:61
|
||||
msgid "Don't have an account? <0><1>Sign up</1></0>"
|
||||
msgstr "Don't have an account? <0><1>Sign up</1></0>"
|
||||
|
||||
@@ -770,11 +724,11 @@ msgstr "Edit workspace URL"
|
||||
msgid "Editing"
|
||||
msgstr "Editing"
|
||||
|
||||
#: src/components/AuthForm.tsx:372
|
||||
#: src/components/AuthForm.tsx:368
|
||||
msgid "email"
|
||||
msgstr "email"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:252
|
||||
#: src/views/members/components/InviteMemberForm.tsx:161
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
|
||||
@@ -790,11 +744,11 @@ msgstr "Enter your current password"
|
||||
msgid "Enter your current password and choose a new secure password."
|
||||
msgstr "Enter your current password and choose a new secure password."
|
||||
|
||||
#: src/components/AuthForm.tsx:337
|
||||
#: src/components/AuthForm.tsx:333
|
||||
msgid "Enter your email address"
|
||||
msgstr "Enter your email address"
|
||||
|
||||
#: src/components/AuthForm.tsx:325
|
||||
#: src/components/AuthForm.tsx:321
|
||||
msgid "Enter your name"
|
||||
msgstr "Enter your name"
|
||||
|
||||
@@ -802,26 +756,14 @@ msgstr "Enter your name"
|
||||
msgid "Enter your new password"
|
||||
msgstr "Enter your new password"
|
||||
|
||||
#: src/components/AuthForm.tsx:350
|
||||
#: src/components/AuthForm.tsx:346
|
||||
msgid "Enter your password"
|
||||
msgstr "Enter your password"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:89
|
||||
msgid "Error Changing Password"
|
||||
msgstr "Error Changing Password"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:117
|
||||
msgid "Error creating invite link"
|
||||
msgstr "Error creating invite link"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:132
|
||||
msgid "Error deactivating invite link"
|
||||
msgstr "Error deactivating invite link"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:39
|
||||
msgid "Error deleting account"
|
||||
msgstr "Error deleting account"
|
||||
@@ -834,12 +776,12 @@ msgstr "Error deleting label"
|
||||
msgid "Error deleting workspace"
|
||||
msgstr "Error deleting workspace"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:60
|
||||
#: src/views/settings/index.tsx:126
|
||||
msgid "Error disconnecting Trello"
|
||||
msgstr "Error disconnecting Trello"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:95
|
||||
#: src/views/members/components/InviteMemberForm.tsx:101
|
||||
#: src/views/members/components/InviteMemberForm.tsx:71
|
||||
#: src/views/members/components/InviteMemberForm.tsx:77
|
||||
msgid "Error inviting member"
|
||||
msgstr "Error inviting member"
|
||||
|
||||
@@ -847,7 +789,7 @@ msgstr "Error inviting member"
|
||||
msgid "Error updating display name"
|
||||
msgstr "Error updating display name"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:77
|
||||
#: src/views/settings/components/Avatar.tsx:39
|
||||
msgid "Error updating profile image"
|
||||
msgstr "Error updating profile image"
|
||||
|
||||
@@ -863,7 +805,7 @@ msgstr "Error updating workspace name"
|
||||
msgid "Error updating workspace URL"
|
||||
msgstr "Error updating workspace URL"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:221
|
||||
#: src/views/members/components/InviteMemberForm.tsx:130
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
|
||||
msgid "Error upgrading subscription"
|
||||
msgstr "Error upgrading subscription"
|
||||
@@ -872,8 +814,8 @@ msgstr "Error upgrading subscription"
|
||||
msgid "Error upgrading to Pro"
|
||||
msgstr "Error upgrading to Pro"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:91
|
||||
#: src/views/settings/components/Avatar.tsx:218
|
||||
#: src/views/settings/components/Avatar.tsx:56
|
||||
#: src/views/settings/components/Avatar.tsx:97
|
||||
msgid "Error uploading profile image"
|
||||
msgstr "Error uploading profile image"
|
||||
|
||||
@@ -889,16 +831,8 @@ msgstr "Everything you need, free forever. Unlimited boards, unlimited lists, un
|
||||
msgid "Execution"
|
||||
msgstr "Execution"
|
||||
|
||||
#: src/views/invite/index.tsx:41
|
||||
msgid "Failed to accept invitation. Please try again later, or contact customer support."
|
||||
msgstr "Failed to accept invitation. Please try again later, or contact customer support."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:197
|
||||
msgid "Failed to copy invite link"
|
||||
msgstr "Failed to copy invite link"
|
||||
|
||||
#. placeholder {0}: provider.at(0)?.toUpperCase() + provider.slice(1)
|
||||
#: src/components/AuthForm.tsx:273
|
||||
#: src/components/AuthForm.tsx:269
|
||||
msgid "Failed to login with {0}. Please try again."
|
||||
msgstr "Failed to login with {0}. Please try again."
|
||||
|
||||
@@ -947,7 +881,7 @@ msgstr "For long-term sustainability, we recognise all good open source projects
|
||||
msgid "Free"
|
||||
msgstr "Free"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:312
|
||||
#: src/views/members/components/InviteMemberForm.tsx:193
|
||||
#: src/views/members/index.tsx:208
|
||||
msgid "Free Plan"
|
||||
msgstr "Free Plan"
|
||||
@@ -964,7 +898,7 @@ msgstr "Full-time"
|
||||
msgid "Fun"
|
||||
msgstr "Fun"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
#: src/views/home/components/Cta.tsx:61
|
||||
#: src/views/home/components/Header.tsx:102
|
||||
#: src/views/home/components/Header.tsx:141
|
||||
@@ -1004,13 +938,8 @@ msgstr "Getting started"
|
||||
msgid "GitHub"
|
||||
msgstr "GitHub"
|
||||
|
||||
#: src/views/invite/index.tsx:113
|
||||
msgid "Go Home"
|
||||
msgstr "Go Home"
|
||||
|
||||
#: src/views/home/components/Header.tsx:96
|
||||
#: src/views/home/components/Header.tsx:133
|
||||
#: src/views/invite/index.tsx:144
|
||||
msgid "Go to app"
|
||||
msgstr "Go to app"
|
||||
|
||||
@@ -1066,7 +995,7 @@ msgstr "Ideas to improve this page..."
|
||||
#~ msgid "Ideas, Research, Planning, Execution, Review, Next Steps, Complete"
|
||||
#~ msgstr "Ideas, Research, Planning, Execution, Review, Next Steps, Complete"
|
||||
|
||||
#: src/views/boards/index.tsx:43
|
||||
#: src/views/boards/index.tsx:38
|
||||
msgid "Import"
|
||||
msgstr "Import"
|
||||
|
||||
@@ -1104,7 +1033,6 @@ msgstr "In Progress"
|
||||
msgid "Individuals"
|
||||
msgstr "Individuals"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:57
|
||||
#: src/views/home/components/Features.tsx:114
|
||||
msgid "Integrations"
|
||||
msgstr "Integrations"
|
||||
@@ -1113,48 +1041,27 @@ msgstr "Integrations"
|
||||
msgid "Interviewing"
|
||||
msgstr "Interviewing"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:49
|
||||
#: src/views/members/components/InviteMemberForm.tsx:40
|
||||
msgid "Invalid email address"
|
||||
msgstr "Invalid email address"
|
||||
|
||||
#: src/views/invite/index.tsx:105
|
||||
msgid "Invalid invitation"
|
||||
msgstr "Invalid invitation"
|
||||
|
||||
#: src/views/members/index.tsx:221
|
||||
msgid "Invite"
|
||||
msgstr "Invite"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:208
|
||||
#~ msgid "Invite another"
|
||||
#~ msgstr "Invite another"
|
||||
msgid "Invite another"
|
||||
msgstr "Invite another"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:190
|
||||
msgid "Invite link copied"
|
||||
msgstr "Invite link copied"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:191
|
||||
msgid "Invite link copied to clipboard"
|
||||
msgstr "Invite link copied to clipboard"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:111
|
||||
#: src/views/members/components/InviteMemberForm.tsx:350
|
||||
#: src/views/card/components/MemberSelector.tsx:112
|
||||
#: src/views/members/components/InviteMemberForm.tsx:233
|
||||
msgid "Invite member"
|
||||
msgstr "Invite member"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:315
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
|
||||
msgstr "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
|
||||
|
||||
#: src/views/invite/index.tsx:79
|
||||
#: src/views/invite/index.tsx:129
|
||||
msgid "Join workspace"
|
||||
msgstr "Join workspace"
|
||||
|
||||
#: src/views/invite/index.tsx:91
|
||||
msgid "Join workspace | kan.bn"
|
||||
msgstr "Join workspace | kan.bn"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:69
|
||||
msgid "Junior"
|
||||
msgstr "Junior"
|
||||
@@ -1182,7 +1089,7 @@ msgstr "Labels"
|
||||
msgid "Labels & Filters"
|
||||
msgstr "Labels & Filters"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:224
|
||||
msgid "Language"
|
||||
msgstr "Language"
|
||||
|
||||
@@ -1227,7 +1134,7 @@ msgstr "List"
|
||||
msgid "List name"
|
||||
msgstr "List name"
|
||||
|
||||
#: src/views/auth/login/index.tsx:33
|
||||
#: src/views/auth/login/index.tsx:31
|
||||
msgid "Login | kan.bn"
|
||||
msgstr "Login | kan.bn"
|
||||
|
||||
@@ -1243,7 +1150,7 @@ msgstr "Long-term"
|
||||
msgid "Low Priority"
|
||||
msgstr "Low Priority"
|
||||
|
||||
#: src/components/AuthForm.tsx:373
|
||||
#: src/components/AuthForm.tsx:369
|
||||
msgid "magic link"
|
||||
msgstr "magic link"
|
||||
|
||||
@@ -1277,7 +1184,7 @@ msgstr "Members | {0}"
|
||||
msgid "Monthly"
|
||||
msgstr "Monthly"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
#: src/views/members/components/InviteMemberForm.tsx:93
|
||||
msgid "monthly billing"
|
||||
msgstr "monthly billing"
|
||||
|
||||
@@ -1308,14 +1215,10 @@ msgstr "Name"
|
||||
msgid "Need help?"
|
||||
msgstr "Need help?"
|
||||
|
||||
#: src/views/boards/index.tsx:53
|
||||
#: src/views/boards/index.tsx:48
|
||||
msgid "New"
|
||||
msgstr "New"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:147
|
||||
msgid "New API key"
|
||||
msgstr "New API key"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:85
|
||||
msgid "New board"
|
||||
msgstr "New board"
|
||||
@@ -1393,15 +1296,15 @@ msgstr "Offer"
|
||||
msgid "Onboarding"
|
||||
msgstr "Onboarding"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:55
|
||||
#: src/views/settings/index.tsx:349
|
||||
msgid "Once you delete your account, there is no going back. This action cannot be undone."
|
||||
msgstr "Once you delete your account, there is no going back. This action cannot be undone."
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:99
|
||||
#: src/views/settings/index.tsx:312
|
||||
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
|
||||
msgstr "Once you delete your workspace, there is no going back. This action cannot be undone."
|
||||
|
||||
#: src/components/AuthForm.tsx:315
|
||||
#: src/components/AuthForm.tsx:311
|
||||
msgid "or"
|
||||
msgstr "or"
|
||||
|
||||
@@ -1429,10 +1332,6 @@ msgstr "Password must be at least 8 characters"
|
||||
msgid "Passwords do not match"
|
||||
msgstr "Passwords do not match"
|
||||
|
||||
#: src/views/members/index.tsx:134
|
||||
msgid "Paused"
|
||||
msgstr "Paused"
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:102
|
||||
msgid "Payment frequency"
|
||||
msgstr "Payment frequency"
|
||||
@@ -1458,19 +1357,19 @@ msgstr "Planning"
|
||||
msgid "Please confirm your new password"
|
||||
msgstr "Please confirm your new password"
|
||||
|
||||
#: src/components/AuthForm.tsx:341
|
||||
#: src/components/AuthForm.tsx:337
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Please enter a valid email address"
|
||||
|
||||
#: src/components/AuthForm.tsx:329
|
||||
#: src/components/AuthForm.tsx:325
|
||||
msgid "Please enter a valid name"
|
||||
msgstr "Please enter a valid name"
|
||||
|
||||
#: src/components/AuthForm.tsx:354
|
||||
#: src/components/AuthForm.tsx:350
|
||||
msgid "Please enter a valid password"
|
||||
msgstr "Please enter a valid password"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:92
|
||||
#: src/views/settings/components/Avatar.tsx:57
|
||||
msgid "Please select a file to upload."
|
||||
msgstr "Please select a file to upload."
|
||||
|
||||
@@ -1491,18 +1390,18 @@ msgstr "Please select a file to upload."
|
||||
#: src/views/card/components/DeleteCardConfirmation.tsx:52
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:37
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:45
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
#: src/views/card/components/LabelSelector.tsx:73
|
||||
#: src/views/card/components/ListSelector.tsx:53
|
||||
#: src/views/card/components/MemberSelector.tsx:80
|
||||
#: src/views/card/components/NewChecklistForm.tsx:70
|
||||
#: src/views/card/components/NewChecklistItemForm.tsx:89
|
||||
#: src/views/card/components/NewCommentForm.tsx:31
|
||||
#: src/views/card/index.tsx:173
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
|
||||
#: src/views/members/components/InviteMemberForm.tsx:102
|
||||
#: src/views/members/components/InviteMemberForm.tsx:222
|
||||
#: src/views/settings/components/Avatar.tsx:78
|
||||
#: src/views/settings/components/Avatar.tsx:219
|
||||
#: src/views/members/components/InviteMemberForm.tsx:78
|
||||
#: src/views/members/components/InviteMemberForm.tsx:131
|
||||
#: src/views/settings/components/Avatar.tsx:40
|
||||
#: src/views/settings/components/Avatar.tsx:98
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:46
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:55
|
||||
@@ -1513,11 +1412,6 @@ msgstr "Please select a file to upload."
|
||||
msgid "Please try again later, or contact customer support."
|
||||
msgstr "Please try again later, or contact customer support."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:118
|
||||
#: src/views/members/components/InviteMemberForm.tsx:133
|
||||
msgid "Please try again later."
|
||||
msgstr "Please try again later."
|
||||
|
||||
#: src/views/home/components/Footer.tsx:50
|
||||
#: src/views/home/components/Header.tsx:15
|
||||
#: src/views/home/components/Pricing.tsx:85
|
||||
@@ -1541,15 +1435,15 @@ msgstr "Private"
|
||||
msgid "Pro Plan"
|
||||
msgstr "Pro Plan"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
msgid "Pro Plan ∞"
|
||||
msgstr "Pro Plan ∞"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:64
|
||||
#: src/views/settings/components/Avatar.tsx:26
|
||||
msgid "Profile image updated"
|
||||
msgstr "Profile image updated"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:29
|
||||
#: src/views/settings/index.tsx:171
|
||||
msgid "Profile picture"
|
||||
msgstr "Profile picture"
|
||||
|
||||
@@ -1637,8 +1531,8 @@ msgid "Review"
|
||||
msgstr "Review"
|
||||
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:49
|
||||
#~ msgid "Revoke"
|
||||
#~ msgstr "Revoke"
|
||||
msgid "Revoke"
|
||||
msgstr "Revoke"
|
||||
|
||||
#: src/views/home/components/Footer.tsx:36
|
||||
#: src/views/home/components/Header.tsx:13
|
||||
@@ -1654,7 +1548,6 @@ msgid "Run on your own infrastructure"
|
||||
msgstr "Run on your own infrastructure"
|
||||
|
||||
#: src/views/card/components/Comment.tsx:165
|
||||
#: src/views/settings/components/Avatar.tsx:309
|
||||
msgid "Save"
|
||||
msgstr "Save"
|
||||
|
||||
@@ -1698,66 +1591,35 @@ msgstr "Send feedback"
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:82
|
||||
#: src/components/SideNavigation.tsx:78
|
||||
#: src/views/settings/index.tsx:165
|
||||
msgid "Settings"
|
||||
msgstr "Settings"
|
||||
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/settings/index.tsx:161
|
||||
#~ msgid "Settings | {0}"
|
||||
#~ msgstr "Settings | {0}"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:25
|
||||
msgid "Settings | Account"
|
||||
msgstr "Settings | Account"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:18
|
||||
msgid "Settings | API"
|
||||
msgstr "Settings | API"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:35
|
||||
msgid "Settings | Billing"
|
||||
msgstr "Settings | Billing"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:69
|
||||
msgid "Settings | Integrations"
|
||||
msgstr "Settings | Integrations"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:54
|
||||
msgid "Settings | Workspace"
|
||||
msgstr "Settings | Workspace"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:327
|
||||
msgid "Share invite link"
|
||||
msgstr "Share invite link"
|
||||
msgid "Settings | {0}"
|
||||
msgstr "Settings | {0}"
|
||||
|
||||
#: src/views/home/components/Header.tsx:100
|
||||
#: src/views/home/components/Header.tsx:138
|
||||
msgid "Sign in"
|
||||
msgstr "Sign in"
|
||||
|
||||
#: src/views/invite/index.tsx:154
|
||||
msgid "Sign In"
|
||||
msgstr "Sign In"
|
||||
|
||||
#: src/views/invite/index.tsx:162
|
||||
msgid "Sign Up"
|
||||
msgstr "Sign Up"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:34
|
||||
#: src/views/auth/signup/index.tsx:59
|
||||
#: src/views/auth/signup/index.tsx:32
|
||||
#: src/views/auth/signup/index.tsx:57
|
||||
msgid "Sign up | kan.bn"
|
||||
msgstr "Sign up | kan.bn"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:44
|
||||
#: src/views/auth/signup/index.tsx:42
|
||||
msgid "Sign up disabled"
|
||||
msgstr "Sign up disabled"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:47
|
||||
#: src/views/auth/signup/index.tsx:45
|
||||
msgid "Sign up is currently disabled. Please try again later."
|
||||
msgstr "Sign up is currently disabled. Please try again later."
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Sign up with "
|
||||
msgstr "Sign up with "
|
||||
|
||||
@@ -1781,8 +1643,8 @@ msgstr "Software Development"
|
||||
msgid "Star on Github"
|
||||
msgstr "Star on Github"
|
||||
|
||||
#: src/components/AuthForm.tsx:207
|
||||
#: src/components/AuthForm.tsx:224
|
||||
#: src/components/AuthForm.tsx:203
|
||||
#: src/components/AuthForm.tsx:220
|
||||
msgid "Success"
|
||||
msgstr "Success"
|
||||
|
||||
@@ -1802,7 +1664,7 @@ msgstr "Support the development of the project"
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
#: src/views/members/index.tsx:207
|
||||
msgid "Team Plan"
|
||||
msgstr "Team Plan"
|
||||
@@ -1855,10 +1717,6 @@ msgstr "They won't be able to access this workspace."
|
||||
msgid "This action can't be undone."
|
||||
msgstr "This action can't be undone."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:129
|
||||
msgid "This API key will only be shown once. Please save it in a secure location."
|
||||
msgstr "This API key will only be shown once. Please save it in a secure location."
|
||||
|
||||
#: src/views/public/board/index.tsx:151
|
||||
msgid "This board is private or does not exist"
|
||||
msgstr "This board is private or does not exist"
|
||||
@@ -1867,10 +1725,6 @@ msgstr "This board is private or does not exist"
|
||||
msgid "This board URL has already been taken"
|
||||
msgstr "This board URL has already been taken"
|
||||
|
||||
#: src/views/invite/index.tsx:108
|
||||
msgid "This invitation link is invalid or has expired."
|
||||
msgstr "This invitation link is invalid or has expired."
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:70
|
||||
msgid "This will result in the permanent deletion of all data associated with this workspace."
|
||||
msgstr "This will result in the permanent deletion of all data associated with this workspace."
|
||||
@@ -1908,11 +1762,7 @@ msgstr "Toggle menu"
|
||||
msgid "Track all card changes with detailed activity history."
|
||||
msgstr "Track all card changes with detailed activity history."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:73
|
||||
msgid "Trello"
|
||||
msgstr "Trello"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:53
|
||||
#: src/views/settings/index.tsx:119
|
||||
msgid "Trello disconnected"
|
||||
msgstr "Trello disconnected"
|
||||
|
||||
@@ -1997,16 +1847,16 @@ msgstr "Unable to update checklist item"
|
||||
msgid "Unable to update comment"
|
||||
msgstr "Unable to update comment"
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:71
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
msgid "Unable to update labels"
|
||||
msgstr "Unable to update labels"
|
||||
|
||||
#: src/views/board/index.tsx:133
|
||||
#: src/views/card/components/ListSelector.tsx:51
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
msgid "Unable to update list"
|
||||
msgstr "Unable to update list"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:78
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
msgid "Unable to update members"
|
||||
msgstr "Unable to update members"
|
||||
|
||||
@@ -2053,9 +1903,9 @@ msgid "Unlimited members"
|
||||
msgstr "Unlimited members"
|
||||
|
||||
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:79
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:90
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:82
|
||||
#: src/views/settings/components/UpdateWorkspaceUrlForm.tsx:154
|
||||
msgid "Update"
|
||||
msgstr "Update"
|
||||
@@ -2090,7 +1940,7 @@ msgid "Upgrade"
|
||||
msgstr "Upgrade"
|
||||
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
|
||||
#: src/views/settings/WorkspaceSettings.tsx:89
|
||||
#: src/views/settings/index.tsx:216
|
||||
msgid "Upgrade to Pro"
|
||||
msgstr "Upgrade to Pro"
|
||||
|
||||
@@ -2098,7 +1948,7 @@ msgstr "Upgrade to Pro"
|
||||
msgid "Upgrade to Pro ($29/month)"
|
||||
msgstr "Upgrade to Pro ($29/month)"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:341
|
||||
#: src/views/members/components/InviteMemberForm.tsx:224
|
||||
msgid "Upgrade to Team Plan"
|
||||
msgstr "Upgrade to Team Plan"
|
||||
|
||||
@@ -2130,7 +1980,7 @@ msgstr "Use template"
|
||||
msgid "User"
|
||||
msgstr "User"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:96
|
||||
#: src/views/members/components/InviteMemberForm.tsx:72
|
||||
msgid "User is already a member of this workspace"
|
||||
msgstr "User is already a member of this workspace"
|
||||
|
||||
@@ -2138,11 +1988,11 @@ msgstr "User is already a member of this workspace"
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:25
|
||||
#: src/views/settings/index.tsx:299
|
||||
msgid "View and manage your API keys."
|
||||
msgstr "View and manage your API keys."
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:238
|
||||
msgid "View and manage your billing and subscription."
|
||||
msgstr "View and manage your billing and subscription."
|
||||
|
||||
@@ -2174,7 +2024,7 @@ msgstr "We are using the <0>AGPL-3.0 license</0>."
|
||||
msgid "We're just getting started. "
|
||||
msgstr "We're just getting started. "
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
msgid "Welcome back"
|
||||
msgstr "Welcome back"
|
||||
|
||||
@@ -2198,7 +2048,6 @@ msgstr "When Trello launched in 2011, it blew everyone away with its carefully d
|
||||
msgid "Why make an open source Trello?"
|
||||
msgstr "Why make an open source Trello?"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:39
|
||||
#: src/views/board/index.tsx:331
|
||||
msgid "Workspace"
|
||||
msgstr "Workspace"
|
||||
@@ -2211,7 +2060,7 @@ msgstr "Workspace created successfully. You can upgrade later in settings."
|
||||
msgid "Workspace deleted"
|
||||
msgstr "Workspace deleted"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:75
|
||||
#: src/views/settings/index.tsx:202
|
||||
msgid "Workspace description"
|
||||
msgstr "Workspace description"
|
||||
|
||||
@@ -2233,7 +2082,7 @@ msgid "Workspace members"
|
||||
msgstr "Workspace members"
|
||||
|
||||
#: src/components/NewWorkspaceForm.tsx:259
|
||||
#: src/views/settings/WorkspaceSettings.tsx:58
|
||||
#: src/views/settings/index.tsx:183
|
||||
msgid "Workspace name"
|
||||
msgstr "Workspace name"
|
||||
|
||||
@@ -2257,7 +2106,7 @@ msgstr "Workspace name updated"
|
||||
msgid "Workspace slug updated"
|
||||
msgstr "Workspace slug updated"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:66
|
||||
#: src/views/settings/index.tsx:192
|
||||
msgid "Workspace URL"
|
||||
msgstr "Workspace URL"
|
||||
|
||||
@@ -2277,7 +2126,7 @@ msgstr "Yearly"
|
||||
msgid "Yes, we offer an forever free plan for individual use. No restrictions, no paywalls, no limits."
|
||||
msgstr "Yes, we offer an forever free plan for individual use. No restrictions, no paywalls, no limits."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:73
|
||||
#: src/views/settings/index.tsx:331
|
||||
msgid "You are about to change your password."
|
||||
msgstr "You are about to change your password."
|
||||
|
||||
@@ -2293,26 +2142,18 @@ msgstr "You can invite team members by clicking the \"Invite\" button in the top
|
||||
msgid "You can self-host by following the instructions in our <0>repo</0>."
|
||||
msgstr "You can self-host by following the instructions in our <0>repo</0>."
|
||||
|
||||
#: src/components/AuthForm.tsx:225
|
||||
#: src/components/AuthForm.tsx:221
|
||||
msgid "You have been logged in successfully."
|
||||
msgstr "You have been logged in successfully."
|
||||
|
||||
#: src/components/AuthForm.tsx:208
|
||||
#: src/components/AuthForm.tsx:204
|
||||
msgid "You have been signed up successfully."
|
||||
msgstr "You have been signed up successfully."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:305
|
||||
#: src/views/members/components/InviteMemberForm.tsx:186
|
||||
msgid "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
|
||||
msgstr "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
|
||||
|
||||
#: src/views/invite/index.tsx:134
|
||||
msgid "You've been invited to join a workspace on kan.bn."
|
||||
msgstr "You've been invited to join a workspace on kan.bn."
|
||||
|
||||
#: src/views/invite/index.tsx:135
|
||||
msgid "You've been invited to join a workspace."
|
||||
msgstr "You've been invited to join a workspace."
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:28
|
||||
msgid "Your account has been deleted."
|
||||
msgstr "Your account has been deleted."
|
||||
@@ -2329,15 +2170,15 @@ msgstr "Your display name has been updated."
|
||||
msgid "Your password has been changed."
|
||||
msgstr "Your password has been changed."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:65
|
||||
#: src/views/settings/components/Avatar.tsx:27
|
||||
msgid "Your profile image has been updated."
|
||||
msgstr "Your profile image has been updated."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:54
|
||||
#: src/views/settings/index.tsx:120
|
||||
msgid "Your Trello account has been disconnected."
|
||||
msgstr "Your Trello account has been disconnected."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:102
|
||||
#: src/views/settings/index.tsx:281
|
||||
msgid "Your Trello account is connected."
|
||||
msgstr "Your Trello account is connected."
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -36,12 +36,12 @@ msgstr "{0} etiquetas"
|
||||
msgid "{boardCount, plural, one {Import board (1)} other {Import boards ({boardCount})}}"
|
||||
msgstr "{boardCount, plural, one {Importar tablero (1)} other {Importar tableros ({boardCount})}}"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:146
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:92
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$10/month"
|
||||
msgstr "$10/mes"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$8/month"
|
||||
msgstr "$8/mes"
|
||||
|
||||
@@ -53,10 +53,6 @@ msgstr "1 usuario"
|
||||
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
|
||||
msgstr "Una aplicación kanban potente y flexible que te ayuda a organizar el trabajo, seguir el progreso y entregar resultados, todo en un solo lugar."
|
||||
|
||||
#: src/components/SettingsLayout.tsx:33
|
||||
msgid "Account"
|
||||
msgstr "Cuenta"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:27
|
||||
msgid "Account deleted"
|
||||
msgstr "Cuenta eliminada"
|
||||
@@ -95,13 +91,13 @@ msgstr "Añadir descripción... (escribe '/' para abrir comandos o '@' para menc
|
||||
msgid "Add details..."
|
||||
msgstr "Añadir detalles..."
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:109
|
||||
#: src/views/card/components/LabelSelector.tsx:114
|
||||
#: src/views/card/components/LabelSelector.tsx:110
|
||||
#: src/views/card/components/LabelSelector.tsx:118
|
||||
msgid "Add label"
|
||||
msgstr "Añadir etiqueta"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:130
|
||||
#: src/views/members/components/InviteMemberForm.tsx:238
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
msgid "Add member"
|
||||
msgstr "Añadir miembro"
|
||||
|
||||
@@ -136,14 +132,10 @@ msgstr "añadió el elemento <0>{0}</0> a la lista de verificación"
|
||||
msgid "added label <0>{0}</0>"
|
||||
msgstr "añadió la etiqueta <0>{0}</0>"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:306
|
||||
#: src/views/members/components/InviteMemberForm.tsx:187
|
||||
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
|
||||
msgstr "Añadir un nuevo miembro costará {price} adicionales ({billingType}) por asiento."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:275
|
||||
msgid "Adjust the square crop to fit your avatar."
|
||||
msgstr "Ajusta el recorte cuadrado para que se adapte a tu avatar."
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:55
|
||||
msgid "Admin roles"
|
||||
msgstr "Roles de administrador"
|
||||
@@ -152,11 +144,11 @@ msgstr "Roles de administrador"
|
||||
msgid "All systems operational"
|
||||
msgstr "Todos los sistemas operativos"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:88
|
||||
#: src/views/auth/signup/index.tsx:86
|
||||
msgid "Already have an account? <0><1>Sign in</1></0>"
|
||||
msgstr "¿Ya tienes una cuenta? <0><1>Iniciar sesión</1></0>"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:61
|
||||
#: src/views/settings/index.tsx:127
|
||||
msgid "An error occurred while disconnecting your Trello account."
|
||||
msgstr "Ocurrió un error al desconectar tu cuenta de Trello."
|
||||
|
||||
@@ -164,31 +156,7 @@ msgstr "Ocurrió un error al desconectar tu cuenta de Trello."
|
||||
msgid "An unexpected error occurred. Please try again later."
|
||||
msgstr "Ha ocurrido un error inesperado. Por favor, inténtalo de nuevo más tarde."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:290
|
||||
msgid "Anyone with this link can join your workspace"
|
||||
msgstr "Cualquier persona con este enlace puede unirse a tu espacio de trabajo"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:51
|
||||
msgid "API"
|
||||
msgstr "API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:91
|
||||
msgid "API key created"
|
||||
msgstr "Clave API creada"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:161
|
||||
msgid "API key name"
|
||||
msgstr "Nombre de la clave API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:25
|
||||
msgid "API key name cannot exceed 30 characters"
|
||||
msgstr "El nombre de la clave API no puede exceder los 30 caracteres"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:24
|
||||
msgid "API key name is required"
|
||||
msgstr "El nombre de la clave API es obligatorio"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:22
|
||||
#: src/views/settings/index.tsx:296
|
||||
msgid "API keys"
|
||||
msgstr "Claves API"
|
||||
|
||||
@@ -250,21 +218,20 @@ msgstr "Pendientes"
|
||||
msgid "Basic Kanban"
|
||||
msgstr "Kanban básico"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed annually"
|
||||
msgstr "facturado anualmente"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed monthly"
|
||||
msgstr "facturado mensualmente"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:44
|
||||
#: src/views/boards/components/TemplateBoards.tsx:55
|
||||
#: src/views/settings/BillingSettings.tsx:39
|
||||
#: src/views/settings/index.tsx:235
|
||||
msgid "Billing"
|
||||
msgstr "Facturación"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:49
|
||||
#: src/views/settings/index.tsx:245
|
||||
msgid "Billing portal"
|
||||
msgstr "Portal de facturación"
|
||||
|
||||
@@ -319,12 +286,12 @@ msgid "Board visibility updated"
|
||||
msgstr "Visibilidad del tablero actualizada"
|
||||
|
||||
#: src/components/SideNavigation.tsx:68
|
||||
#: src/views/boards/index.tsx:32
|
||||
#: src/views/boards/index.tsx:27
|
||||
msgid "Boards"
|
||||
msgstr "Tableros"
|
||||
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/boards/index.tsx:28
|
||||
#: src/views/boards/index.tsx:23
|
||||
msgid "Boards | {0}"
|
||||
msgstr "Tableros | {0}"
|
||||
|
||||
@@ -346,7 +313,6 @@ msgstr "Informe de error"
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:63
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:76
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:55
|
||||
#: src/views/settings/components/Avatar.tsx:306
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
|
||||
@@ -362,19 +328,19 @@ msgstr "Tarjeta no encontrada"
|
||||
msgid "Card title"
|
||||
msgstr "Título de la tarjeta"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:70
|
||||
#: src/views/settings/AccountSettings.tsx:80
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:109
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:178
|
||||
#: src/views/settings/index.tsx:328
|
||||
#: src/views/settings/index.tsx:338
|
||||
msgid "Change Password"
|
||||
msgstr "Cambiar contraseña"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:45
|
||||
msgid "Change your language preferences."
|
||||
msgstr "Cambia tus preferencias de idioma."
|
||||
#: src/views/settings/index.tsx:227
|
||||
msgid "Change the language of the app."
|
||||
msgstr "Cambiar el idioma de la aplicación."
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
msgid "Check your inbox"
|
||||
msgstr "Revisa tu bandeja de entrada"
|
||||
|
||||
@@ -386,15 +352,11 @@ msgstr "Nombre de la lista de verificación"
|
||||
msgid "Clear filters"
|
||||
msgstr "Borrar filtros"
|
||||
|
||||
#: src/views/auth/login/index.tsx:48
|
||||
#: src/views/auth/signup/index.tsx:74
|
||||
#: src/views/auth/login/index.tsx:46
|
||||
#: src/views/auth/signup/index.tsx:72
|
||||
msgid "Click on the link we've sent to {magicLinkRecipient} to sign in."
|
||||
msgstr "Haz clic en el enlace que hemos enviado a {magicLinkRecipient} para iniciar sesión."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:136
|
||||
msgid "Close"
|
||||
msgstr "Cerrar"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:22
|
||||
msgid "Code Review"
|
||||
msgstr "Revisión de código"
|
||||
@@ -439,7 +401,7 @@ msgid "Confirm your new password"
|
||||
msgstr "Confirma tu nueva contraseña"
|
||||
|
||||
#: src/views/boards/components/ImportBoardsForm.tsx:157
|
||||
#: src/views/settings/IntegrationsSettings.tsx:93
|
||||
#: src/views/settings/index.tsx:272
|
||||
msgid "Connect Trello"
|
||||
msgstr "Conectar Trello"
|
||||
|
||||
@@ -447,7 +409,7 @@ msgstr "Conectar Trello"
|
||||
msgid "Connect your favorite tools to streamline your workflow."
|
||||
msgstr "Conecta tus herramientas favoritas para agilizar tu flujo de trabajo."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:80
|
||||
#: src/views/settings/index.tsx:259
|
||||
msgid "Connect your Trello account to import boards."
|
||||
msgstr "Conecta tu cuenta de Trello para importar tableros."
|
||||
|
||||
@@ -463,12 +425,12 @@ msgstr "Contáctanos"
|
||||
msgid "Content Creation"
|
||||
msgstr "Creación de contenido"
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Continue with "
|
||||
msgstr "Continuar con "
|
||||
|
||||
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
|
||||
#: src/components/AuthForm.tsx:301
|
||||
#: src/components/AuthForm.tsx:297
|
||||
msgid "Continue with {0}"
|
||||
msgstr "Continuar con {0}"
|
||||
|
||||
@@ -482,10 +444,6 @@ msgstr "Controla quién puede ver y editar tus tableros."
|
||||
msgid "Create another"
|
||||
msgstr "Crear otro"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:175
|
||||
msgid "Create API key"
|
||||
msgstr "Crear clave API"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:128
|
||||
msgid "Create board"
|
||||
msgstr "Crear tablero"
|
||||
@@ -510,12 +468,12 @@ msgstr "Crear lista"
|
||||
msgid "Create new board"
|
||||
msgstr "Crear nuevo tablero"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:30
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:54
|
||||
msgid "Create new key"
|
||||
msgstr "Crear nueva clave"
|
||||
|
||||
#: src/views/board/components/NewCardForm.tsx:394
|
||||
#: src/views/card/components/LabelSelector.tsx:97
|
||||
#: src/views/card/components/LabelSelector.tsx:98
|
||||
msgid "Create new label"
|
||||
msgstr "Crear nueva etiqueta"
|
||||
|
||||
@@ -536,10 +494,6 @@ msgstr "creó la tarjeta"
|
||||
msgid "Critical"
|
||||
msgstr "Crítico"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:272
|
||||
msgid "Crop your avatar"
|
||||
msgstr "Recorta tu avatar"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:19
|
||||
msgid "Current password is required"
|
||||
msgstr "Se requiere la contraseña actual"
|
||||
@@ -573,9 +527,9 @@ msgstr "Oscuro"
|
||||
msgid "Delete"
|
||||
msgstr "Eliminar"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:52
|
||||
#: src/views/settings/AccountSettings.tsx:62
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:96
|
||||
#: src/views/settings/index.tsx:346
|
||||
#: src/views/settings/index.tsx:356
|
||||
msgid "Delete account"
|
||||
msgstr "Eliminar cuenta"
|
||||
|
||||
@@ -596,8 +550,8 @@ msgid "Delete list"
|
||||
msgstr "Eliminar lista"
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
|
||||
#: src/views/settings/WorkspaceSettings.tsx:96
|
||||
#: src/views/settings/WorkspaceSettings.tsx:107
|
||||
#: src/views/settings/index.tsx:309
|
||||
#: src/views/settings/index.tsx:320
|
||||
msgid "Delete workspace"
|
||||
msgstr "Eliminar espacio de trabajo"
|
||||
|
||||
@@ -623,7 +577,7 @@ msgstr "eliminó el elemento <0>{0}</0> de la lista de verificación"
|
||||
msgid "Design"
|
||||
msgstr "Diseño"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:108
|
||||
#: src/views/settings/index.tsx:287
|
||||
msgid "Disconnect Trello"
|
||||
msgstr "Desconectar Trello"
|
||||
|
||||
@@ -631,7 +585,7 @@ msgstr "Desconectar Trello"
|
||||
msgid "Discuss and collaborate on cards."
|
||||
msgstr "Discute y colabora en las tarjetas."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:35
|
||||
#: src/views/settings/index.tsx:176
|
||||
msgid "Display name"
|
||||
msgstr "Nombre visible"
|
||||
|
||||
@@ -665,7 +619,7 @@ msgstr "Documentos"
|
||||
msgid "Documentation"
|
||||
msgstr "Documentación"
|
||||
|
||||
#: src/views/auth/login/index.tsx:63
|
||||
#: src/views/auth/login/index.tsx:61
|
||||
msgid "Don't have an account? <0><1>Sign up</1></0>"
|
||||
msgstr "¿No tienes una cuenta? <0><1>Regístrate</1></0>"
|
||||
|
||||
@@ -697,11 +651,11 @@ msgstr "Editar URL del espacio de trabajo"
|
||||
msgid "Editing"
|
||||
msgstr "Editando"
|
||||
|
||||
#: src/components/AuthForm.tsx:372
|
||||
#: src/components/AuthForm.tsx:368
|
||||
msgid "email"
|
||||
msgstr "correo electrónico"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:252
|
||||
#: src/views/members/components/InviteMemberForm.tsx:161
|
||||
msgid "Email"
|
||||
msgstr "Correo electrónico"
|
||||
|
||||
@@ -717,11 +671,11 @@ msgstr "Introduce tu contraseña actual"
|
||||
msgid "Enter your current password and choose a new secure password."
|
||||
msgstr "Introduce tu contraseña actual y elige una nueva contraseña segura."
|
||||
|
||||
#: src/components/AuthForm.tsx:337
|
||||
#: src/components/AuthForm.tsx:333
|
||||
msgid "Enter your email address"
|
||||
msgstr "Introduce tu dirección de correo electrónico"
|
||||
|
||||
#: src/components/AuthForm.tsx:325
|
||||
#: src/components/AuthForm.tsx:321
|
||||
msgid "Enter your name"
|
||||
msgstr "Introduce tu nombre"
|
||||
|
||||
@@ -729,26 +683,14 @@ msgstr "Introduce tu nombre"
|
||||
msgid "Enter your new password"
|
||||
msgstr "Introduce tu nueva contraseña"
|
||||
|
||||
#: src/components/AuthForm.tsx:350
|
||||
#: src/components/AuthForm.tsx:346
|
||||
msgid "Enter your password"
|
||||
msgstr "Introduce tu contraseña"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:89
|
||||
msgid "Error Changing Password"
|
||||
msgstr "Error al cambiar la contraseña"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:117
|
||||
msgid "Error creating invite link"
|
||||
msgstr "Error al crear el enlace de invitación"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:132
|
||||
msgid "Error deactivating invite link"
|
||||
msgstr "Error al desactivar el enlace de invitación"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:39
|
||||
msgid "Error deleting account"
|
||||
msgstr "Error al eliminar la cuenta"
|
||||
@@ -761,12 +703,12 @@ msgstr "Error al eliminar la etiqueta"
|
||||
msgid "Error deleting workspace"
|
||||
msgstr "Error al eliminar el espacio de trabajo"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:60
|
||||
#: src/views/settings/index.tsx:126
|
||||
msgid "Error disconnecting Trello"
|
||||
msgstr "Error al desconectar Trello"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:95
|
||||
#: src/views/members/components/InviteMemberForm.tsx:101
|
||||
#: src/views/members/components/InviteMemberForm.tsx:71
|
||||
#: src/views/members/components/InviteMemberForm.tsx:77
|
||||
msgid "Error inviting member"
|
||||
msgstr "Error al invitar al miembro"
|
||||
|
||||
@@ -774,7 +716,7 @@ msgstr "Error al invitar al miembro"
|
||||
msgid "Error updating display name"
|
||||
msgstr "Error al actualizar el nombre visible"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:77
|
||||
#: src/views/settings/components/Avatar.tsx:39
|
||||
msgid "Error updating profile image"
|
||||
msgstr "Error al actualizar la imagen de perfil"
|
||||
|
||||
@@ -790,7 +732,7 @@ msgstr "Error al actualizar el nombre del espacio de trabajo"
|
||||
msgid "Error updating workspace URL"
|
||||
msgstr "Error al actualizar la URL del espacio de trabajo"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:221
|
||||
#: src/views/members/components/InviteMemberForm.tsx:130
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
|
||||
msgid "Error upgrading subscription"
|
||||
msgstr "Error al actualizar la suscripción"
|
||||
@@ -799,8 +741,8 @@ msgstr "Error al actualizar la suscripción"
|
||||
msgid "Error upgrading to Pro"
|
||||
msgstr "Error al actualizar a Pro"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:91
|
||||
#: src/views/settings/components/Avatar.tsx:218
|
||||
#: src/views/settings/components/Avatar.tsx:56
|
||||
#: src/views/settings/components/Avatar.tsx:97
|
||||
msgid "Error uploading profile image"
|
||||
msgstr "Error al subir la imagen de perfil"
|
||||
|
||||
@@ -816,16 +758,8 @@ msgstr "Todo lo que necesitas, gratis para siempre. Tableros ilimitados, listas
|
||||
msgid "Execution"
|
||||
msgstr "Ejecución"
|
||||
|
||||
#: src/views/invite/index.tsx:41
|
||||
msgid "Failed to accept invitation. Please try again later, or contact customer support."
|
||||
msgstr "No se pudo aceptar la invitación. Por favor, inténtalo de nuevo más tarde o contacta con atención al cliente."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:197
|
||||
msgid "Failed to copy invite link"
|
||||
msgstr "No se pudo copiar el enlace de invitación"
|
||||
|
||||
#. placeholder {0}: provider.at(0)?.toUpperCase() + provider.slice(1)
|
||||
#: src/components/AuthForm.tsx:273
|
||||
#: src/components/AuthForm.tsx:269
|
||||
msgid "Failed to login with {0}. Please try again."
|
||||
msgstr "Error al iniciar sesión con {0}. Por favor, inténtalo de nuevo."
|
||||
|
||||
@@ -873,7 +807,7 @@ msgstr "Para la sostenibilidad a largo plazo, reconocemos que todos los buenos p
|
||||
msgid "Free"
|
||||
msgstr "Gratis"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:312
|
||||
#: src/views/members/components/InviteMemberForm.tsx:193
|
||||
#: src/views/members/index.tsx:208
|
||||
msgid "Free Plan"
|
||||
msgstr "Plan gratuito"
|
||||
@@ -890,7 +824,7 @@ msgstr "Tiempo completo"
|
||||
msgid "Fun"
|
||||
msgstr "Diversión"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
#: src/views/home/components/Cta.tsx:61
|
||||
#: src/views/home/components/Header.tsx:102
|
||||
#: src/views/home/components/Header.tsx:141
|
||||
@@ -930,13 +864,8 @@ msgstr "Primeros pasos"
|
||||
msgid "GitHub"
|
||||
msgstr "GitHub"
|
||||
|
||||
#: src/views/invite/index.tsx:113
|
||||
msgid "Go Home"
|
||||
msgstr "Ir a inicio"
|
||||
|
||||
#: src/views/home/components/Header.tsx:96
|
||||
#: src/views/home/components/Header.tsx:133
|
||||
#: src/views/invite/index.tsx:144
|
||||
msgid "Go to app"
|
||||
msgstr "Ir a la aplicación"
|
||||
|
||||
@@ -988,7 +917,7 @@ msgstr "Ideas"
|
||||
msgid "Ideas to improve this page..."
|
||||
msgstr "Ideas para mejorar esta página..."
|
||||
|
||||
#: src/views/boards/index.tsx:43
|
||||
#: src/views/boards/index.tsx:38
|
||||
msgid "Import"
|
||||
msgstr "Importar"
|
||||
|
||||
@@ -1026,7 +955,6 @@ msgstr "En progreso"
|
||||
msgid "Individuals"
|
||||
msgstr "Individuos"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:57
|
||||
#: src/views/home/components/Features.tsx:114
|
||||
msgid "Integrations"
|
||||
msgstr "Integraciones"
|
||||
@@ -1035,44 +963,27 @@ msgstr "Integraciones"
|
||||
msgid "Interviewing"
|
||||
msgstr "Entrevistando"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:49
|
||||
#: src/views/members/components/InviteMemberForm.tsx:40
|
||||
msgid "Invalid email address"
|
||||
msgstr "Dirección de correo electrónico no válida"
|
||||
|
||||
#: src/views/invite/index.tsx:105
|
||||
msgid "Invalid invitation"
|
||||
msgstr "Invitación no válida"
|
||||
|
||||
#: src/views/members/index.tsx:221
|
||||
msgid "Invite"
|
||||
msgstr "Invitar"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:190
|
||||
msgid "Invite link copied"
|
||||
msgstr "Enlace de invitación copiado"
|
||||
#: src/views/members/components/InviteMemberForm.tsx:208
|
||||
msgid "Invite another"
|
||||
msgstr "Invitar a otro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:191
|
||||
msgid "Invite link copied to clipboard"
|
||||
msgstr "Enlace de invitación copiado al portapapeles"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:111
|
||||
#: src/views/members/components/InviteMemberForm.tsx:350
|
||||
#: src/views/card/components/MemberSelector.tsx:112
|
||||
#: src/views/members/components/InviteMemberForm.tsx:233
|
||||
msgid "Invite member"
|
||||
msgstr "Invitar miembro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:315
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
|
||||
msgstr "Invitar miembros requiere un Plan de Equipo. Serás redirigido para actualizar tu espacio de trabajo."
|
||||
|
||||
#: src/views/invite/index.tsx:79
|
||||
#: src/views/invite/index.tsx:129
|
||||
msgid "Join workspace"
|
||||
msgstr "Unirse al espacio de trabajo"
|
||||
|
||||
#: src/views/invite/index.tsx:91
|
||||
msgid "Join workspace | kan.bn"
|
||||
msgstr "Unirse al espacio de trabajo | kan.bn"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:69
|
||||
msgid "Junior"
|
||||
msgstr "Junior"
|
||||
@@ -1100,7 +1011,7 @@ msgstr "Etiquetas"
|
||||
msgid "Labels & Filters"
|
||||
msgstr "Etiquetas y filtros"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:224
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
|
||||
@@ -1145,7 +1056,7 @@ msgstr "Lista"
|
||||
msgid "List name"
|
||||
msgstr "Nombre de la lista"
|
||||
|
||||
#: src/views/auth/login/index.tsx:33
|
||||
#: src/views/auth/login/index.tsx:31
|
||||
msgid "Login | kan.bn"
|
||||
msgstr "Iniciar sesión | kan.bn"
|
||||
|
||||
@@ -1161,7 +1072,7 @@ msgstr "Largo plazo"
|
||||
msgid "Low Priority"
|
||||
msgstr "Prioridad baja"
|
||||
|
||||
#: src/components/AuthForm.tsx:373
|
||||
#: src/components/AuthForm.tsx:369
|
||||
msgid "magic link"
|
||||
msgstr "enlace mágico"
|
||||
|
||||
@@ -1195,7 +1106,7 @@ msgstr "Miembros | {0}"
|
||||
msgid "Monthly"
|
||||
msgstr "Mensual"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
#: src/views/members/components/InviteMemberForm.tsx:93
|
||||
msgid "monthly billing"
|
||||
msgstr "facturación mensual"
|
||||
|
||||
@@ -1218,14 +1129,10 @@ msgstr "Nombre"
|
||||
msgid "Need help?"
|
||||
msgstr "¿Necesitas ayuda?"
|
||||
|
||||
#: src/views/boards/index.tsx:53
|
||||
#: src/views/boards/index.tsx:48
|
||||
msgid "New"
|
||||
msgstr "Nuevo"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:147
|
||||
msgid "New API key"
|
||||
msgstr "Nueva clave API"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:85
|
||||
msgid "New board"
|
||||
msgstr "Nuevo tablero"
|
||||
@@ -1299,15 +1206,15 @@ msgstr "Oferta"
|
||||
msgid "Onboarding"
|
||||
msgstr "Incorporación"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:55
|
||||
#: src/views/settings/index.tsx:349
|
||||
msgid "Once you delete your account, there is no going back. This action cannot be undone."
|
||||
msgstr "Una vez que elimines tu cuenta, no hay vuelta atrás. Esta acción no se puede deshacer."
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:99
|
||||
#: src/views/settings/index.tsx:312
|
||||
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
|
||||
msgstr "Una vez que elimines tu espacio de trabajo, no hay vuelta atrás. Esta acción no se puede deshacer."
|
||||
|
||||
#: src/components/AuthForm.tsx:315
|
||||
#: src/components/AuthForm.tsx:311
|
||||
msgid "or"
|
||||
msgstr "o"
|
||||
|
||||
@@ -1335,10 +1242,6 @@ msgstr "La contraseña debe tener al menos 8 caracteres"
|
||||
msgid "Passwords do not match"
|
||||
msgstr "Las contraseñas no coinciden"
|
||||
|
||||
#: src/views/members/index.tsx:134
|
||||
msgid "Paused"
|
||||
msgstr "Pausado"
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:102
|
||||
msgid "Payment frequency"
|
||||
msgstr "Frecuencia de pago"
|
||||
@@ -1364,19 +1267,19 @@ msgstr "Planificación"
|
||||
msgid "Please confirm your new password"
|
||||
msgstr "Por favor, confirma tu nueva contraseña"
|
||||
|
||||
#: src/components/AuthForm.tsx:341
|
||||
#: src/components/AuthForm.tsx:337
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Por favor, introduce una dirección de correo electrónico válida"
|
||||
|
||||
#: src/components/AuthForm.tsx:329
|
||||
#: src/components/AuthForm.tsx:325
|
||||
msgid "Please enter a valid name"
|
||||
msgstr "Por favor, introduce un nombre válido"
|
||||
|
||||
#: src/components/AuthForm.tsx:354
|
||||
#: src/components/AuthForm.tsx:350
|
||||
msgid "Please enter a valid password"
|
||||
msgstr "Por favor, introduce una contraseña válida"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:92
|
||||
#: src/views/settings/components/Avatar.tsx:57
|
||||
msgid "Please select a file to upload."
|
||||
msgstr "Por favor selecciona un archivo para subir."
|
||||
|
||||
@@ -1397,18 +1300,18 @@ msgstr "Por favor selecciona un archivo para subir."
|
||||
#: src/views/card/components/DeleteCardConfirmation.tsx:52
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:37
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:45
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
#: src/views/card/components/LabelSelector.tsx:73
|
||||
#: src/views/card/components/ListSelector.tsx:53
|
||||
#: src/views/card/components/MemberSelector.tsx:80
|
||||
#: src/views/card/components/NewChecklistForm.tsx:70
|
||||
#: src/views/card/components/NewChecklistItemForm.tsx:89
|
||||
#: src/views/card/components/NewCommentForm.tsx:31
|
||||
#: src/views/card/index.tsx:173
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
|
||||
#: src/views/members/components/InviteMemberForm.tsx:102
|
||||
#: src/views/members/components/InviteMemberForm.tsx:222
|
||||
#: src/views/settings/components/Avatar.tsx:78
|
||||
#: src/views/settings/components/Avatar.tsx:219
|
||||
#: src/views/members/components/InviteMemberForm.tsx:78
|
||||
#: src/views/members/components/InviteMemberForm.tsx:131
|
||||
#: src/views/settings/components/Avatar.tsx:40
|
||||
#: src/views/settings/components/Avatar.tsx:98
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:46
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:55
|
||||
@@ -1419,11 +1322,6 @@ msgstr "Por favor selecciona un archivo para subir."
|
||||
msgid "Please try again later, or contact customer support."
|
||||
msgstr "Por favor, inténtalo de nuevo más tarde o contacta con atención al cliente."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:118
|
||||
#: src/views/members/components/InviteMemberForm.tsx:133
|
||||
msgid "Please try again later."
|
||||
msgstr "Por favor, inténtalo de nuevo más tarde."
|
||||
|
||||
#: src/views/home/components/Footer.tsx:50
|
||||
#: src/views/home/components/Header.tsx:15
|
||||
#: src/views/home/components/Pricing.tsx:85
|
||||
@@ -1447,15 +1345,15 @@ msgstr "Privado"
|
||||
msgid "Pro Plan"
|
||||
msgstr "Plan Pro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
msgid "Pro Plan ∞"
|
||||
msgstr "Plan Pro ∞"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:64
|
||||
#: src/views/settings/components/Avatar.tsx:26
|
||||
msgid "Profile image updated"
|
||||
msgstr "Imagen de perfil actualizada"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:29
|
||||
#: src/views/settings/index.tsx:171
|
||||
msgid "Profile picture"
|
||||
msgstr "Foto de perfil"
|
||||
|
||||
@@ -1538,6 +1436,10 @@ msgstr "Recursos"
|
||||
msgid "Review"
|
||||
msgstr "Revisión"
|
||||
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:49
|
||||
msgid "Revoke"
|
||||
msgstr "Revocar"
|
||||
|
||||
#: src/views/home/components/Footer.tsx:36
|
||||
#: src/views/home/components/Header.tsx:13
|
||||
msgid "Roadmap"
|
||||
@@ -1552,7 +1454,6 @@ msgid "Run on your own infrastructure"
|
||||
msgstr "Ejecuta en tu propia infraestructura"
|
||||
|
||||
#: src/views/card/components/Comment.tsx:165
|
||||
#: src/views/settings/components/Avatar.tsx:309
|
||||
msgid "Save"
|
||||
msgstr "Guardar"
|
||||
|
||||
@@ -1592,62 +1493,35 @@ msgstr "Enviar comentarios"
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:82
|
||||
#: src/components/SideNavigation.tsx:78
|
||||
#: src/views/settings/index.tsx:165
|
||||
msgid "Settings"
|
||||
msgstr "Configuración"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:25
|
||||
msgid "Settings | Account"
|
||||
msgstr "Configuración | Cuenta"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:18
|
||||
msgid "Settings | API"
|
||||
msgstr "Configuración | API"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:35
|
||||
msgid "Settings | Billing"
|
||||
msgstr "Configuración | Facturación"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:69
|
||||
msgid "Settings | Integrations"
|
||||
msgstr "Configuración | Integraciones"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:54
|
||||
msgid "Settings | Workspace"
|
||||
msgstr "Configuración | Espacio de trabajo"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:327
|
||||
msgid "Share invite link"
|
||||
msgstr "Compartir enlace de invitación"
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/settings/index.tsx:161
|
||||
msgid "Settings | {0}"
|
||||
msgstr "Configuración | {0}"
|
||||
|
||||
#: src/views/home/components/Header.tsx:100
|
||||
#: src/views/home/components/Header.tsx:138
|
||||
msgid "Sign in"
|
||||
msgstr "Iniciar sesión"
|
||||
|
||||
#: src/views/invite/index.tsx:154
|
||||
msgid "Sign In"
|
||||
msgstr "Iniciar sesión"
|
||||
|
||||
#: src/views/invite/index.tsx:162
|
||||
msgid "Sign Up"
|
||||
msgstr "Registrarse"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:34
|
||||
#: src/views/auth/signup/index.tsx:59
|
||||
#: src/views/auth/signup/index.tsx:32
|
||||
#: src/views/auth/signup/index.tsx:57
|
||||
msgid "Sign up | kan.bn"
|
||||
msgstr "Registrarse | kan.bn"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:44
|
||||
#: src/views/auth/signup/index.tsx:42
|
||||
msgid "Sign up disabled"
|
||||
msgstr "Registro deshabilitado"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:47
|
||||
#: src/views/auth/signup/index.tsx:45
|
||||
msgid "Sign up is currently disabled. Please try again later."
|
||||
msgstr "El registro está actualmente deshabilitado. Por favor, inténtalo de nuevo más tarde."
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Sign up with "
|
||||
msgstr "Registrarse con "
|
||||
|
||||
@@ -1671,8 +1545,8 @@ msgstr "Desarrollo de software"
|
||||
msgid "Star on Github"
|
||||
msgstr "Estrella en Github"
|
||||
|
||||
#: src/components/AuthForm.tsx:207
|
||||
#: src/components/AuthForm.tsx:224
|
||||
#: src/components/AuthForm.tsx:203
|
||||
#: src/components/AuthForm.tsx:220
|
||||
msgid "Success"
|
||||
msgstr "Éxito"
|
||||
|
||||
@@ -1692,7 +1566,7 @@ msgstr "Apoya el desarrollo del proyecto"
|
||||
msgid "System"
|
||||
msgstr "Sistema"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
#: src/views/members/index.tsx:207
|
||||
msgid "Team Plan"
|
||||
msgstr "Plan de Equipo"
|
||||
@@ -1745,10 +1619,6 @@ msgstr "No podrán acceder a este espacio de trabajo."
|
||||
msgid "This action can't be undone."
|
||||
msgstr "Esta acción no se puede deshacer."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:129
|
||||
msgid "This API key will only be shown once. Please save it in a secure location."
|
||||
msgstr "Esta clave API solo se mostrará una vez. Por favor, guárdala en un lugar seguro."
|
||||
|
||||
#: src/views/public/board/index.tsx:151
|
||||
msgid "This board is private or does not exist"
|
||||
msgstr "Este tablero es privado o no existe"
|
||||
@@ -1757,10 +1627,6 @@ msgstr "Este tablero es privado o no existe"
|
||||
msgid "This board URL has already been taken"
|
||||
msgstr "Esta URL de tablero ya ha sido utilizada"
|
||||
|
||||
#: src/views/invite/index.tsx:108
|
||||
msgid "This invitation link is invalid or has expired."
|
||||
msgstr "Este enlace de invitación no es válido o ha caducado."
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:70
|
||||
msgid "This will result in the permanent deletion of all data associated with this workspace."
|
||||
msgstr "Esto resultará en la eliminación permanente de todos los datos asociados con este espacio de trabajo."
|
||||
@@ -1794,11 +1660,7 @@ msgstr "Alternar menú"
|
||||
msgid "Track all card changes with detailed activity history."
|
||||
msgstr "Rastrea todos los cambios en las tarjetas con un historial de actividad detallado."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:73
|
||||
msgid "Trello"
|
||||
msgstr "Trello"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:53
|
||||
#: src/views/settings/index.tsx:119
|
||||
msgid "Trello disconnected"
|
||||
msgstr "Trello desconectado"
|
||||
|
||||
@@ -1883,16 +1745,16 @@ msgstr "No se puede actualizar el elemento de la lista de verificación"
|
||||
msgid "Unable to update comment"
|
||||
msgstr "No se puede actualizar el comentario"
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:71
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
msgid "Unable to update labels"
|
||||
msgstr "No se pueden actualizar las etiquetas"
|
||||
|
||||
#: src/views/board/index.tsx:133
|
||||
#: src/views/card/components/ListSelector.tsx:51
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
msgid "Unable to update list"
|
||||
msgstr "No se puede actualizar la lista"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:78
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
msgid "Unable to update members"
|
||||
msgstr "No se pueden actualizar los miembros"
|
||||
|
||||
@@ -1935,9 +1797,9 @@ msgid "Unlimited members"
|
||||
msgstr "Miembros ilimitados"
|
||||
|
||||
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:79
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:90
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:82
|
||||
#: src/views/settings/components/UpdateWorkspaceUrlForm.tsx:154
|
||||
msgid "Update"
|
||||
msgstr "Actualizar"
|
||||
@@ -1968,7 +1830,7 @@ msgid "Upgrade"
|
||||
msgstr "Actualizar"
|
||||
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
|
||||
#: src/views/settings/WorkspaceSettings.tsx:89
|
||||
#: src/views/settings/index.tsx:216
|
||||
msgid "Upgrade to Pro"
|
||||
msgstr "Actualizar a Pro"
|
||||
|
||||
@@ -1976,7 +1838,7 @@ msgstr "Actualizar a Pro"
|
||||
msgid "Upgrade to Pro ($29/month)"
|
||||
msgstr "Actualizar a Pro ($29/mes)"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:341
|
||||
#: src/views/members/components/InviteMemberForm.tsx:224
|
||||
msgid "Upgrade to Team Plan"
|
||||
msgstr "Actualizar al Plan de Equipo"
|
||||
|
||||
@@ -2008,7 +1870,7 @@ msgstr "Usar plantilla"
|
||||
msgid "User"
|
||||
msgstr "Usuario"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:96
|
||||
#: src/views/members/components/InviteMemberForm.tsx:72
|
||||
msgid "User is already a member of this workspace"
|
||||
msgstr "El usuario ya es miembro de este espacio de trabajo"
|
||||
|
||||
@@ -2016,11 +1878,11 @@ msgstr "El usuario ya es miembro de este espacio de trabajo"
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:25
|
||||
#: src/views/settings/index.tsx:299
|
||||
msgid "View and manage your API keys."
|
||||
msgstr "Ver y gestionar tus claves API."
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:238
|
||||
msgid "View and manage your billing and subscription."
|
||||
msgstr "Ver y gestionar tu facturación y suscripción."
|
||||
|
||||
@@ -2052,7 +1914,7 @@ msgstr "Estamos usando la <0>licencia AGPL-3.0</0>."
|
||||
msgid "We're just getting started. "
|
||||
msgstr "Apenas estamos comenzando. "
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
msgid "Welcome back"
|
||||
msgstr "Bienvenido de nuevo"
|
||||
|
||||
@@ -2072,7 +1934,6 @@ msgstr "Cuando Trello se lanzó en 2011, impresionó a todos con su simplicidad
|
||||
msgid "Why make an open source Trello?"
|
||||
msgstr "¿Por qué crear un Trello de código abierto?"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:39
|
||||
#: src/views/board/index.tsx:331
|
||||
msgid "Workspace"
|
||||
msgstr "Espacio de trabajo"
|
||||
@@ -2085,7 +1946,7 @@ msgstr "Espacio de trabajo creado con éxito. Puedes actualizar más tarde en co
|
||||
msgid "Workspace deleted"
|
||||
msgstr "Espacio de trabajo eliminado"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:75
|
||||
#: src/views/settings/index.tsx:202
|
||||
msgid "Workspace description"
|
||||
msgstr "Descripción del espacio de trabajo"
|
||||
|
||||
@@ -2107,7 +1968,7 @@ msgid "Workspace members"
|
||||
msgstr "Miembros del espacio de trabajo"
|
||||
|
||||
#: src/components/NewWorkspaceForm.tsx:259
|
||||
#: src/views/settings/WorkspaceSettings.tsx:58
|
||||
#: src/views/settings/index.tsx:183
|
||||
msgid "Workspace name"
|
||||
msgstr "Nombre del espacio de trabajo"
|
||||
|
||||
@@ -2131,7 +1992,7 @@ msgstr "Nombre del espacio de trabajo actualizado"
|
||||
msgid "Workspace slug updated"
|
||||
msgstr "Slug del espacio de trabajo actualizado"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:66
|
||||
#: src/views/settings/index.tsx:192
|
||||
msgid "Workspace URL"
|
||||
msgstr "URL del espacio de trabajo"
|
||||
|
||||
@@ -2151,7 +2012,7 @@ msgstr "Anual"
|
||||
msgid "Yes, we offer an forever free plan for individual use. No restrictions, no paywalls, no limits."
|
||||
msgstr "Sí, ofrecemos un plan gratuito para siempre para uso individual. Sin restricciones, sin muros de pago, sin límites."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:73
|
||||
#: src/views/settings/index.tsx:331
|
||||
msgid "You are about to change your password."
|
||||
msgstr "Estás a punto de cambiar tu contraseña."
|
||||
|
||||
@@ -2167,26 +2028,18 @@ msgstr "Puedes invitar a miembros del equipo haciendo clic en el botón \"Invita
|
||||
msgid "You can self-host by following the instructions in our <0>repo</0>."
|
||||
msgstr "Puedes autoalojar siguiendo las instrucciones en nuestro <0>repositorio</0>."
|
||||
|
||||
#: src/components/AuthForm.tsx:225
|
||||
#: src/components/AuthForm.tsx:221
|
||||
msgid "You have been logged in successfully."
|
||||
msgstr "Has iniciado sesión correctamente."
|
||||
|
||||
#: src/components/AuthForm.tsx:208
|
||||
#: src/components/AuthForm.tsx:204
|
||||
msgid "You have been signed up successfully."
|
||||
msgstr "Te has registrado correctamente."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:305
|
||||
#: src/views/members/components/InviteMemberForm.tsx:186
|
||||
msgid "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
|
||||
msgstr "Tienes plazas ilimitadas con tu Plan Pro. ¡No hay cargos adicionales por nuevos miembros!"
|
||||
|
||||
#: src/views/invite/index.tsx:134
|
||||
msgid "You've been invited to join a workspace on kan.bn."
|
||||
msgstr "Has sido invitado a unirte a un espacio de trabajo en kan.bn."
|
||||
|
||||
#: src/views/invite/index.tsx:135
|
||||
msgid "You've been invited to join a workspace."
|
||||
msgstr "Has sido invitado a unirte a un espacio de trabajo."
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:28
|
||||
msgid "Your account has been deleted."
|
||||
msgstr "Tu cuenta ha sido eliminada."
|
||||
@@ -2203,15 +2056,15 @@ msgstr "Tu nombre de visualización ha sido actualizado."
|
||||
msgid "Your password has been changed."
|
||||
msgstr "Tu contraseña ha sido cambiada."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:65
|
||||
#: src/views/settings/components/Avatar.tsx:27
|
||||
msgid "Your profile image has been updated."
|
||||
msgstr "Tu imagen de perfil ha sido actualizada."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:54
|
||||
#: src/views/settings/index.tsx:120
|
||||
msgid "Your Trello account has been disconnected."
|
||||
msgstr "Tu cuenta de Trello ha sido desconectada."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:102
|
||||
#: src/views/settings/index.tsx:281
|
||||
msgid "Your Trello account is connected."
|
||||
msgstr "Tu cuenta de Trello está conectada."
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -36,12 +36,12 @@ msgstr "{0} étiquettes"
|
||||
msgid "{boardCount, plural, one {Import board (1)} other {Import boards ({boardCount})}}"
|
||||
msgstr "{boardCount, plural, one {Importer le tableau (1)} other {Importer les tableaux ({boardCount})}}"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:146
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:92
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$10/month"
|
||||
msgstr "10 $/mois"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$8/month"
|
||||
msgstr "8 $/mois"
|
||||
|
||||
@@ -53,10 +53,6 @@ msgstr "1 utilisateur"
|
||||
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
|
||||
msgstr "Une application kanban puissante et flexible qui vous aide à organiser le travail, suivre les progrès et livrer des résultats—le tout en un seul endroit."
|
||||
|
||||
#: src/components/SettingsLayout.tsx:33
|
||||
msgid "Account"
|
||||
msgstr "Compte"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:27
|
||||
msgid "Account deleted"
|
||||
msgstr "Compte supprimé"
|
||||
@@ -95,13 +91,13 @@ msgstr "Ajouter une description... (tapez '/' pour ouvrir les commandes ou '@' p
|
||||
msgid "Add details..."
|
||||
msgstr "Ajouter des détails..."
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:109
|
||||
#: src/views/card/components/LabelSelector.tsx:114
|
||||
#: src/views/card/components/LabelSelector.tsx:110
|
||||
#: src/views/card/components/LabelSelector.tsx:118
|
||||
msgid "Add label"
|
||||
msgstr "Ajouter une étiquette"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:130
|
||||
#: src/views/members/components/InviteMemberForm.tsx:238
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
msgid "Add member"
|
||||
msgstr "Ajouter un membre"
|
||||
|
||||
@@ -136,14 +132,10 @@ msgstr "a ajouté l'élément <0>{0}</0> à la checklist"
|
||||
msgid "added label <0>{0}</0>"
|
||||
msgstr "a ajouté l'étiquette <0>{0}</0>"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:306
|
||||
#: src/views/members/components/InviteMemberForm.tsx:187
|
||||
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
|
||||
msgstr "L'ajout d'un nouveau membre coûtera {price} supplémentaires ({billingType}) par siège."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:275
|
||||
msgid "Adjust the square crop to fit your avatar."
|
||||
msgstr "Ajustez le recadrage carré pour adapter votre avatar."
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:55
|
||||
msgid "Admin roles"
|
||||
msgstr "Rôles d'administrateur"
|
||||
@@ -152,11 +144,11 @@ msgstr "Rôles d'administrateur"
|
||||
msgid "All systems operational"
|
||||
msgstr "Tous les systèmes opérationnels"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:88
|
||||
#: src/views/auth/signup/index.tsx:86
|
||||
msgid "Already have an account? <0><1>Sign in</1></0>"
|
||||
msgstr "Vous avez déjà un compte ? <0><1>Connectez-vous</1></0>"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:61
|
||||
#: src/views/settings/index.tsx:127
|
||||
msgid "An error occurred while disconnecting your Trello account."
|
||||
msgstr "Une erreur s'est produite lors de la déconnexion de votre compte Trello."
|
||||
|
||||
@@ -164,31 +156,7 @@ msgstr "Une erreur s'est produite lors de la déconnexion de votre compte Trello
|
||||
msgid "An unexpected error occurred. Please try again later."
|
||||
msgstr "Une erreur inattendue s'est produite. Veuillez réessayer plus tard."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:290
|
||||
msgid "Anyone with this link can join your workspace"
|
||||
msgstr "Toute personne disposant de ce lien peut rejoindre votre espace de travail"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:51
|
||||
msgid "API"
|
||||
msgstr "API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:91
|
||||
msgid "API key created"
|
||||
msgstr "Clé API créée"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:161
|
||||
msgid "API key name"
|
||||
msgstr "Nom de la clé API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:25
|
||||
msgid "API key name cannot exceed 30 characters"
|
||||
msgstr "Le nom de la clé API ne peut pas dépasser 30 caractères"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:24
|
||||
msgid "API key name is required"
|
||||
msgstr "Le nom de la clé API est requis"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:22
|
||||
#: src/views/settings/index.tsx:296
|
||||
msgid "API keys"
|
||||
msgstr "Clés API"
|
||||
|
||||
@@ -250,21 +218,20 @@ msgstr "Backlog"
|
||||
msgid "Basic Kanban"
|
||||
msgstr "Kanban basique"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed annually"
|
||||
msgstr "facturation annuelle"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed monthly"
|
||||
msgstr "facturation mensuelle"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:44
|
||||
#: src/views/boards/components/TemplateBoards.tsx:55
|
||||
#: src/views/settings/BillingSettings.tsx:39
|
||||
#: src/views/settings/index.tsx:235
|
||||
msgid "Billing"
|
||||
msgstr "Facturation"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:49
|
||||
#: src/views/settings/index.tsx:245
|
||||
msgid "Billing portal"
|
||||
msgstr "Portail de facturation"
|
||||
|
||||
@@ -319,12 +286,12 @@ msgid "Board visibility updated"
|
||||
msgstr "Visibilité du tableau mise à jour"
|
||||
|
||||
#: src/components/SideNavigation.tsx:68
|
||||
#: src/views/boards/index.tsx:32
|
||||
#: src/views/boards/index.tsx:27
|
||||
msgid "Boards"
|
||||
msgstr "Tableaux"
|
||||
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/boards/index.tsx:28
|
||||
#: src/views/boards/index.tsx:23
|
||||
msgid "Boards | {0}"
|
||||
msgstr "Tableaux | {0}"
|
||||
|
||||
@@ -346,7 +313,6 @@ msgstr "Rapport de bug"
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:63
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:76
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:55
|
||||
#: src/views/settings/components/Avatar.tsx:306
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
|
||||
@@ -362,19 +328,19 @@ msgstr "Carte introuvable"
|
||||
msgid "Card title"
|
||||
msgstr "Titre de la carte"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:70
|
||||
#: src/views/settings/AccountSettings.tsx:80
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:109
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:178
|
||||
#: src/views/settings/index.tsx:328
|
||||
#: src/views/settings/index.tsx:338
|
||||
msgid "Change Password"
|
||||
msgstr "Modifier le mot de passe"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:45
|
||||
msgid "Change your language preferences."
|
||||
msgstr "Modifiez vos préférences linguistiques."
|
||||
#: src/views/settings/index.tsx:227
|
||||
msgid "Change the language of the app."
|
||||
msgstr "Changer la langue de l'application."
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
msgid "Check your inbox"
|
||||
msgstr "Vérifiez votre boîte de réception"
|
||||
|
||||
@@ -386,15 +352,11 @@ msgstr "Nom de la checklist"
|
||||
msgid "Clear filters"
|
||||
msgstr "Effacer les filtres"
|
||||
|
||||
#: src/views/auth/login/index.tsx:48
|
||||
#: src/views/auth/signup/index.tsx:74
|
||||
#: src/views/auth/login/index.tsx:46
|
||||
#: src/views/auth/signup/index.tsx:72
|
||||
msgid "Click on the link we've sent to {magicLinkRecipient} to sign in."
|
||||
msgstr "Cliquez sur le lien que nous avons envoyé à {magicLinkRecipient} pour vous connecter."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:136
|
||||
msgid "Close"
|
||||
msgstr "Fermer"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:22
|
||||
msgid "Code Review"
|
||||
msgstr "Revue de code"
|
||||
@@ -439,7 +401,7 @@ msgid "Confirm your new password"
|
||||
msgstr "Confirmez votre nouveau mot de passe"
|
||||
|
||||
#: src/views/boards/components/ImportBoardsForm.tsx:157
|
||||
#: src/views/settings/IntegrationsSettings.tsx:93
|
||||
#: src/views/settings/index.tsx:272
|
||||
msgid "Connect Trello"
|
||||
msgstr "Connecter Trello"
|
||||
|
||||
@@ -447,7 +409,7 @@ msgstr "Connecter Trello"
|
||||
msgid "Connect your favorite tools to streamline your workflow."
|
||||
msgstr "Connectez vos outils favoris pour simplifier votre flux de travail."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:80
|
||||
#: src/views/settings/index.tsx:259
|
||||
msgid "Connect your Trello account to import boards."
|
||||
msgstr "Connectez votre compte Trello pour importer des tableaux."
|
||||
|
||||
@@ -463,12 +425,12 @@ msgstr "Contactez-nous"
|
||||
msgid "Content Creation"
|
||||
msgstr "Création de contenu"
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Continue with "
|
||||
msgstr "Continuer avec "
|
||||
|
||||
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
|
||||
#: src/components/AuthForm.tsx:301
|
||||
#: src/components/AuthForm.tsx:297
|
||||
msgid "Continue with {0}"
|
||||
msgstr "Continuer avec {0}"
|
||||
|
||||
@@ -482,10 +444,6 @@ msgstr "Contrôlez qui peut voir et modifier vos tableaux."
|
||||
msgid "Create another"
|
||||
msgstr "Créer un autre"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:175
|
||||
msgid "Create API key"
|
||||
msgstr "Créer une clé API"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:128
|
||||
msgid "Create board"
|
||||
msgstr "Créer un tableau"
|
||||
@@ -510,12 +468,12 @@ msgstr "Créer une liste"
|
||||
msgid "Create new board"
|
||||
msgstr "Créer un nouveau tableau"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:30
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:54
|
||||
msgid "Create new key"
|
||||
msgstr "Créer une nouvelle clé"
|
||||
|
||||
#: src/views/board/components/NewCardForm.tsx:394
|
||||
#: src/views/card/components/LabelSelector.tsx:97
|
||||
#: src/views/card/components/LabelSelector.tsx:98
|
||||
msgid "Create new label"
|
||||
msgstr "Créer une nouvelle étiquette"
|
||||
|
||||
@@ -536,10 +494,6 @@ msgstr "a créé la carte"
|
||||
msgid "Critical"
|
||||
msgstr "Critique"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:272
|
||||
msgid "Crop your avatar"
|
||||
msgstr "Recadrez votre avatar"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:19
|
||||
msgid "Current password is required"
|
||||
msgstr "Le mot de passe actuel est requis"
|
||||
@@ -573,9 +527,9 @@ msgstr "Sombre"
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:52
|
||||
#: src/views/settings/AccountSettings.tsx:62
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:96
|
||||
#: src/views/settings/index.tsx:346
|
||||
#: src/views/settings/index.tsx:356
|
||||
msgid "Delete account"
|
||||
msgstr "Supprimer le compte"
|
||||
|
||||
@@ -596,8 +550,8 @@ msgid "Delete list"
|
||||
msgstr "Supprimer la liste"
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
|
||||
#: src/views/settings/WorkspaceSettings.tsx:96
|
||||
#: src/views/settings/WorkspaceSettings.tsx:107
|
||||
#: src/views/settings/index.tsx:309
|
||||
#: src/views/settings/index.tsx:320
|
||||
msgid "Delete workspace"
|
||||
msgstr "Supprimer l'espace de travail"
|
||||
|
||||
@@ -623,7 +577,7 @@ msgstr "a supprimé l'élément <0>{0}</0> de la checklist"
|
||||
msgid "Design"
|
||||
msgstr "Design"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:108
|
||||
#: src/views/settings/index.tsx:287
|
||||
msgid "Disconnect Trello"
|
||||
msgstr "Déconnecter Trello"
|
||||
|
||||
@@ -631,7 +585,7 @@ msgstr "Déconnecter Trello"
|
||||
msgid "Discuss and collaborate on cards."
|
||||
msgstr "Discutez et collaborez sur les cartes."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:35
|
||||
#: src/views/settings/index.tsx:176
|
||||
msgid "Display name"
|
||||
msgstr "Nom d'affichage"
|
||||
|
||||
@@ -665,7 +619,7 @@ msgstr "Documentation"
|
||||
msgid "Documentation"
|
||||
msgstr "Documentation"
|
||||
|
||||
#: src/views/auth/login/index.tsx:63
|
||||
#: src/views/auth/login/index.tsx:61
|
||||
msgid "Don't have an account? <0><1>Sign up</1></0>"
|
||||
msgstr "Vous n'avez pas de compte ? <0><1>Inscrivez-vous</1></0>"
|
||||
|
||||
@@ -697,11 +651,11 @@ msgstr "Modifier l'URL de l'espace de travail"
|
||||
msgid "Editing"
|
||||
msgstr "Édition"
|
||||
|
||||
#: src/components/AuthForm.tsx:372
|
||||
#: src/components/AuthForm.tsx:368
|
||||
msgid "email"
|
||||
msgstr "e-mail"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:252
|
||||
#: src/views/members/components/InviteMemberForm.tsx:161
|
||||
msgid "Email"
|
||||
msgstr "E-mail"
|
||||
|
||||
@@ -717,11 +671,11 @@ msgstr "Saisissez votre mot de passe actuel"
|
||||
msgid "Enter your current password and choose a new secure password."
|
||||
msgstr "Saisissez votre mot de passe actuel et choisissez un nouveau mot de passe sécurisé."
|
||||
|
||||
#: src/components/AuthForm.tsx:337
|
||||
#: src/components/AuthForm.tsx:333
|
||||
msgid "Enter your email address"
|
||||
msgstr "Saisissez votre adresse e-mail"
|
||||
|
||||
#: src/components/AuthForm.tsx:325
|
||||
#: src/components/AuthForm.tsx:321
|
||||
msgid "Enter your name"
|
||||
msgstr "Saisissez votre nom"
|
||||
|
||||
@@ -729,26 +683,14 @@ msgstr "Saisissez votre nom"
|
||||
msgid "Enter your new password"
|
||||
msgstr "Saisissez votre nouveau mot de passe"
|
||||
|
||||
#: src/components/AuthForm.tsx:350
|
||||
#: src/components/AuthForm.tsx:346
|
||||
msgid "Enter your password"
|
||||
msgstr "Saisissez votre mot de passe"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Error"
|
||||
msgstr "Erreur"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:89
|
||||
msgid "Error Changing Password"
|
||||
msgstr "Erreur lors du changement de mot de passe"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:117
|
||||
msgid "Error creating invite link"
|
||||
msgstr "Erreur lors de la création du lien d'invitation"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:132
|
||||
msgid "Error deactivating invite link"
|
||||
msgstr "Erreur lors de la désactivation du lien d'invitation"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:39
|
||||
msgid "Error deleting account"
|
||||
msgstr "Erreur lors de la suppression du compte"
|
||||
@@ -761,12 +703,12 @@ msgstr "Erreur lors de la suppression de l'étiquette"
|
||||
msgid "Error deleting workspace"
|
||||
msgstr "Erreur lors de la suppression de l'espace de travail"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:60
|
||||
#: src/views/settings/index.tsx:126
|
||||
msgid "Error disconnecting Trello"
|
||||
msgstr "Erreur lors de la déconnexion de Trello"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:95
|
||||
#: src/views/members/components/InviteMemberForm.tsx:101
|
||||
#: src/views/members/components/InviteMemberForm.tsx:71
|
||||
#: src/views/members/components/InviteMemberForm.tsx:77
|
||||
msgid "Error inviting member"
|
||||
msgstr "Erreur lors de l'invitation du membre"
|
||||
|
||||
@@ -774,7 +716,7 @@ msgstr "Erreur lors de l'invitation du membre"
|
||||
msgid "Error updating display name"
|
||||
msgstr "Erreur lors de la mise à jour du nom d'affichage"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:77
|
||||
#: src/views/settings/components/Avatar.tsx:39
|
||||
msgid "Error updating profile image"
|
||||
msgstr "Erreur lors de la mise à jour de l'image de profil"
|
||||
|
||||
@@ -790,7 +732,7 @@ msgstr "Erreur lors de la mise à jour du nom de l'espace de travail"
|
||||
msgid "Error updating workspace URL"
|
||||
msgstr "Erreur lors de la mise à jour de l'URL de l'espace de travail"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:221
|
||||
#: src/views/members/components/InviteMemberForm.tsx:130
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
|
||||
msgid "Error upgrading subscription"
|
||||
msgstr "Erreur lors de la mise à niveau de l'abonnement"
|
||||
@@ -799,8 +741,8 @@ msgstr "Erreur lors de la mise à niveau de l'abonnement"
|
||||
msgid "Error upgrading to Pro"
|
||||
msgstr "Erreur lors de la mise à niveau vers Pro"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:91
|
||||
#: src/views/settings/components/Avatar.tsx:218
|
||||
#: src/views/settings/components/Avatar.tsx:56
|
||||
#: src/views/settings/components/Avatar.tsx:97
|
||||
msgid "Error uploading profile image"
|
||||
msgstr "Erreur lors du téléchargement de l'image de profil"
|
||||
|
||||
@@ -816,16 +758,8 @@ msgstr "Tout ce dont vous avez besoin, gratuit pour toujours. Tableaux illimité
|
||||
msgid "Execution"
|
||||
msgstr "Exécution"
|
||||
|
||||
#: src/views/invite/index.tsx:41
|
||||
msgid "Failed to accept invitation. Please try again later, or contact customer support."
|
||||
msgstr "Échec de l'acceptation de l'invitation. Veuillez réessayer plus tard ou contacter le service client."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:197
|
||||
msgid "Failed to copy invite link"
|
||||
msgstr "Échec de la copie du lien d'invitation"
|
||||
|
||||
#. placeholder {0}: provider.at(0)?.toUpperCase() + provider.slice(1)
|
||||
#: src/components/AuthForm.tsx:273
|
||||
#: src/components/AuthForm.tsx:269
|
||||
msgid "Failed to login with {0}. Please try again."
|
||||
msgstr "Échec de connexion avec {0}. Veuillez réessayer."
|
||||
|
||||
@@ -873,7 +807,7 @@ msgstr "Pour une durabilité à long terme, nous reconnaissons que tous les bons
|
||||
msgid "Free"
|
||||
msgstr "Gratuit"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:312
|
||||
#: src/views/members/components/InviteMemberForm.tsx:193
|
||||
#: src/views/members/index.tsx:208
|
||||
msgid "Free Plan"
|
||||
msgstr "Forfait gratuit"
|
||||
@@ -890,7 +824,7 @@ msgstr "Temps plein"
|
||||
msgid "Fun"
|
||||
msgstr "Amusant"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
#: src/views/home/components/Cta.tsx:61
|
||||
#: src/views/home/components/Header.tsx:102
|
||||
#: src/views/home/components/Header.tsx:141
|
||||
@@ -930,13 +864,8 @@ msgstr "Premiers pas"
|
||||
msgid "GitHub"
|
||||
msgstr "GitHub"
|
||||
|
||||
#: src/views/invite/index.tsx:113
|
||||
msgid "Go Home"
|
||||
msgstr "Aller à l'accueil"
|
||||
|
||||
#: src/views/home/components/Header.tsx:96
|
||||
#: src/views/home/components/Header.tsx:133
|
||||
#: src/views/invite/index.tsx:144
|
||||
msgid "Go to app"
|
||||
msgstr "Accéder à l'application"
|
||||
|
||||
@@ -988,7 +917,7 @@ msgstr "Idées"
|
||||
msgid "Ideas to improve this page..."
|
||||
msgstr "Idées pour améliorer cette page..."
|
||||
|
||||
#: src/views/boards/index.tsx:43
|
||||
#: src/views/boards/index.tsx:38
|
||||
msgid "Import"
|
||||
msgstr "Importer"
|
||||
|
||||
@@ -1026,7 +955,6 @@ msgstr "En cours"
|
||||
msgid "Individuals"
|
||||
msgstr "Particuliers"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:57
|
||||
#: src/views/home/components/Features.tsx:114
|
||||
msgid "Integrations"
|
||||
msgstr "Intégrations"
|
||||
@@ -1035,44 +963,27 @@ msgstr "Intégrations"
|
||||
msgid "Interviewing"
|
||||
msgstr "Entretien"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:49
|
||||
#: src/views/members/components/InviteMemberForm.tsx:40
|
||||
msgid "Invalid email address"
|
||||
msgstr "Adresse e-mail invalide"
|
||||
|
||||
#: src/views/invite/index.tsx:105
|
||||
msgid "Invalid invitation"
|
||||
msgstr "Invitation non valide"
|
||||
|
||||
#: src/views/members/index.tsx:221
|
||||
msgid "Invite"
|
||||
msgstr "Inviter"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:190
|
||||
msgid "Invite link copied"
|
||||
msgstr "Lien d'invitation copié"
|
||||
#: src/views/members/components/InviteMemberForm.tsx:208
|
||||
msgid "Invite another"
|
||||
msgstr "Inviter un autre"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:191
|
||||
msgid "Invite link copied to clipboard"
|
||||
msgstr "Lien d'invitation copié dans le presse-papiers"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:111
|
||||
#: src/views/members/components/InviteMemberForm.tsx:350
|
||||
#: src/views/card/components/MemberSelector.tsx:112
|
||||
#: src/views/members/components/InviteMemberForm.tsx:233
|
||||
msgid "Invite member"
|
||||
msgstr "Inviter un membre"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:315
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
|
||||
msgstr "L'invitation de membres nécessite un forfait d'équipe. Vous serez redirigé pour mettre à niveau votre espace de travail."
|
||||
|
||||
#: src/views/invite/index.tsx:79
|
||||
#: src/views/invite/index.tsx:129
|
||||
msgid "Join workspace"
|
||||
msgstr "Rejoindre l'espace de travail"
|
||||
|
||||
#: src/views/invite/index.tsx:91
|
||||
msgid "Join workspace | kan.bn"
|
||||
msgstr "Rejoindre l'espace de travail | kan.bn"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:69
|
||||
msgid "Junior"
|
||||
msgstr "Junior"
|
||||
@@ -1100,7 +1011,7 @@ msgstr "Étiquettes"
|
||||
msgid "Labels & Filters"
|
||||
msgstr "Étiquettes & filtres"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:224
|
||||
msgid "Language"
|
||||
msgstr "Langue"
|
||||
|
||||
@@ -1145,7 +1056,7 @@ msgstr "Liste"
|
||||
msgid "List name"
|
||||
msgstr "Nom de la liste"
|
||||
|
||||
#: src/views/auth/login/index.tsx:33
|
||||
#: src/views/auth/login/index.tsx:31
|
||||
msgid "Login | kan.bn"
|
||||
msgstr "Connexion | kan.bn"
|
||||
|
||||
@@ -1161,7 +1072,7 @@ msgstr "Long terme"
|
||||
msgid "Low Priority"
|
||||
msgstr "Priorité basse"
|
||||
|
||||
#: src/components/AuthForm.tsx:373
|
||||
#: src/components/AuthForm.tsx:369
|
||||
msgid "magic link"
|
||||
msgstr "lien magique"
|
||||
|
||||
@@ -1195,7 +1106,7 @@ msgstr "Membres | {0}"
|
||||
msgid "Monthly"
|
||||
msgstr "Mensuel"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
#: src/views/members/components/InviteMemberForm.tsx:93
|
||||
msgid "monthly billing"
|
||||
msgstr "facturation mensuelle"
|
||||
|
||||
@@ -1218,14 +1129,10 @@ msgstr "Nom"
|
||||
msgid "Need help?"
|
||||
msgstr "Besoin d'aide ?"
|
||||
|
||||
#: src/views/boards/index.tsx:53
|
||||
#: src/views/boards/index.tsx:48
|
||||
msgid "New"
|
||||
msgstr "Nouveau"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:147
|
||||
msgid "New API key"
|
||||
msgstr "Nouvelle clé API"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:85
|
||||
msgid "New board"
|
||||
msgstr "Nouveau tableau"
|
||||
@@ -1299,15 +1206,15 @@ msgstr "Offre"
|
||||
msgid "Onboarding"
|
||||
msgstr "Intégration"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:55
|
||||
#: src/views/settings/index.tsx:349
|
||||
msgid "Once you delete your account, there is no going back. This action cannot be undone."
|
||||
msgstr "Une fois que vous supprimez votre compte, il n'y a pas de retour possible. Cette action ne peut pas être annulée."
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:99
|
||||
#: src/views/settings/index.tsx:312
|
||||
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
|
||||
msgstr "Une fois que vous supprimez votre espace de travail, il n'y a pas de retour possible. Cette action ne peut pas être annulée."
|
||||
|
||||
#: src/components/AuthForm.tsx:315
|
||||
#: src/components/AuthForm.tsx:311
|
||||
msgid "or"
|
||||
msgstr "ou"
|
||||
|
||||
@@ -1335,10 +1242,6 @@ msgstr "Le mot de passe doit comporter au moins 8 caractères"
|
||||
msgid "Passwords do not match"
|
||||
msgstr "Les mots de passe ne correspondent pas"
|
||||
|
||||
#: src/views/members/index.tsx:134
|
||||
msgid "Paused"
|
||||
msgstr "En pause"
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:102
|
||||
msgid "Payment frequency"
|
||||
msgstr "Fréquence de paiement"
|
||||
@@ -1364,19 +1267,19 @@ msgstr "Planification"
|
||||
msgid "Please confirm your new password"
|
||||
msgstr "Veuillez confirmer votre nouveau mot de passe"
|
||||
|
||||
#: src/components/AuthForm.tsx:341
|
||||
#: src/components/AuthForm.tsx:337
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Veuillez saisir une adresse e-mail valide"
|
||||
|
||||
#: src/components/AuthForm.tsx:329
|
||||
#: src/components/AuthForm.tsx:325
|
||||
msgid "Please enter a valid name"
|
||||
msgstr "Veuillez saisir un nom valide"
|
||||
|
||||
#: src/components/AuthForm.tsx:354
|
||||
#: src/components/AuthForm.tsx:350
|
||||
msgid "Please enter a valid password"
|
||||
msgstr "Veuillez saisir un mot de passe valide"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:92
|
||||
#: src/views/settings/components/Avatar.tsx:57
|
||||
msgid "Please select a file to upload."
|
||||
msgstr "Veuillez sélectionner un fichier à télécharger."
|
||||
|
||||
@@ -1397,18 +1300,18 @@ msgstr "Veuillez sélectionner un fichier à télécharger."
|
||||
#: src/views/card/components/DeleteCardConfirmation.tsx:52
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:37
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:45
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
#: src/views/card/components/LabelSelector.tsx:73
|
||||
#: src/views/card/components/ListSelector.tsx:53
|
||||
#: src/views/card/components/MemberSelector.tsx:80
|
||||
#: src/views/card/components/NewChecklistForm.tsx:70
|
||||
#: src/views/card/components/NewChecklistItemForm.tsx:89
|
||||
#: src/views/card/components/NewCommentForm.tsx:31
|
||||
#: src/views/card/index.tsx:173
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
|
||||
#: src/views/members/components/InviteMemberForm.tsx:102
|
||||
#: src/views/members/components/InviteMemberForm.tsx:222
|
||||
#: src/views/settings/components/Avatar.tsx:78
|
||||
#: src/views/settings/components/Avatar.tsx:219
|
||||
#: src/views/members/components/InviteMemberForm.tsx:78
|
||||
#: src/views/members/components/InviteMemberForm.tsx:131
|
||||
#: src/views/settings/components/Avatar.tsx:40
|
||||
#: src/views/settings/components/Avatar.tsx:98
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:46
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:55
|
||||
@@ -1419,11 +1322,6 @@ msgstr "Veuillez sélectionner un fichier à télécharger."
|
||||
msgid "Please try again later, or contact customer support."
|
||||
msgstr "Veuillez réessayer plus tard ou contacter le service client."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:118
|
||||
#: src/views/members/components/InviteMemberForm.tsx:133
|
||||
msgid "Please try again later."
|
||||
msgstr "Veuillez réessayer plus tard."
|
||||
|
||||
#: src/views/home/components/Footer.tsx:50
|
||||
#: src/views/home/components/Header.tsx:15
|
||||
#: src/views/home/components/Pricing.tsx:85
|
||||
@@ -1447,15 +1345,15 @@ msgstr "Privé"
|
||||
msgid "Pro Plan"
|
||||
msgstr "Plan Pro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
msgid "Pro Plan ∞"
|
||||
msgstr "Plan Pro ∞"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:64
|
||||
#: src/views/settings/components/Avatar.tsx:26
|
||||
msgid "Profile image updated"
|
||||
msgstr "Image de profil mise à jour"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:29
|
||||
#: src/views/settings/index.tsx:171
|
||||
msgid "Profile picture"
|
||||
msgstr "Photo de profil"
|
||||
|
||||
@@ -1538,6 +1436,10 @@ msgstr "Ressources"
|
||||
msgid "Review"
|
||||
msgstr "Révision"
|
||||
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:49
|
||||
msgid "Revoke"
|
||||
msgstr "Révoquer"
|
||||
|
||||
#: src/views/home/components/Footer.tsx:36
|
||||
#: src/views/home/components/Header.tsx:13
|
||||
msgid "Roadmap"
|
||||
@@ -1552,7 +1454,6 @@ msgid "Run on your own infrastructure"
|
||||
msgstr "Exécutez sur votre propre infrastructure"
|
||||
|
||||
#: src/views/card/components/Comment.tsx:165
|
||||
#: src/views/settings/components/Avatar.tsx:309
|
||||
msgid "Save"
|
||||
msgstr "Enregistrer"
|
||||
|
||||
@@ -1592,62 +1493,35 @@ msgstr "Envoyer des commentaires"
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:82
|
||||
#: src/components/SideNavigation.tsx:78
|
||||
#: src/views/settings/index.tsx:165
|
||||
msgid "Settings"
|
||||
msgstr "Paramètres"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:25
|
||||
msgid "Settings | Account"
|
||||
msgstr "Paramètres | Compte"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:18
|
||||
msgid "Settings | API"
|
||||
msgstr "Paramètres | API"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:35
|
||||
msgid "Settings | Billing"
|
||||
msgstr "Paramètres | Facturation"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:69
|
||||
msgid "Settings | Integrations"
|
||||
msgstr "Paramètres | Intégrations"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:54
|
||||
msgid "Settings | Workspace"
|
||||
msgstr "Paramètres | Espace de travail"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:327
|
||||
msgid "Share invite link"
|
||||
msgstr "Partager le lien d'invitation"
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/settings/index.tsx:161
|
||||
msgid "Settings | {0}"
|
||||
msgstr "Paramètres | {0}"
|
||||
|
||||
#: src/views/home/components/Header.tsx:100
|
||||
#: src/views/home/components/Header.tsx:138
|
||||
msgid "Sign in"
|
||||
msgstr "Se connecter"
|
||||
|
||||
#: src/views/invite/index.tsx:154
|
||||
msgid "Sign In"
|
||||
msgstr "Se connecter"
|
||||
|
||||
#: src/views/invite/index.tsx:162
|
||||
msgid "Sign Up"
|
||||
msgstr "S'inscrire"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:34
|
||||
#: src/views/auth/signup/index.tsx:59
|
||||
#: src/views/auth/signup/index.tsx:32
|
||||
#: src/views/auth/signup/index.tsx:57
|
||||
msgid "Sign up | kan.bn"
|
||||
msgstr "Inscription | kan.bn"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:44
|
||||
#: src/views/auth/signup/index.tsx:42
|
||||
msgid "Sign up disabled"
|
||||
msgstr "Inscription désactivée"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:47
|
||||
#: src/views/auth/signup/index.tsx:45
|
||||
msgid "Sign up is currently disabled. Please try again later."
|
||||
msgstr "L'inscription est actuellement désactivée. Veuillez réessayer plus tard."
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Sign up with "
|
||||
msgstr "S'inscrire avec "
|
||||
|
||||
@@ -1671,8 +1545,8 @@ msgstr "Développement logiciel"
|
||||
msgid "Star on Github"
|
||||
msgstr "Étoile sur Github"
|
||||
|
||||
#: src/components/AuthForm.tsx:207
|
||||
#: src/components/AuthForm.tsx:224
|
||||
#: src/components/AuthForm.tsx:203
|
||||
#: src/components/AuthForm.tsx:220
|
||||
msgid "Success"
|
||||
msgstr "Succès"
|
||||
|
||||
@@ -1692,7 +1566,7 @@ msgstr "Soutenir le développement du projet"
|
||||
msgid "System"
|
||||
msgstr "Système"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
#: src/views/members/index.tsx:207
|
||||
msgid "Team Plan"
|
||||
msgstr "Forfait d'équipe"
|
||||
@@ -1745,10 +1619,6 @@ msgstr "Ils ne pourront plus accéder à cet espace de travail."
|
||||
msgid "This action can't be undone."
|
||||
msgstr "Cette action ne peut pas être annulée."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:129
|
||||
msgid "This API key will only be shown once. Please save it in a secure location."
|
||||
msgstr "Cette clé API ne sera affichée qu'une seule fois. Veuillez la sauvegarder dans un emplacement sécurisé."
|
||||
|
||||
#: src/views/public/board/index.tsx:151
|
||||
msgid "This board is private or does not exist"
|
||||
msgstr "Ce tableau est privé ou n'existe pas"
|
||||
@@ -1757,10 +1627,6 @@ msgstr "Ce tableau est privé ou n'existe pas"
|
||||
msgid "This board URL has already been taken"
|
||||
msgstr "Cette URL de tableau est déjà utilisée"
|
||||
|
||||
#: src/views/invite/index.tsx:108
|
||||
msgid "This invitation link is invalid or has expired."
|
||||
msgstr "Ce lien d'invitation est invalide ou a expiré."
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:70
|
||||
msgid "This will result in the permanent deletion of all data associated with this workspace."
|
||||
msgstr "Cela entraînera la suppression définitive de toutes les données associées à cet espace de travail."
|
||||
@@ -1794,11 +1660,7 @@ msgstr "Basculer le menu"
|
||||
msgid "Track all card changes with detailed activity history."
|
||||
msgstr "Suivez tous les changements de cartes avec un historique d'activité détaillé."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:73
|
||||
msgid "Trello"
|
||||
msgstr "Trello"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:53
|
||||
#: src/views/settings/index.tsx:119
|
||||
msgid "Trello disconnected"
|
||||
msgstr "Trello déconnecté"
|
||||
|
||||
@@ -1883,16 +1745,16 @@ msgstr "Impossible de mettre à jour l'élément de la liste de contrôle"
|
||||
msgid "Unable to update comment"
|
||||
msgstr "Impossible de mettre à jour le commentaire"
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:71
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
msgid "Unable to update labels"
|
||||
msgstr "Impossible de mettre à jour les étiquettes"
|
||||
|
||||
#: src/views/board/index.tsx:133
|
||||
#: src/views/card/components/ListSelector.tsx:51
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
msgid "Unable to update list"
|
||||
msgstr "Impossible de mettre à jour la liste"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:78
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
msgid "Unable to update members"
|
||||
msgstr "Impossible de mettre à jour les membres"
|
||||
|
||||
@@ -1935,9 +1797,9 @@ msgid "Unlimited members"
|
||||
msgstr "Membres illimités"
|
||||
|
||||
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:79
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:90
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:82
|
||||
#: src/views/settings/components/UpdateWorkspaceUrlForm.tsx:154
|
||||
msgid "Update"
|
||||
msgstr "Mettre à jour"
|
||||
@@ -1968,7 +1830,7 @@ msgid "Upgrade"
|
||||
msgstr "Mettre à niveau"
|
||||
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
|
||||
#: src/views/settings/WorkspaceSettings.tsx:89
|
||||
#: src/views/settings/index.tsx:216
|
||||
msgid "Upgrade to Pro"
|
||||
msgstr "Passer à Pro"
|
||||
|
||||
@@ -1976,7 +1838,7 @@ msgstr "Passer à Pro"
|
||||
msgid "Upgrade to Pro ($29/month)"
|
||||
msgstr "Passer à Pro (29 $/mois)"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:341
|
||||
#: src/views/members/components/InviteMemberForm.tsx:224
|
||||
msgid "Upgrade to Team Plan"
|
||||
msgstr "Passer au forfait d'équipe"
|
||||
|
||||
@@ -2008,7 +1870,7 @@ msgstr "Utiliser le modèle"
|
||||
msgid "User"
|
||||
msgstr "Utilisateur"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:96
|
||||
#: src/views/members/components/InviteMemberForm.tsx:72
|
||||
msgid "User is already a member of this workspace"
|
||||
msgstr "L'utilisateur est déjà membre de cet espace de travail"
|
||||
|
||||
@@ -2016,11 +1878,11 @@ msgstr "L'utilisateur est déjà membre de cet espace de travail"
|
||||
msgid "Video"
|
||||
msgstr "Vidéo"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:25
|
||||
#: src/views/settings/index.tsx:299
|
||||
msgid "View and manage your API keys."
|
||||
msgstr "Consultez et gérez vos clés API."
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:238
|
||||
msgid "View and manage your billing and subscription."
|
||||
msgstr "Consultez et gérez votre facturation et votre abonnement."
|
||||
|
||||
@@ -2052,7 +1914,7 @@ msgstr "Nous utilisons la <0>licence AGPL-3.0</0>."
|
||||
msgid "We're just getting started. "
|
||||
msgstr "Nous ne faisons que commencer. "
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
msgid "Welcome back"
|
||||
msgstr "Bienvenue à nouveau"
|
||||
|
||||
@@ -2072,7 +1934,6 @@ msgstr "Quand Trello a été lancé en 2011, il a impressionné tout le monde pa
|
||||
msgid "Why make an open source Trello?"
|
||||
msgstr "Pourquoi créer un Trello open source ?"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:39
|
||||
#: src/views/board/index.tsx:331
|
||||
msgid "Workspace"
|
||||
msgstr "Espace de travail"
|
||||
@@ -2085,7 +1946,7 @@ msgstr "Espace de travail créé avec succès. Vous pourrez effectuer la mise à
|
||||
msgid "Workspace deleted"
|
||||
msgstr "Espace de travail supprimé"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:75
|
||||
#: src/views/settings/index.tsx:202
|
||||
msgid "Workspace description"
|
||||
msgstr "Description de l'espace de travail"
|
||||
|
||||
@@ -2107,7 +1968,7 @@ msgid "Workspace members"
|
||||
msgstr "Membres de l'espace de travail"
|
||||
|
||||
#: src/components/NewWorkspaceForm.tsx:259
|
||||
#: src/views/settings/WorkspaceSettings.tsx:58
|
||||
#: src/views/settings/index.tsx:183
|
||||
msgid "Workspace name"
|
||||
msgstr "Nom de l'espace de travail"
|
||||
|
||||
@@ -2131,7 +1992,7 @@ msgstr "Nom de l'espace de travail mis à jour"
|
||||
msgid "Workspace slug updated"
|
||||
msgstr "Slug de l'espace de travail mis à jour"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:66
|
||||
#: src/views/settings/index.tsx:192
|
||||
msgid "Workspace URL"
|
||||
msgstr "URL de l'espace de travail"
|
||||
|
||||
@@ -2151,7 +2012,7 @@ msgstr "Annuel"
|
||||
msgid "Yes, we offer an forever free plan for individual use. No restrictions, no paywalls, no limits."
|
||||
msgstr "Oui, nous proposons un plan gratuit à vie pour un usage individuel. Aucune restriction, aucun paywall, aucune limite."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:73
|
||||
#: src/views/settings/index.tsx:331
|
||||
msgid "You are about to change your password."
|
||||
msgstr "Vous êtes sur le point de modifier votre mot de passe."
|
||||
|
||||
@@ -2167,26 +2028,18 @@ msgstr "Vous pouvez inviter des membres de l'équipe en cliquant sur le bouton \
|
||||
msgid "You can self-host by following the instructions in our <0>repo</0>."
|
||||
msgstr "Vous pouvez auto-héberger en suivant les instructions dans notre <0>dépôt</0>."
|
||||
|
||||
#: src/components/AuthForm.tsx:225
|
||||
#: src/components/AuthForm.tsx:221
|
||||
msgid "You have been logged in successfully."
|
||||
msgstr "Vous vous êtes connecté avec succès."
|
||||
|
||||
#: src/components/AuthForm.tsx:208
|
||||
#: src/components/AuthForm.tsx:204
|
||||
msgid "You have been signed up successfully."
|
||||
msgstr "Vous vous êtes inscrit avec succès."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:305
|
||||
#: src/views/members/components/InviteMemberForm.tsx:186
|
||||
msgid "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
|
||||
msgstr "Vous disposez de places illimitées avec votre Plan Pro. Il n'y a pas de frais supplémentaires pour les nouveaux membres !"
|
||||
|
||||
#: src/views/invite/index.tsx:134
|
||||
msgid "You've been invited to join a workspace on kan.bn."
|
||||
msgstr "Vous avez été invité à rejoindre un espace de travail sur kan.bn."
|
||||
|
||||
#: src/views/invite/index.tsx:135
|
||||
msgid "You've been invited to join a workspace."
|
||||
msgstr "Vous avez été invité à rejoindre un espace de travail."
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:28
|
||||
msgid "Your account has been deleted."
|
||||
msgstr "Votre compte a été supprimé."
|
||||
@@ -2203,15 +2056,15 @@ msgstr "Votre nom d'affichage a été mis à jour."
|
||||
msgid "Your password has been changed."
|
||||
msgstr "Votre mot de passe a été modifié."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:65
|
||||
#: src/views/settings/components/Avatar.tsx:27
|
||||
msgid "Your profile image has been updated."
|
||||
msgstr "Votre image de profil a été mise à jour."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:54
|
||||
#: src/views/settings/index.tsx:120
|
||||
msgid "Your Trello account has been disconnected."
|
||||
msgstr "Votre compte Trello a été déconnecté."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:102
|
||||
#: src/views/settings/index.tsx:281
|
||||
msgid "Your Trello account is connected."
|
||||
msgstr "Votre compte Trello est connecté."
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
export const locales = ["en", "fr", "de", "es", "it", "nl", "ru"] as const;
|
||||
export const locales = ["en", "fr", "de", "es", "it", "nl"] as const;
|
||||
|
||||
export type Locale = (typeof locales)[number];
|
||||
|
||||
@@ -11,5 +11,4 @@ export const localeNames: Record<Locale, string> = {
|
||||
es: "Español",
|
||||
it: "Italiano",
|
||||
nl: "Nederlands",
|
||||
ru: "Русский",
|
||||
};
|
||||
|
||||
@@ -36,12 +36,12 @@ msgstr "{0} etichette"
|
||||
msgid "{boardCount, plural, one {Import board (1)} other {Import boards ({boardCount})}}"
|
||||
msgstr "{boardCount, plural, one {Importa bacheca (1)} other {Importa bacheche ({boardCount})}}"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:146
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:92
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$10/month"
|
||||
msgstr "$10/mese"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$8/month"
|
||||
msgstr "$8/mese"
|
||||
|
||||
@@ -53,10 +53,6 @@ msgstr "1 utente"
|
||||
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
|
||||
msgstr "Un'app kanban potente e flessibile che ti aiuta a organizzare il lavoro, monitorare i progressi e ottenere risultati, tutto in un unico posto."
|
||||
|
||||
#: src/components/SettingsLayout.tsx:33
|
||||
msgid "Account"
|
||||
msgstr "Account"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:27
|
||||
msgid "Account deleted"
|
||||
msgstr "Account eliminato"
|
||||
@@ -95,13 +91,13 @@ msgstr "Aggiungi descrizione... (digita '/' per aprire i comandi o '@' per menzi
|
||||
msgid "Add details..."
|
||||
msgstr "Aggiungi dettagli..."
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:109
|
||||
#: src/views/card/components/LabelSelector.tsx:114
|
||||
#: src/views/card/components/LabelSelector.tsx:110
|
||||
#: src/views/card/components/LabelSelector.tsx:118
|
||||
msgid "Add label"
|
||||
msgstr "Aggiungi etichetta"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:130
|
||||
#: src/views/members/components/InviteMemberForm.tsx:238
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
msgid "Add member"
|
||||
msgstr "Aggiungi membro"
|
||||
|
||||
@@ -136,14 +132,10 @@ msgstr "ha aggiunto l'elemento <0>{0}</0> alla checklist"
|
||||
msgid "added label <0>{0}</0>"
|
||||
msgstr "ha aggiunto l'etichetta <0>{0}</0>"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:306
|
||||
#: src/views/members/components/InviteMemberForm.tsx:187
|
||||
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
|
||||
msgstr "L'aggiunta di un nuovo membro costerà un supplemento di {price} ({billingType}) per posto."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:275
|
||||
msgid "Adjust the square crop to fit your avatar."
|
||||
msgstr "Regola il ritaglio quadrato per adattarlo al tuo avatar."
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:55
|
||||
msgid "Admin roles"
|
||||
msgstr "Ruoli amministratore"
|
||||
@@ -152,11 +144,11 @@ msgstr "Ruoli amministratore"
|
||||
msgid "All systems operational"
|
||||
msgstr "Tutti i sistemi operativi"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:88
|
||||
#: src/views/auth/signup/index.tsx:86
|
||||
msgid "Already have an account? <0><1>Sign in</1></0>"
|
||||
msgstr "Hai già un account? <0><1>Accedi</1></0>"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:61
|
||||
#: src/views/settings/index.tsx:127
|
||||
msgid "An error occurred while disconnecting your Trello account."
|
||||
msgstr "Si è verificato un errore durante la disconnessione del tuo account Trello."
|
||||
|
||||
@@ -164,31 +156,7 @@ msgstr "Si è verificato un errore durante la disconnessione del tuo account Tre
|
||||
msgid "An unexpected error occurred. Please try again later."
|
||||
msgstr "Si è verificato un errore imprevisto. Riprova più tardi."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:290
|
||||
msgid "Anyone with this link can join your workspace"
|
||||
msgstr "Chiunque abbia questo link può unirsi al tuo spazio di lavoro"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:51
|
||||
msgid "API"
|
||||
msgstr "API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:91
|
||||
msgid "API key created"
|
||||
msgstr "Chiave API creata"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:161
|
||||
msgid "API key name"
|
||||
msgstr "Nome chiave API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:25
|
||||
msgid "API key name cannot exceed 30 characters"
|
||||
msgstr "Il nome della chiave API non può superare i 30 caratteri"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:24
|
||||
msgid "API key name is required"
|
||||
msgstr "Il nome della chiave API è obbligatorio"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:22
|
||||
#: src/views/settings/index.tsx:296
|
||||
msgid "API keys"
|
||||
msgstr "Chiavi API"
|
||||
|
||||
@@ -250,21 +218,20 @@ msgstr "Backlog"
|
||||
msgid "Basic Kanban"
|
||||
msgstr "Kanban base"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed annually"
|
||||
msgstr "fatturato annualmente"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed monthly"
|
||||
msgstr "fatturato mensilmente"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:44
|
||||
#: src/views/boards/components/TemplateBoards.tsx:55
|
||||
#: src/views/settings/BillingSettings.tsx:39
|
||||
#: src/views/settings/index.tsx:235
|
||||
msgid "Billing"
|
||||
msgstr "Fatturazione"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:49
|
||||
#: src/views/settings/index.tsx:245
|
||||
msgid "Billing portal"
|
||||
msgstr "Portale di fatturazione"
|
||||
|
||||
@@ -319,12 +286,12 @@ msgid "Board visibility updated"
|
||||
msgstr "Visibilità della bacheca aggiornata"
|
||||
|
||||
#: src/components/SideNavigation.tsx:68
|
||||
#: src/views/boards/index.tsx:32
|
||||
#: src/views/boards/index.tsx:27
|
||||
msgid "Boards"
|
||||
msgstr "Bacheche"
|
||||
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/boards/index.tsx:28
|
||||
#: src/views/boards/index.tsx:23
|
||||
msgid "Boards | {0}"
|
||||
msgstr "Bacheche | {0}"
|
||||
|
||||
@@ -346,7 +313,6 @@ msgstr "Segnalazione bug"
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:63
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:76
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:55
|
||||
#: src/views/settings/components/Avatar.tsx:306
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
|
||||
@@ -362,19 +328,19 @@ msgstr "Carta non trovata"
|
||||
msgid "Card title"
|
||||
msgstr "Titolo della carta"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:70
|
||||
#: src/views/settings/AccountSettings.tsx:80
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:109
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:178
|
||||
#: src/views/settings/index.tsx:328
|
||||
#: src/views/settings/index.tsx:338
|
||||
msgid "Change Password"
|
||||
msgstr "Cambia password"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:45
|
||||
msgid "Change your language preferences."
|
||||
msgstr "Modifica le tue preferenze di lingua."
|
||||
#: src/views/settings/index.tsx:227
|
||||
msgid "Change the language of the app."
|
||||
msgstr "Cambia la lingua dell'app."
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
msgid "Check your inbox"
|
||||
msgstr "Controlla la tua casella di posta"
|
||||
|
||||
@@ -386,15 +352,11 @@ msgstr "Nome della checklist"
|
||||
msgid "Clear filters"
|
||||
msgstr "Cancella filtri"
|
||||
|
||||
#: src/views/auth/login/index.tsx:48
|
||||
#: src/views/auth/signup/index.tsx:74
|
||||
#: src/views/auth/login/index.tsx:46
|
||||
#: src/views/auth/signup/index.tsx:72
|
||||
msgid "Click on the link we've sent to {magicLinkRecipient} to sign in."
|
||||
msgstr "Clicca sul link che abbiamo inviato a {magicLinkRecipient} per accedere."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:136
|
||||
msgid "Close"
|
||||
msgstr "Chiudi"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:22
|
||||
msgid "Code Review"
|
||||
msgstr "Revisione del codice"
|
||||
@@ -439,7 +401,7 @@ msgid "Confirm your new password"
|
||||
msgstr "Conferma la tua nuova password"
|
||||
|
||||
#: src/views/boards/components/ImportBoardsForm.tsx:157
|
||||
#: src/views/settings/IntegrationsSettings.tsx:93
|
||||
#: src/views/settings/index.tsx:272
|
||||
msgid "Connect Trello"
|
||||
msgstr "Connetti Trello"
|
||||
|
||||
@@ -447,7 +409,7 @@ msgstr "Connetti Trello"
|
||||
msgid "Connect your favorite tools to streamline your workflow."
|
||||
msgstr "Connetti i tuoi strumenti preferiti per semplificare il tuo flusso di lavoro."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:80
|
||||
#: src/views/settings/index.tsx:259
|
||||
msgid "Connect your Trello account to import boards."
|
||||
msgstr "Connetti il tuo account Trello per importare le bacheche."
|
||||
|
||||
@@ -463,12 +425,12 @@ msgstr "Contattaci"
|
||||
msgid "Content Creation"
|
||||
msgstr "Creazione contenuti"
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Continue with "
|
||||
msgstr "Continua con "
|
||||
|
||||
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
|
||||
#: src/components/AuthForm.tsx:301
|
||||
#: src/components/AuthForm.tsx:297
|
||||
msgid "Continue with {0}"
|
||||
msgstr "Continua con {0}"
|
||||
|
||||
@@ -482,10 +444,6 @@ msgstr "Controlla chi può visualizzare e modificare le tue bacheche."
|
||||
msgid "Create another"
|
||||
msgstr "Crea un altro"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:175
|
||||
msgid "Create API key"
|
||||
msgstr "Crea chiave API"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:128
|
||||
msgid "Create board"
|
||||
msgstr "Crea bacheca"
|
||||
@@ -510,12 +468,12 @@ msgstr "Crea lista"
|
||||
msgid "Create new board"
|
||||
msgstr "Crea nuova bacheca"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:30
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:54
|
||||
msgid "Create new key"
|
||||
msgstr "Crea nuova chiave"
|
||||
|
||||
#: src/views/board/components/NewCardForm.tsx:394
|
||||
#: src/views/card/components/LabelSelector.tsx:97
|
||||
#: src/views/card/components/LabelSelector.tsx:98
|
||||
msgid "Create new label"
|
||||
msgstr "Crea nuova etichetta"
|
||||
|
||||
@@ -536,10 +494,6 @@ msgstr "ha creato la carta"
|
||||
msgid "Critical"
|
||||
msgstr "Critico"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:272
|
||||
msgid "Crop your avatar"
|
||||
msgstr "Ritaglia il tuo avatar"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:19
|
||||
msgid "Current password is required"
|
||||
msgstr "La password attuale è obbligatoria"
|
||||
@@ -573,9 +527,9 @@ msgstr "Scuro"
|
||||
msgid "Delete"
|
||||
msgstr "Elimina"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:52
|
||||
#: src/views/settings/AccountSettings.tsx:62
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:96
|
||||
#: src/views/settings/index.tsx:346
|
||||
#: src/views/settings/index.tsx:356
|
||||
msgid "Delete account"
|
||||
msgstr "Elimina account"
|
||||
|
||||
@@ -596,8 +550,8 @@ msgid "Delete list"
|
||||
msgstr "Elimina lista"
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
|
||||
#: src/views/settings/WorkspaceSettings.tsx:96
|
||||
#: src/views/settings/WorkspaceSettings.tsx:107
|
||||
#: src/views/settings/index.tsx:309
|
||||
#: src/views/settings/index.tsx:320
|
||||
msgid "Delete workspace"
|
||||
msgstr "Elimina spazio di lavoro"
|
||||
|
||||
@@ -623,7 +577,7 @@ msgstr "ha eliminato l'elemento <0>{0}</0> della checklist"
|
||||
msgid "Design"
|
||||
msgstr "Design"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:108
|
||||
#: src/views/settings/index.tsx:287
|
||||
msgid "Disconnect Trello"
|
||||
msgstr "Disconnetti Trello"
|
||||
|
||||
@@ -631,7 +585,7 @@ msgstr "Disconnetti Trello"
|
||||
msgid "Discuss and collaborate on cards."
|
||||
msgstr "Discuti e collabora sulle schede."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:35
|
||||
#: src/views/settings/index.tsx:176
|
||||
msgid "Display name"
|
||||
msgstr "Nome visualizzato"
|
||||
|
||||
@@ -665,7 +619,7 @@ msgstr "Documenti"
|
||||
msgid "Documentation"
|
||||
msgstr "Documentazione"
|
||||
|
||||
#: src/views/auth/login/index.tsx:63
|
||||
#: src/views/auth/login/index.tsx:61
|
||||
msgid "Don't have an account? <0><1>Sign up</1></0>"
|
||||
msgstr "Non hai un account? <0><1>Registrati</1></0>"
|
||||
|
||||
@@ -697,11 +651,11 @@ msgstr "Modifica URL dell'area di lavoro"
|
||||
msgid "Editing"
|
||||
msgstr "Modifica"
|
||||
|
||||
#: src/components/AuthForm.tsx:372
|
||||
#: src/components/AuthForm.tsx:368
|
||||
msgid "email"
|
||||
msgstr "email"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:252
|
||||
#: src/views/members/components/InviteMemberForm.tsx:161
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
|
||||
@@ -717,11 +671,11 @@ msgstr "Inserisci la tua password attuale"
|
||||
msgid "Enter your current password and choose a new secure password."
|
||||
msgstr "Inserisci la tua password attuale e scegli una nuova password sicura."
|
||||
|
||||
#: src/components/AuthForm.tsx:337
|
||||
#: src/components/AuthForm.tsx:333
|
||||
msgid "Enter your email address"
|
||||
msgstr "Inserisci il tuo indirizzo email"
|
||||
|
||||
#: src/components/AuthForm.tsx:325
|
||||
#: src/components/AuthForm.tsx:321
|
||||
msgid "Enter your name"
|
||||
msgstr "Inserisci il tuo nome"
|
||||
|
||||
@@ -729,26 +683,14 @@ msgstr "Inserisci il tuo nome"
|
||||
msgid "Enter your new password"
|
||||
msgstr "Inserisci la tua nuova password"
|
||||
|
||||
#: src/components/AuthForm.tsx:350
|
||||
#: src/components/AuthForm.tsx:346
|
||||
msgid "Enter your password"
|
||||
msgstr "Inserisci la tua password"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Error"
|
||||
msgstr "Errore"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:89
|
||||
msgid "Error Changing Password"
|
||||
msgstr "Errore durante il cambio della password"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:117
|
||||
msgid "Error creating invite link"
|
||||
msgstr "Errore durante la creazione del link di invito"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:132
|
||||
msgid "Error deactivating invite link"
|
||||
msgstr "Errore durante la disattivazione del link di invito"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:39
|
||||
msgid "Error deleting account"
|
||||
msgstr "Errore durante l'eliminazione dell'account"
|
||||
@@ -761,12 +703,12 @@ msgstr "Errore durante l'eliminazione dell'etichetta"
|
||||
msgid "Error deleting workspace"
|
||||
msgstr "Errore durante l'eliminazione dell'area di lavoro"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:60
|
||||
#: src/views/settings/index.tsx:126
|
||||
msgid "Error disconnecting Trello"
|
||||
msgstr "Errore durante la disconnessione da Trello"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:95
|
||||
#: src/views/members/components/InviteMemberForm.tsx:101
|
||||
#: src/views/members/components/InviteMemberForm.tsx:71
|
||||
#: src/views/members/components/InviteMemberForm.tsx:77
|
||||
msgid "Error inviting member"
|
||||
msgstr "Errore durante l'invito del membro"
|
||||
|
||||
@@ -774,7 +716,7 @@ msgstr "Errore durante l'invito del membro"
|
||||
msgid "Error updating display name"
|
||||
msgstr "Errore durante l'aggiornamento del nome visualizzato"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:77
|
||||
#: src/views/settings/components/Avatar.tsx:39
|
||||
msgid "Error updating profile image"
|
||||
msgstr "Errore durante l'aggiornamento dell'immagine del profilo"
|
||||
|
||||
@@ -790,7 +732,7 @@ msgstr "Errore durante l'aggiornamento del nome dell'area di lavoro"
|
||||
msgid "Error updating workspace URL"
|
||||
msgstr "Errore durante l'aggiornamento dell'URL dell'area di lavoro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:221
|
||||
#: src/views/members/components/InviteMemberForm.tsx:130
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
|
||||
msgid "Error upgrading subscription"
|
||||
msgstr "Errore nell'aggiornamento dell'abbonamento"
|
||||
@@ -799,8 +741,8 @@ msgstr "Errore nell'aggiornamento dell'abbonamento"
|
||||
msgid "Error upgrading to Pro"
|
||||
msgstr "Errore durante l'aggiornamento a Pro"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:91
|
||||
#: src/views/settings/components/Avatar.tsx:218
|
||||
#: src/views/settings/components/Avatar.tsx:56
|
||||
#: src/views/settings/components/Avatar.tsx:97
|
||||
msgid "Error uploading profile image"
|
||||
msgstr "Errore durante il caricamento dell'immagine del profilo"
|
||||
|
||||
@@ -816,16 +758,8 @@ msgstr "Tutto ciò di cui hai bisogno, gratis per sempre. Bacheche illimitate, l
|
||||
msgid "Execution"
|
||||
msgstr "Esecuzione"
|
||||
|
||||
#: src/views/invite/index.tsx:41
|
||||
msgid "Failed to accept invitation. Please try again later, or contact customer support."
|
||||
msgstr "Impossibile accettare l'invito. Riprova più tardi o contatta l'assistenza clienti."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:197
|
||||
msgid "Failed to copy invite link"
|
||||
msgstr "Impossibile copiare il link di invito"
|
||||
|
||||
#. placeholder {0}: provider.at(0)?.toUpperCase() + provider.slice(1)
|
||||
#: src/components/AuthForm.tsx:273
|
||||
#: src/components/AuthForm.tsx:269
|
||||
msgid "Failed to login with {0}. Please try again."
|
||||
msgstr "Accesso con {0} fallito. Riprova."
|
||||
|
||||
@@ -873,7 +807,7 @@ msgstr "Per la sostenibilità a lungo termine, riconosciamo che tutti i buoni pr
|
||||
msgid "Free"
|
||||
msgstr "Gratuito"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:312
|
||||
#: src/views/members/components/InviteMemberForm.tsx:193
|
||||
#: src/views/members/index.tsx:208
|
||||
msgid "Free Plan"
|
||||
msgstr "Piano gratuito"
|
||||
@@ -890,7 +824,7 @@ msgstr "Tempo pieno"
|
||||
msgid "Fun"
|
||||
msgstr "Divertimento"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
#: src/views/home/components/Cta.tsx:61
|
||||
#: src/views/home/components/Header.tsx:102
|
||||
#: src/views/home/components/Header.tsx:141
|
||||
@@ -930,13 +864,8 @@ msgstr "Primi passi"
|
||||
msgid "GitHub"
|
||||
msgstr "GitHub"
|
||||
|
||||
#: src/views/invite/index.tsx:113
|
||||
msgid "Go Home"
|
||||
msgstr "Vai alla home"
|
||||
|
||||
#: src/views/home/components/Header.tsx:96
|
||||
#: src/views/home/components/Header.tsx:133
|
||||
#: src/views/invite/index.tsx:144
|
||||
msgid "Go to app"
|
||||
msgstr "Vai all'app"
|
||||
|
||||
@@ -988,7 +917,7 @@ msgstr "Idee"
|
||||
msgid "Ideas to improve this page..."
|
||||
msgstr "Idee per migliorare questa pagina..."
|
||||
|
||||
#: src/views/boards/index.tsx:43
|
||||
#: src/views/boards/index.tsx:38
|
||||
msgid "Import"
|
||||
msgstr "Importa"
|
||||
|
||||
@@ -1026,7 +955,6 @@ msgstr "In corso"
|
||||
msgid "Individuals"
|
||||
msgstr "Privati"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:57
|
||||
#: src/views/home/components/Features.tsx:114
|
||||
msgid "Integrations"
|
||||
msgstr "Integrazioni"
|
||||
@@ -1035,44 +963,27 @@ msgstr "Integrazioni"
|
||||
msgid "Interviewing"
|
||||
msgstr "Colloquio"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:49
|
||||
#: src/views/members/components/InviteMemberForm.tsx:40
|
||||
msgid "Invalid email address"
|
||||
msgstr "Indirizzo email non valido"
|
||||
|
||||
#: src/views/invite/index.tsx:105
|
||||
msgid "Invalid invitation"
|
||||
msgstr "Invito non valido"
|
||||
|
||||
#: src/views/members/index.tsx:221
|
||||
msgid "Invite"
|
||||
msgstr "Invita"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:190
|
||||
msgid "Invite link copied"
|
||||
msgstr "Link di invito copiato"
|
||||
#: src/views/members/components/InviteMemberForm.tsx:208
|
||||
msgid "Invite another"
|
||||
msgstr "Invita un altro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:191
|
||||
msgid "Invite link copied to clipboard"
|
||||
msgstr "Link di invito copiato negli appunti"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:111
|
||||
#: src/views/members/components/InviteMemberForm.tsx:350
|
||||
#: src/views/card/components/MemberSelector.tsx:112
|
||||
#: src/views/members/components/InviteMemberForm.tsx:233
|
||||
msgid "Invite member"
|
||||
msgstr "Invita membro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:315
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
|
||||
msgstr "L'invito di membri richiede un Piano Team. Sarai reindirizzato per aggiornare il tuo spazio di lavoro."
|
||||
|
||||
#: src/views/invite/index.tsx:79
|
||||
#: src/views/invite/index.tsx:129
|
||||
msgid "Join workspace"
|
||||
msgstr "Unisciti allo spazio di lavoro"
|
||||
|
||||
#: src/views/invite/index.tsx:91
|
||||
msgid "Join workspace | kan.bn"
|
||||
msgstr "Unisciti allo spazio di lavoro | kan.bn"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:69
|
||||
msgid "Junior"
|
||||
msgstr "Junior"
|
||||
@@ -1100,7 +1011,7 @@ msgstr "Etichette"
|
||||
msgid "Labels & Filters"
|
||||
msgstr "Etichette & Filtri"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:224
|
||||
msgid "Language"
|
||||
msgstr "Lingua"
|
||||
|
||||
@@ -1145,7 +1056,7 @@ msgstr "Lista"
|
||||
msgid "List name"
|
||||
msgstr "Nome lista"
|
||||
|
||||
#: src/views/auth/login/index.tsx:33
|
||||
#: src/views/auth/login/index.tsx:31
|
||||
msgid "Login | kan.bn"
|
||||
msgstr "Login | kan.bn"
|
||||
|
||||
@@ -1161,7 +1072,7 @@ msgstr "A lungo termine"
|
||||
msgid "Low Priority"
|
||||
msgstr "Bassa priorità"
|
||||
|
||||
#: src/components/AuthForm.tsx:373
|
||||
#: src/components/AuthForm.tsx:369
|
||||
msgid "magic link"
|
||||
msgstr "link magico"
|
||||
|
||||
@@ -1195,7 +1106,7 @@ msgstr "Membri | {0}"
|
||||
msgid "Monthly"
|
||||
msgstr "Mensile"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
#: src/views/members/components/InviteMemberForm.tsx:93
|
||||
msgid "monthly billing"
|
||||
msgstr "fatturazione mensile"
|
||||
|
||||
@@ -1218,14 +1129,10 @@ msgstr "Nome"
|
||||
msgid "Need help?"
|
||||
msgstr "Hai bisogno di aiuto?"
|
||||
|
||||
#: src/views/boards/index.tsx:53
|
||||
#: src/views/boards/index.tsx:48
|
||||
msgid "New"
|
||||
msgstr "Nuovo"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:147
|
||||
msgid "New API key"
|
||||
msgstr "Nuova chiave API"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:85
|
||||
msgid "New board"
|
||||
msgstr "Nuova bacheca"
|
||||
@@ -1299,15 +1206,15 @@ msgstr "Offerta"
|
||||
msgid "Onboarding"
|
||||
msgstr "Inserimento"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:55
|
||||
#: src/views/settings/index.tsx:349
|
||||
msgid "Once you delete your account, there is no going back. This action cannot be undone."
|
||||
msgstr "Una volta eliminato il tuo account, non si può tornare indietro. Questa azione non può essere annullata."
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:99
|
||||
#: src/views/settings/index.tsx:312
|
||||
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
|
||||
msgstr "Una volta eliminata l'area di lavoro, non si può tornare indietro. Questa azione non può essere annullata."
|
||||
|
||||
#: src/components/AuthForm.tsx:315
|
||||
#: src/components/AuthForm.tsx:311
|
||||
msgid "or"
|
||||
msgstr "o"
|
||||
|
||||
@@ -1335,10 +1242,6 @@ msgstr "La password deve contenere almeno 8 caratteri"
|
||||
msgid "Passwords do not match"
|
||||
msgstr "Le password non corrispondono"
|
||||
|
||||
#: src/views/members/index.tsx:134
|
||||
msgid "Paused"
|
||||
msgstr "In pausa"
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:102
|
||||
msgid "Payment frequency"
|
||||
msgstr "Frequenza di pagamento"
|
||||
@@ -1364,19 +1267,19 @@ msgstr "Pianificazione"
|
||||
msgid "Please confirm your new password"
|
||||
msgstr "Conferma la tua nuova password"
|
||||
|
||||
#: src/components/AuthForm.tsx:341
|
||||
#: src/components/AuthForm.tsx:337
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Inserisci un indirizzo email valido"
|
||||
|
||||
#: src/components/AuthForm.tsx:329
|
||||
#: src/components/AuthForm.tsx:325
|
||||
msgid "Please enter a valid name"
|
||||
msgstr "Inserisci un nome valido"
|
||||
|
||||
#: src/components/AuthForm.tsx:354
|
||||
#: src/components/AuthForm.tsx:350
|
||||
msgid "Please enter a valid password"
|
||||
msgstr "Inserisci una password valida"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:92
|
||||
#: src/views/settings/components/Avatar.tsx:57
|
||||
msgid "Please select a file to upload."
|
||||
msgstr "Seleziona un file da caricare."
|
||||
|
||||
@@ -1397,18 +1300,18 @@ msgstr "Seleziona un file da caricare."
|
||||
#: src/views/card/components/DeleteCardConfirmation.tsx:52
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:37
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:45
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
#: src/views/card/components/LabelSelector.tsx:73
|
||||
#: src/views/card/components/ListSelector.tsx:53
|
||||
#: src/views/card/components/MemberSelector.tsx:80
|
||||
#: src/views/card/components/NewChecklistForm.tsx:70
|
||||
#: src/views/card/components/NewChecklistItemForm.tsx:89
|
||||
#: src/views/card/components/NewCommentForm.tsx:31
|
||||
#: src/views/card/index.tsx:173
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
|
||||
#: src/views/members/components/InviteMemberForm.tsx:102
|
||||
#: src/views/members/components/InviteMemberForm.tsx:222
|
||||
#: src/views/settings/components/Avatar.tsx:78
|
||||
#: src/views/settings/components/Avatar.tsx:219
|
||||
#: src/views/members/components/InviteMemberForm.tsx:78
|
||||
#: src/views/members/components/InviteMemberForm.tsx:131
|
||||
#: src/views/settings/components/Avatar.tsx:40
|
||||
#: src/views/settings/components/Avatar.tsx:98
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:46
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:55
|
||||
@@ -1419,11 +1322,6 @@ msgstr "Seleziona un file da caricare."
|
||||
msgid "Please try again later, or contact customer support."
|
||||
msgstr "Riprova più tardi o contatta l'assistenza clienti."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:118
|
||||
#: src/views/members/components/InviteMemberForm.tsx:133
|
||||
msgid "Please try again later."
|
||||
msgstr "Riprova più tardi."
|
||||
|
||||
#: src/views/home/components/Footer.tsx:50
|
||||
#: src/views/home/components/Header.tsx:15
|
||||
#: src/views/home/components/Pricing.tsx:85
|
||||
@@ -1447,15 +1345,15 @@ msgstr "Privato"
|
||||
msgid "Pro Plan"
|
||||
msgstr "Piano Pro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
msgid "Pro Plan ∞"
|
||||
msgstr "Piano Pro ∞"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:64
|
||||
#: src/views/settings/components/Avatar.tsx:26
|
||||
msgid "Profile image updated"
|
||||
msgstr "Immagine del profilo aggiornata"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:29
|
||||
#: src/views/settings/index.tsx:171
|
||||
msgid "Profile picture"
|
||||
msgstr "Immagine del profilo"
|
||||
|
||||
@@ -1538,6 +1436,10 @@ msgstr "Risorse"
|
||||
msgid "Review"
|
||||
msgstr "Revisione"
|
||||
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:49
|
||||
msgid "Revoke"
|
||||
msgstr "Revoca"
|
||||
|
||||
#: src/views/home/components/Footer.tsx:36
|
||||
#: src/views/home/components/Header.tsx:13
|
||||
msgid "Roadmap"
|
||||
@@ -1552,7 +1454,6 @@ msgid "Run on your own infrastructure"
|
||||
msgstr "Esegui sulla tua infrastruttura"
|
||||
|
||||
#: src/views/card/components/Comment.tsx:165
|
||||
#: src/views/settings/components/Avatar.tsx:309
|
||||
msgid "Save"
|
||||
msgstr "Salva"
|
||||
|
||||
@@ -1592,62 +1493,35 @@ msgstr "Invia feedback"
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:82
|
||||
#: src/components/SideNavigation.tsx:78
|
||||
#: src/views/settings/index.tsx:165
|
||||
msgid "Settings"
|
||||
msgstr "Impostazioni"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:25
|
||||
msgid "Settings | Account"
|
||||
msgstr "Impostazioni | Account"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:18
|
||||
msgid "Settings | API"
|
||||
msgstr "Impostazioni | API"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:35
|
||||
msgid "Settings | Billing"
|
||||
msgstr "Impostazioni | Fatturazione"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:69
|
||||
msgid "Settings | Integrations"
|
||||
msgstr "Impostazioni | Integrazioni"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:54
|
||||
msgid "Settings | Workspace"
|
||||
msgstr "Impostazioni | Area di lavoro"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:327
|
||||
msgid "Share invite link"
|
||||
msgstr "Condividi link di invito"
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/settings/index.tsx:161
|
||||
msgid "Settings | {0}"
|
||||
msgstr "Impostazioni | {0}"
|
||||
|
||||
#: src/views/home/components/Header.tsx:100
|
||||
#: src/views/home/components/Header.tsx:138
|
||||
msgid "Sign in"
|
||||
msgstr "Accedi"
|
||||
|
||||
#: src/views/invite/index.tsx:154
|
||||
msgid "Sign In"
|
||||
msgstr "Accedi"
|
||||
|
||||
#: src/views/invite/index.tsx:162
|
||||
msgid "Sign Up"
|
||||
msgstr "Registrati"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:34
|
||||
#: src/views/auth/signup/index.tsx:59
|
||||
#: src/views/auth/signup/index.tsx:32
|
||||
#: src/views/auth/signup/index.tsx:57
|
||||
msgid "Sign up | kan.bn"
|
||||
msgstr "Registrati | kan.bn"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:44
|
||||
#: src/views/auth/signup/index.tsx:42
|
||||
msgid "Sign up disabled"
|
||||
msgstr "Registrazione disabilitata"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:47
|
||||
#: src/views/auth/signup/index.tsx:45
|
||||
msgid "Sign up is currently disabled. Please try again later."
|
||||
msgstr "La registrazione è attualmente disabilitata. Riprova più tardi."
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Sign up with "
|
||||
msgstr "Registrati con "
|
||||
|
||||
@@ -1671,8 +1545,8 @@ msgstr "Sviluppo software"
|
||||
msgid "Star on Github"
|
||||
msgstr "Metti una stella su Github"
|
||||
|
||||
#: src/components/AuthForm.tsx:207
|
||||
#: src/components/AuthForm.tsx:224
|
||||
#: src/components/AuthForm.tsx:203
|
||||
#: src/components/AuthForm.tsx:220
|
||||
msgid "Success"
|
||||
msgstr "Operazione riuscita"
|
||||
|
||||
@@ -1692,7 +1566,7 @@ msgstr "Sostieni lo sviluppo del progetto"
|
||||
msgid "System"
|
||||
msgstr "Sistema"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
#: src/views/members/index.tsx:207
|
||||
msgid "Team Plan"
|
||||
msgstr "Piano Team"
|
||||
@@ -1745,10 +1619,6 @@ msgstr "Non potranno accedere a questo spazio di lavoro."
|
||||
msgid "This action can't be undone."
|
||||
msgstr "Questa azione non può essere annullata."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:129
|
||||
msgid "This API key will only be shown once. Please save it in a secure location."
|
||||
msgstr "Questa chiave API verrà mostrata una sola volta. Salvala in un luogo sicuro."
|
||||
|
||||
#: src/views/public/board/index.tsx:151
|
||||
msgid "This board is private or does not exist"
|
||||
msgstr "Questa bacheca è privata o non esiste"
|
||||
@@ -1757,10 +1627,6 @@ msgstr "Questa bacheca è privata o non esiste"
|
||||
msgid "This board URL has already been taken"
|
||||
msgstr "Questo URL della bacheca è già stato utilizzato"
|
||||
|
||||
#: src/views/invite/index.tsx:108
|
||||
msgid "This invitation link is invalid or has expired."
|
||||
msgstr "Questo link di invito non è valido o è scaduto."
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:70
|
||||
msgid "This will result in the permanent deletion of all data associated with this workspace."
|
||||
msgstr "Questo comporterà l'eliminazione permanente di tutti i dati associati a questo spazio di lavoro."
|
||||
@@ -1794,11 +1660,7 @@ msgstr "Attiva/disattiva menu"
|
||||
msgid "Track all card changes with detailed activity history."
|
||||
msgstr "Tieni traccia di tutte le modifiche alle schede con una cronologia dettagliata delle attività."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:73
|
||||
msgid "Trello"
|
||||
msgstr "Trello"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:53
|
||||
#: src/views/settings/index.tsx:119
|
||||
msgid "Trello disconnected"
|
||||
msgstr "Trello disconnesso"
|
||||
|
||||
@@ -1883,16 +1745,16 @@ msgstr "Impossibile aggiornare l'elemento della checklist"
|
||||
msgid "Unable to update comment"
|
||||
msgstr "Impossibile aggiornare il commento"
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:71
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
msgid "Unable to update labels"
|
||||
msgstr "Impossibile aggiornare le etichette"
|
||||
|
||||
#: src/views/board/index.tsx:133
|
||||
#: src/views/card/components/ListSelector.tsx:51
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
msgid "Unable to update list"
|
||||
msgstr "Impossibile aggiornare la lista"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:78
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
msgid "Unable to update members"
|
||||
msgstr "Impossibile aggiornare i membri"
|
||||
|
||||
@@ -1935,9 +1797,9 @@ msgid "Unlimited members"
|
||||
msgstr "Membri illimitati"
|
||||
|
||||
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:79
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:90
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:82
|
||||
#: src/views/settings/components/UpdateWorkspaceUrlForm.tsx:154
|
||||
msgid "Update"
|
||||
msgstr "Aggiorna"
|
||||
@@ -1968,7 +1830,7 @@ msgid "Upgrade"
|
||||
msgstr "Aggiorna"
|
||||
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
|
||||
#: src/views/settings/WorkspaceSettings.tsx:89
|
||||
#: src/views/settings/index.tsx:216
|
||||
msgid "Upgrade to Pro"
|
||||
msgstr "Passa a Pro"
|
||||
|
||||
@@ -1976,7 +1838,7 @@ msgstr "Passa a Pro"
|
||||
msgid "Upgrade to Pro ($29/month)"
|
||||
msgstr "Passa a Pro ($29/mese)"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:341
|
||||
#: src/views/members/components/InviteMemberForm.tsx:224
|
||||
msgid "Upgrade to Team Plan"
|
||||
msgstr "Passa al Piano Team"
|
||||
|
||||
@@ -2008,7 +1870,7 @@ msgstr "Usa template"
|
||||
msgid "User"
|
||||
msgstr "Utente"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:96
|
||||
#: src/views/members/components/InviteMemberForm.tsx:72
|
||||
msgid "User is already a member of this workspace"
|
||||
msgstr "L'utente è già membro di questo spazio di lavoro"
|
||||
|
||||
@@ -2016,11 +1878,11 @@ msgstr "L'utente è già membro di questo spazio di lavoro"
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:25
|
||||
#: src/views/settings/index.tsx:299
|
||||
msgid "View and manage your API keys."
|
||||
msgstr "Visualizza e gestisci le tue chiavi API."
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:238
|
||||
msgid "View and manage your billing and subscription."
|
||||
msgstr "Visualizza e gestisci la tua fatturazione e abbonamento."
|
||||
|
||||
@@ -2052,7 +1914,7 @@ msgstr "Utilizziamo la <0>licenza AGPL-3.0</0>."
|
||||
msgid "We're just getting started. "
|
||||
msgstr "Siamo solo all'inizio. "
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
msgid "Welcome back"
|
||||
msgstr "Bentornato"
|
||||
|
||||
@@ -2072,7 +1934,6 @@ msgstr "Quando Trello fu lanciato nel 2011, stupì tutti con la sua semplicità
|
||||
msgid "Why make an open source Trello?"
|
||||
msgstr "Perché creare un Trello open source?"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:39
|
||||
#: src/views/board/index.tsx:331
|
||||
msgid "Workspace"
|
||||
msgstr "Spazio di lavoro"
|
||||
@@ -2085,7 +1946,7 @@ msgstr "Spazio di lavoro creato con successo. Puoi effettuare l'aggiornamento pi
|
||||
msgid "Workspace deleted"
|
||||
msgstr "Spazio di lavoro eliminato"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:75
|
||||
#: src/views/settings/index.tsx:202
|
||||
msgid "Workspace description"
|
||||
msgstr "Descrizione dello spazio di lavoro"
|
||||
|
||||
@@ -2107,7 +1968,7 @@ msgid "Workspace members"
|
||||
msgstr "Membri del workspace"
|
||||
|
||||
#: src/components/NewWorkspaceForm.tsx:259
|
||||
#: src/views/settings/WorkspaceSettings.tsx:58
|
||||
#: src/views/settings/index.tsx:183
|
||||
msgid "Workspace name"
|
||||
msgstr "Nome dello spazio di lavoro"
|
||||
|
||||
@@ -2131,7 +1992,7 @@ msgstr "Nome dello spazio di lavoro aggiornato"
|
||||
msgid "Workspace slug updated"
|
||||
msgstr "Slug dell'area di lavoro aggiornato"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:66
|
||||
#: src/views/settings/index.tsx:192
|
||||
msgid "Workspace URL"
|
||||
msgstr "URL dello spazio di lavoro"
|
||||
|
||||
@@ -2151,7 +2012,7 @@ msgstr "Annuale"
|
||||
msgid "Yes, we offer an forever free plan for individual use. No restrictions, no paywalls, no limits."
|
||||
msgstr "Sì, offriamo un piano gratuito per sempre per uso individuale. Nessuna restrizione, nessun paywall, nessun limite."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:73
|
||||
#: src/views/settings/index.tsx:331
|
||||
msgid "You are about to change your password."
|
||||
msgstr "Stai per cambiare la tua password."
|
||||
|
||||
@@ -2167,26 +2028,18 @@ msgstr "Puoi invitare i membri del team cliccando sul pulsante \"Invita\" nell'a
|
||||
msgid "You can self-host by following the instructions in our <0>repo</0>."
|
||||
msgstr "Puoi effettuare il self-hosting seguendo le istruzioni nel nostro <0>repo</0>."
|
||||
|
||||
#: src/components/AuthForm.tsx:225
|
||||
#: src/components/AuthForm.tsx:221
|
||||
msgid "You have been logged in successfully."
|
||||
msgstr "Hai effettuato l'accesso con successo."
|
||||
|
||||
#: src/components/AuthForm.tsx:208
|
||||
#: src/components/AuthForm.tsx:204
|
||||
msgid "You have been signed up successfully."
|
||||
msgstr "Ti sei registrato con successo."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:305
|
||||
#: src/views/members/components/InviteMemberForm.tsx:186
|
||||
msgid "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
|
||||
msgstr "Hai posti illimitati con il tuo Piano Pro. Non ci sono costi aggiuntivi per i nuovi membri!"
|
||||
|
||||
#: src/views/invite/index.tsx:134
|
||||
msgid "You've been invited to join a workspace on kan.bn."
|
||||
msgstr "Sei stato invitato a unirti a uno spazio di lavoro su kan.bn."
|
||||
|
||||
#: src/views/invite/index.tsx:135
|
||||
msgid "You've been invited to join a workspace."
|
||||
msgstr "Sei stato invitato a unirti a uno spazio di lavoro."
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:28
|
||||
msgid "Your account has been deleted."
|
||||
msgstr "Il tuo account è stato eliminato."
|
||||
@@ -2203,15 +2056,15 @@ msgstr "Il tuo nome visualizzato è stato aggiornato."
|
||||
msgid "Your password has been changed."
|
||||
msgstr "La tua password è stata modificata."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:65
|
||||
#: src/views/settings/components/Avatar.tsx:27
|
||||
msgid "Your profile image has been updated."
|
||||
msgstr "La tua immagine del profilo è stata aggiornata."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:54
|
||||
#: src/views/settings/index.tsx:120
|
||||
msgid "Your Trello account has been disconnected."
|
||||
msgstr "Il tuo account Trello è stato disconnesso."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:102
|
||||
#: src/views/settings/index.tsx:281
|
||||
msgid "Your Trello account is connected."
|
||||
msgstr "Il tuo account Trello è connesso."
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -36,12 +36,12 @@ msgstr "{0} labels"
|
||||
msgid "{boardCount, plural, one {Import board (1)} other {Import boards ({boardCount})}}"
|
||||
msgstr "{boardCount, plural, one {Bord importeren (1)} other {Borden importeren ({boardCount})}}"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:146
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:92
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$10/month"
|
||||
msgstr "$10/maand"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:158
|
||||
#: src/views/members/components/InviteMemberForm.tsx:104
|
||||
msgid "$8/month"
|
||||
msgstr "$8/maand"
|
||||
|
||||
@@ -53,10 +53,6 @@ msgstr "1 gebruiker"
|
||||
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
|
||||
msgstr "Een krachtige, flexibele kanban-app die je helpt werk te organiseren, voortgang bij te houden en resultaten te leveren—allemaal op één plek."
|
||||
|
||||
#: src/components/SettingsLayout.tsx:33
|
||||
msgid "Account"
|
||||
msgstr "Account"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:27
|
||||
msgid "Account deleted"
|
||||
msgstr "Account verwijderd"
|
||||
@@ -95,13 +91,13 @@ msgstr "Beschrijving toevoegen... (typ '/' om commando's te openen of '@' om te
|
||||
msgid "Add details..."
|
||||
msgstr "Details toevoegen..."
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:109
|
||||
#: src/views/card/components/LabelSelector.tsx:114
|
||||
#: src/views/card/components/LabelSelector.tsx:110
|
||||
#: src/views/card/components/LabelSelector.tsx:118
|
||||
msgid "Add label"
|
||||
msgstr "Label toevoegen"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:130
|
||||
#: src/views/members/components/InviteMemberForm.tsx:238
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
msgid "Add member"
|
||||
msgstr "Lid toevoegen"
|
||||
|
||||
@@ -136,14 +132,10 @@ msgstr "heeft checklistitem <0>{0}</0> toegevoegd"
|
||||
msgid "added label <0>{0}</0>"
|
||||
msgstr "heeft label <0>{0}</0> toegevoegd"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:306
|
||||
#: src/views/members/components/InviteMemberForm.tsx:187
|
||||
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
|
||||
msgstr "Het toevoegen van een nieuw lid kost een extra {price} ({billingType}) per plaats."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:275
|
||||
msgid "Adjust the square crop to fit your avatar."
|
||||
msgstr "Pas de vierkante uitsnede aan zodat je avatar goed past."
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:55
|
||||
msgid "Admin roles"
|
||||
msgstr "Beheerdersrollen"
|
||||
@@ -152,11 +144,11 @@ msgstr "Beheerdersrollen"
|
||||
msgid "All systems operational"
|
||||
msgstr "Alle systemen operationeel"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:88
|
||||
#: src/views/auth/signup/index.tsx:86
|
||||
msgid "Already have an account? <0><1>Sign in</1></0>"
|
||||
msgstr "Heb je al een account? <0><1>Log in</1></0>"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:61
|
||||
#: src/views/settings/index.tsx:127
|
||||
msgid "An error occurred while disconnecting your Trello account."
|
||||
msgstr "Er is een fout opgetreden bij het verbreken van de verbinding met je Trello-account."
|
||||
|
||||
@@ -164,31 +156,7 @@ msgstr "Er is een fout opgetreden bij het verbreken van de verbinding met je Tre
|
||||
msgid "An unexpected error occurred. Please try again later."
|
||||
msgstr "Er is een onverwachte fout opgetreden. Probeer het later opnieuw."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:290
|
||||
msgid "Anyone with this link can join your workspace"
|
||||
msgstr "Iedereen met deze link kan deelnemen aan je werkruimte"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:51
|
||||
msgid "API"
|
||||
msgstr "API"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:91
|
||||
msgid "API key created"
|
||||
msgstr "API-sleutel aangemaakt"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:161
|
||||
msgid "API key name"
|
||||
msgstr "API-sleutelnaam"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:25
|
||||
msgid "API key name cannot exceed 30 characters"
|
||||
msgstr "API-sleutelnaam mag niet langer zijn dan 30 tekens"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:24
|
||||
msgid "API key name is required"
|
||||
msgstr "API-sleutelnaam is verplicht"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:22
|
||||
#: src/views/settings/index.tsx:296
|
||||
msgid "API keys"
|
||||
msgstr "API-sleutels"
|
||||
|
||||
@@ -250,21 +218,20 @@ msgstr "Backlog"
|
||||
msgid "Basic Kanban"
|
||||
msgstr "Basis kanban"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed annually"
|
||||
msgstr "jaarlijks gefactureerd"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:159
|
||||
#: src/views/members/components/InviteMemberForm.tsx:105
|
||||
msgid "billed monthly"
|
||||
msgstr "maandelijks gefactureerd"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:44
|
||||
#: src/views/boards/components/TemplateBoards.tsx:55
|
||||
#: src/views/settings/BillingSettings.tsx:39
|
||||
#: src/views/settings/index.tsx:235
|
||||
msgid "Billing"
|
||||
msgstr "Facturering"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:49
|
||||
#: src/views/settings/index.tsx:245
|
||||
msgid "Billing portal"
|
||||
msgstr "Factureringsportaal"
|
||||
|
||||
@@ -319,12 +286,12 @@ msgid "Board visibility updated"
|
||||
msgstr "Zichtbaarheid van bord bijgewerkt"
|
||||
|
||||
#: src/components/SideNavigation.tsx:68
|
||||
#: src/views/boards/index.tsx:32
|
||||
#: src/views/boards/index.tsx:27
|
||||
msgid "Boards"
|
||||
msgstr "Borden"
|
||||
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/boards/index.tsx:28
|
||||
#: src/views/boards/index.tsx:23
|
||||
msgid "Boards | {0}"
|
||||
msgstr "Borden | {0}"
|
||||
|
||||
@@ -346,7 +313,6 @@ msgstr "Bugrapport"
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:63
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:76
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:55
|
||||
#: src/views/settings/components/Avatar.tsx:306
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
|
||||
@@ -362,19 +328,19 @@ msgstr "Kaart niet gevonden"
|
||||
msgid "Card title"
|
||||
msgstr "Kaarttitel"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:70
|
||||
#: src/views/settings/AccountSettings.tsx:80
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:109
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:178
|
||||
#: src/views/settings/index.tsx:328
|
||||
#: src/views/settings/index.tsx:338
|
||||
msgid "Change Password"
|
||||
msgstr "Wachtwoord wijzigen"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:45
|
||||
msgid "Change your language preferences."
|
||||
msgstr "Wijzig je taalvoorkeuren."
|
||||
#: src/views/settings/index.tsx:227
|
||||
msgid "Change the language of the app."
|
||||
msgstr "Wijzig de taal van de app."
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
msgid "Check your inbox"
|
||||
msgstr "Controleer je inbox"
|
||||
|
||||
@@ -386,15 +352,11 @@ msgstr "Naam checklist"
|
||||
msgid "Clear filters"
|
||||
msgstr "Filters wissen"
|
||||
|
||||
#: src/views/auth/login/index.tsx:48
|
||||
#: src/views/auth/signup/index.tsx:74
|
||||
#: src/views/auth/login/index.tsx:46
|
||||
#: src/views/auth/signup/index.tsx:72
|
||||
msgid "Click on the link we've sent to {magicLinkRecipient} to sign in."
|
||||
msgstr "Klik op de link die we naar {magicLinkRecipient} hebben gestuurd om in te loggen."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:136
|
||||
msgid "Close"
|
||||
msgstr "Sluiten"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:22
|
||||
msgid "Code Review"
|
||||
msgstr "Code review"
|
||||
@@ -439,7 +401,7 @@ msgid "Confirm your new password"
|
||||
msgstr "Bevestig je nieuwe wachtwoord"
|
||||
|
||||
#: src/views/boards/components/ImportBoardsForm.tsx:157
|
||||
#: src/views/settings/IntegrationsSettings.tsx:93
|
||||
#: src/views/settings/index.tsx:272
|
||||
msgid "Connect Trello"
|
||||
msgstr "Verbind Trello"
|
||||
|
||||
@@ -447,7 +409,7 @@ msgstr "Verbind Trello"
|
||||
msgid "Connect your favorite tools to streamline your workflow."
|
||||
msgstr "Verbind je favoriete tools om je werkstroom te stroomlijnen."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:80
|
||||
#: src/views/settings/index.tsx:259
|
||||
msgid "Connect your Trello account to import boards."
|
||||
msgstr "Verbind je Trello-account om borden te importeren."
|
||||
|
||||
@@ -463,12 +425,12 @@ msgstr "Neem contact op"
|
||||
msgid "Content Creation"
|
||||
msgstr "Content creatie"
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Continue with "
|
||||
msgstr "Doorgaan met "
|
||||
|
||||
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
|
||||
#: src/components/AuthForm.tsx:301
|
||||
#: src/components/AuthForm.tsx:297
|
||||
msgid "Continue with {0}"
|
||||
msgstr "Doorgaan met {0}"
|
||||
|
||||
@@ -482,10 +444,6 @@ msgstr "Bepaal wie je borden kan bekijken en bewerken."
|
||||
msgid "Create another"
|
||||
msgstr "Maak nog een"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:175
|
||||
msgid "Create API key"
|
||||
msgstr "API-sleutel aanmaken"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:128
|
||||
msgid "Create board"
|
||||
msgstr "Maak bord"
|
||||
@@ -510,12 +468,12 @@ msgstr "Lijst maken"
|
||||
msgid "Create new board"
|
||||
msgstr "Nieuw bord maken"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:30
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:54
|
||||
msgid "Create new key"
|
||||
msgstr "Nieuwe sleutel aanmaken"
|
||||
|
||||
#: src/views/board/components/NewCardForm.tsx:394
|
||||
#: src/views/card/components/LabelSelector.tsx:97
|
||||
#: src/views/card/components/LabelSelector.tsx:98
|
||||
msgid "Create new label"
|
||||
msgstr "Maak nieuw label"
|
||||
|
||||
@@ -536,10 +494,6 @@ msgstr "heeft de kaart aangemaakt"
|
||||
msgid "Critical"
|
||||
msgstr "Kritiek"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:272
|
||||
msgid "Crop your avatar"
|
||||
msgstr "Snijd je avatar bij"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:19
|
||||
msgid "Current password is required"
|
||||
msgstr "Huidig wachtwoord is vereist"
|
||||
@@ -573,9 +527,9 @@ msgstr "Donker"
|
||||
msgid "Delete"
|
||||
msgstr "Verwijderen"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:52
|
||||
#: src/views/settings/AccountSettings.tsx:62
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:96
|
||||
#: src/views/settings/index.tsx:346
|
||||
#: src/views/settings/index.tsx:356
|
||||
msgid "Delete account"
|
||||
msgstr "Account verwijderen"
|
||||
|
||||
@@ -596,8 +550,8 @@ msgid "Delete list"
|
||||
msgstr "Lijst verwijderen"
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
|
||||
#: src/views/settings/WorkspaceSettings.tsx:96
|
||||
#: src/views/settings/WorkspaceSettings.tsx:107
|
||||
#: src/views/settings/index.tsx:309
|
||||
#: src/views/settings/index.tsx:320
|
||||
msgid "Delete workspace"
|
||||
msgstr "Werkruimte verwijderen"
|
||||
|
||||
@@ -623,7 +577,7 @@ msgstr "heeft checklistitem <0>{0}</0> verwijderd"
|
||||
msgid "Design"
|
||||
msgstr "Ontwerp"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:108
|
||||
#: src/views/settings/index.tsx:287
|
||||
msgid "Disconnect Trello"
|
||||
msgstr "Trello ontkoppelen"
|
||||
|
||||
@@ -631,7 +585,7 @@ msgstr "Trello ontkoppelen"
|
||||
msgid "Discuss and collaborate on cards."
|
||||
msgstr "Bespreek en werk samen aan kaarten."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:35
|
||||
#: src/views/settings/index.tsx:176
|
||||
msgid "Display name"
|
||||
msgstr "Weergavenaam"
|
||||
|
||||
@@ -665,7 +619,7 @@ msgstr "Docs"
|
||||
msgid "Documentation"
|
||||
msgstr "Documentatie"
|
||||
|
||||
#: src/views/auth/login/index.tsx:63
|
||||
#: src/views/auth/login/index.tsx:61
|
||||
msgid "Don't have an account? <0><1>Sign up</1></0>"
|
||||
msgstr "Heb je geen account? <0><1>Registreer je</1></0>"
|
||||
|
||||
@@ -697,11 +651,11 @@ msgstr "Werkruimte-URL bewerken"
|
||||
msgid "Editing"
|
||||
msgstr "Bewerken"
|
||||
|
||||
#: src/components/AuthForm.tsx:372
|
||||
#: src/components/AuthForm.tsx:368
|
||||
msgid "email"
|
||||
msgstr "e-mail"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:252
|
||||
#: src/views/members/components/InviteMemberForm.tsx:161
|
||||
msgid "Email"
|
||||
msgstr "E-mail"
|
||||
|
||||
@@ -717,11 +671,11 @@ msgstr "Voer je huidige wachtwoord in"
|
||||
msgid "Enter your current password and choose a new secure password."
|
||||
msgstr "Voer je huidige wachtwoord in en kies een nieuw veilig wachtwoord."
|
||||
|
||||
#: src/components/AuthForm.tsx:337
|
||||
#: src/components/AuthForm.tsx:333
|
||||
msgid "Enter your email address"
|
||||
msgstr "Voer je e-mailadres in"
|
||||
|
||||
#: src/components/AuthForm.tsx:325
|
||||
#: src/components/AuthForm.tsx:321
|
||||
msgid "Enter your name"
|
||||
msgstr "Voer je naam in"
|
||||
|
||||
@@ -729,26 +683,14 @@ msgstr "Voer je naam in"
|
||||
msgid "Enter your new password"
|
||||
msgstr "Voer je nieuwe wachtwoord in"
|
||||
|
||||
#: src/components/AuthForm.tsx:350
|
||||
#: src/components/AuthForm.tsx:346
|
||||
msgid "Enter your password"
|
||||
msgstr "Voer je wachtwoord in"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Error"
|
||||
msgstr "Fout"
|
||||
|
||||
#: src/views/settings/components/ChangePasswordConfirmation.tsx:89
|
||||
msgid "Error Changing Password"
|
||||
msgstr "Fout bij wijzigen wachtwoord"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:117
|
||||
msgid "Error creating invite link"
|
||||
msgstr "Fout bij het maken van uitnodigingslink"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:132
|
||||
msgid "Error deactivating invite link"
|
||||
msgstr "Fout bij het deactiveren van uitnodigingslink"
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:39
|
||||
msgid "Error deleting account"
|
||||
msgstr "Fout bij verwijderen account"
|
||||
@@ -761,12 +703,12 @@ msgstr "Fout bij het verwijderen van label"
|
||||
msgid "Error deleting workspace"
|
||||
msgstr "Fout bij verwijderen werkruimte"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:60
|
||||
#: src/views/settings/index.tsx:126
|
||||
msgid "Error disconnecting Trello"
|
||||
msgstr "Fout bij ontkoppelen van Trello"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:95
|
||||
#: src/views/members/components/InviteMemberForm.tsx:101
|
||||
#: src/views/members/components/InviteMemberForm.tsx:71
|
||||
#: src/views/members/components/InviteMemberForm.tsx:77
|
||||
msgid "Error inviting member"
|
||||
msgstr "Fout bij het uitnodigen van lid"
|
||||
|
||||
@@ -774,7 +716,7 @@ msgstr "Fout bij het uitnodigen van lid"
|
||||
msgid "Error updating display name"
|
||||
msgstr "Fout bij bijwerken weergavenaam"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:77
|
||||
#: src/views/settings/components/Avatar.tsx:39
|
||||
msgid "Error updating profile image"
|
||||
msgstr "Fout bij het bijwerken van profielafbeelding"
|
||||
|
||||
@@ -790,7 +732,7 @@ msgstr "Fout bij bijwerken naam werkruimte"
|
||||
msgid "Error updating workspace URL"
|
||||
msgstr "Fout bij het bijwerken van werkruimte-URL"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:221
|
||||
#: src/views/members/components/InviteMemberForm.tsx:130
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
|
||||
msgid "Error upgrading subscription"
|
||||
msgstr "Fout bij het upgraden van abonnement"
|
||||
@@ -799,8 +741,8 @@ msgstr "Fout bij het upgraden van abonnement"
|
||||
msgid "Error upgrading to Pro"
|
||||
msgstr "Fout bij upgraden naar Pro"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:91
|
||||
#: src/views/settings/components/Avatar.tsx:218
|
||||
#: src/views/settings/components/Avatar.tsx:56
|
||||
#: src/views/settings/components/Avatar.tsx:97
|
||||
msgid "Error uploading profile image"
|
||||
msgstr "Fout bij het uploaden van profielafbeelding"
|
||||
|
||||
@@ -816,16 +758,8 @@ msgstr "Alles wat je nodig hebt, voor altijd gratis. Onbeperkte borden, onbeperk
|
||||
msgid "Execution"
|
||||
msgstr "Uitvoering"
|
||||
|
||||
#: src/views/invite/index.tsx:41
|
||||
msgid "Failed to accept invitation. Please try again later, or contact customer support."
|
||||
msgstr "Uitnodiging accepteren mislukt. Probeer het later opnieuw of neem contact op met de klantenservice."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:197
|
||||
msgid "Failed to copy invite link"
|
||||
msgstr "Kopiëren van uitnodigingslink mislukt"
|
||||
|
||||
#. placeholder {0}: provider.at(0)?.toUpperCase() + provider.slice(1)
|
||||
#: src/components/AuthForm.tsx:273
|
||||
#: src/components/AuthForm.tsx:269
|
||||
msgid "Failed to login with {0}. Please try again."
|
||||
msgstr "Inloggen met {0} is mislukt. Probeer het opnieuw."
|
||||
|
||||
@@ -873,7 +807,7 @@ msgstr "Voor duurzaamheid op lange termijn erkennen we dat alle goede open sourc
|
||||
msgid "Free"
|
||||
msgstr "Gratis"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:312
|
||||
#: src/views/members/components/InviteMemberForm.tsx:193
|
||||
#: src/views/members/index.tsx:208
|
||||
msgid "Free Plan"
|
||||
msgstr "Gratis plan"
|
||||
@@ -890,7 +824,7 @@ msgstr "Fulltime"
|
||||
msgid "Fun"
|
||||
msgstr "Leuk"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:69
|
||||
#: src/views/auth/signup/index.tsx:67
|
||||
#: src/views/home/components/Cta.tsx:61
|
||||
#: src/views/home/components/Header.tsx:102
|
||||
#: src/views/home/components/Header.tsx:141
|
||||
@@ -930,13 +864,8 @@ msgstr "Aan de slag"
|
||||
msgid "GitHub"
|
||||
msgstr "GitHub"
|
||||
|
||||
#: src/views/invite/index.tsx:113
|
||||
msgid "Go Home"
|
||||
msgstr "Naar startpagina"
|
||||
|
||||
#: src/views/home/components/Header.tsx:96
|
||||
#: src/views/home/components/Header.tsx:133
|
||||
#: src/views/invite/index.tsx:144
|
||||
msgid "Go to app"
|
||||
msgstr "Naar de app"
|
||||
|
||||
@@ -988,7 +917,7 @@ msgstr "Ideeën"
|
||||
msgid "Ideas to improve this page..."
|
||||
msgstr "Ideeën om deze pagina te verbeteren..."
|
||||
|
||||
#: src/views/boards/index.tsx:43
|
||||
#: src/views/boards/index.tsx:38
|
||||
msgid "Import"
|
||||
msgstr "Importeren"
|
||||
|
||||
@@ -1026,7 +955,6 @@ msgstr "In behandeling"
|
||||
msgid "Individuals"
|
||||
msgstr "Individuen"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:57
|
||||
#: src/views/home/components/Features.tsx:114
|
||||
msgid "Integrations"
|
||||
msgstr "Integraties"
|
||||
@@ -1035,44 +963,27 @@ msgstr "Integraties"
|
||||
msgid "Interviewing"
|
||||
msgstr "Interviewen"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:49
|
||||
#: src/views/members/components/InviteMemberForm.tsx:40
|
||||
msgid "Invalid email address"
|
||||
msgstr "Ongeldig e-mailadres"
|
||||
|
||||
#: src/views/invite/index.tsx:105
|
||||
msgid "Invalid invitation"
|
||||
msgstr "Ongeldige uitnodiging"
|
||||
|
||||
#: src/views/members/index.tsx:221
|
||||
msgid "Invite"
|
||||
msgstr "Uitnodigen"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:190
|
||||
msgid "Invite link copied"
|
||||
msgstr "Uitnodigingslink gekopieerd"
|
||||
#: src/views/members/components/InviteMemberForm.tsx:208
|
||||
msgid "Invite another"
|
||||
msgstr "Nog iemand uitnodigen"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:191
|
||||
msgid "Invite link copied to clipboard"
|
||||
msgstr "Uitnodigingslink gekopieerd naar klembord"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:111
|
||||
#: src/views/members/components/InviteMemberForm.tsx:350
|
||||
#: src/views/card/components/MemberSelector.tsx:112
|
||||
#: src/views/members/components/InviteMemberForm.tsx:233
|
||||
msgid "Invite member"
|
||||
msgstr "Lid uitnodigen"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:315
|
||||
#: src/views/members/components/InviteMemberForm.tsx:196
|
||||
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
|
||||
msgstr "Voor het uitnodigen van leden is een teamplan vereist. Je wordt doorgestuurd om je werkruimte te upgraden."
|
||||
|
||||
#: src/views/invite/index.tsx:79
|
||||
#: src/views/invite/index.tsx:129
|
||||
msgid "Join workspace"
|
||||
msgstr "Deelnemen aan werkruimte"
|
||||
|
||||
#: src/views/invite/index.tsx:91
|
||||
msgid "Join workspace | kan.bn"
|
||||
msgstr "Deelnemen aan werkruimte | kan.bn"
|
||||
|
||||
#: src/views/boards/components/TemplateBoards.tsx:69
|
||||
msgid "Junior"
|
||||
msgstr "Junior"
|
||||
@@ -1100,7 +1011,7 @@ msgstr "Labels"
|
||||
msgid "Labels & Filters"
|
||||
msgstr "Labels & filters"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:224
|
||||
msgid "Language"
|
||||
msgstr "Taal"
|
||||
|
||||
@@ -1145,7 +1056,7 @@ msgstr "Lijst"
|
||||
msgid "List name"
|
||||
msgstr "Lijstnaam"
|
||||
|
||||
#: src/views/auth/login/index.tsx:33
|
||||
#: src/views/auth/login/index.tsx:31
|
||||
msgid "Login | kan.bn"
|
||||
msgstr "Login | kan.bn"
|
||||
|
||||
@@ -1161,7 +1072,7 @@ msgstr "Lange termijn"
|
||||
msgid "Low Priority"
|
||||
msgstr "Lage prioriteit"
|
||||
|
||||
#: src/components/AuthForm.tsx:373
|
||||
#: src/components/AuthForm.tsx:369
|
||||
msgid "magic link"
|
||||
msgstr "magische link"
|
||||
|
||||
@@ -1195,7 +1106,7 @@ msgstr "Leden | {0}"
|
||||
msgid "Monthly"
|
||||
msgstr "Maandelijks"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:147
|
||||
#: src/views/members/components/InviteMemberForm.tsx:93
|
||||
msgid "monthly billing"
|
||||
msgstr "maandelijkse facturering"
|
||||
|
||||
@@ -1218,14 +1129,10 @@ msgstr "Naam"
|
||||
msgid "Need help?"
|
||||
msgstr "Hulp nodig?"
|
||||
|
||||
#: src/views/boards/index.tsx:53
|
||||
#: src/views/boards/index.tsx:48
|
||||
msgid "New"
|
||||
msgstr "Nieuw"
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:147
|
||||
msgid "New API key"
|
||||
msgstr "Nieuwe API-sleutel"
|
||||
|
||||
#: src/views/boards/components/NewBoardForm.tsx:85
|
||||
msgid "New board"
|
||||
msgstr "Nieuw bord"
|
||||
@@ -1299,15 +1206,15 @@ msgstr "Aanbod"
|
||||
msgid "Onboarding"
|
||||
msgstr "Inwerktraject"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:55
|
||||
#: src/views/settings/index.tsx:349
|
||||
msgid "Once you delete your account, there is no going back. This action cannot be undone."
|
||||
msgstr "Zodra je je account verwijdert, is er geen weg terug. Deze actie kan niet ongedaan worden gemaakt."
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:99
|
||||
#: src/views/settings/index.tsx:312
|
||||
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
|
||||
msgstr "Zodra je je werkruimte verwijdert, is er geen weg terug. Deze actie kan niet ongedaan worden gemaakt."
|
||||
|
||||
#: src/components/AuthForm.tsx:315
|
||||
#: src/components/AuthForm.tsx:311
|
||||
msgid "or"
|
||||
msgstr "of"
|
||||
|
||||
@@ -1335,10 +1242,6 @@ msgstr "Wachtwoord moet minimaal 8 tekens bevatten"
|
||||
msgid "Passwords do not match"
|
||||
msgstr "Wachtwoorden komen niet overeen"
|
||||
|
||||
#: src/views/members/index.tsx:134
|
||||
msgid "Paused"
|
||||
msgstr "Gepauzeerd"
|
||||
|
||||
#: src/views/home/components/Pricing.tsx:102
|
||||
msgid "Payment frequency"
|
||||
msgstr "Betalingsfrequentie"
|
||||
@@ -1364,19 +1267,19 @@ msgstr "Planning"
|
||||
msgid "Please confirm your new password"
|
||||
msgstr "Bevestig je nieuwe wachtwoord"
|
||||
|
||||
#: src/components/AuthForm.tsx:341
|
||||
#: src/components/AuthForm.tsx:337
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Voer een geldig e-mailadres in"
|
||||
|
||||
#: src/components/AuthForm.tsx:329
|
||||
#: src/components/AuthForm.tsx:325
|
||||
msgid "Please enter a valid name"
|
||||
msgstr "Voer een geldige naam in"
|
||||
|
||||
#: src/components/AuthForm.tsx:354
|
||||
#: src/components/AuthForm.tsx:350
|
||||
msgid "Please enter a valid password"
|
||||
msgstr "Voer een geldig wachtwoord in"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:92
|
||||
#: src/views/settings/components/Avatar.tsx:57
|
||||
msgid "Please select a file to upload."
|
||||
msgstr "Selecteer een bestand om te uploaden."
|
||||
|
||||
@@ -1397,18 +1300,18 @@ msgstr "Selecteer een bestand om te uploaden."
|
||||
#: src/views/card/components/DeleteCardConfirmation.tsx:52
|
||||
#: src/views/card/components/DeleteChecklistConfirmation.tsx:37
|
||||
#: src/views/card/components/DeleteCommentConfirmation.tsx:45
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
#: src/views/card/components/LabelSelector.tsx:73
|
||||
#: src/views/card/components/ListSelector.tsx:53
|
||||
#: src/views/card/components/MemberSelector.tsx:80
|
||||
#: src/views/card/components/NewChecklistForm.tsx:70
|
||||
#: src/views/card/components/NewChecklistItemForm.tsx:89
|
||||
#: src/views/card/components/NewCommentForm.tsx:31
|
||||
#: src/views/card/index.tsx:173
|
||||
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
|
||||
#: src/views/members/components/InviteMemberForm.tsx:102
|
||||
#: src/views/members/components/InviteMemberForm.tsx:222
|
||||
#: src/views/settings/components/Avatar.tsx:78
|
||||
#: src/views/settings/components/Avatar.tsx:219
|
||||
#: src/views/members/components/InviteMemberForm.tsx:78
|
||||
#: src/views/members/components/InviteMemberForm.tsx:131
|
||||
#: src/views/settings/components/Avatar.tsx:40
|
||||
#: src/views/settings/components/Avatar.tsx:98
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:46
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:55
|
||||
@@ -1419,11 +1322,6 @@ msgstr "Selecteer een bestand om te uploaden."
|
||||
msgid "Please try again later, or contact customer support."
|
||||
msgstr "Probeer het later opnieuw of neem contact op met de klantenservice."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:118
|
||||
#: src/views/members/components/InviteMemberForm.tsx:133
|
||||
msgid "Please try again later."
|
||||
msgstr "Probeer het later opnieuw."
|
||||
|
||||
#: src/views/home/components/Footer.tsx:50
|
||||
#: src/views/home/components/Header.tsx:15
|
||||
#: src/views/home/components/Pricing.tsx:85
|
||||
@@ -1447,15 +1345,15 @@ msgstr "Privé"
|
||||
msgid "Pro Plan"
|
||||
msgstr "Pro Plan"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
msgid "Pro Plan ∞"
|
||||
msgstr "Pro Plan ∞"
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:64
|
||||
#: src/views/settings/components/Avatar.tsx:26
|
||||
msgid "Profile image updated"
|
||||
msgstr "Profielafbeelding bijgewerkt"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:29
|
||||
#: src/views/settings/index.tsx:171
|
||||
msgid "Profile picture"
|
||||
msgstr "Profielfoto"
|
||||
|
||||
@@ -1538,6 +1436,10 @@ msgstr "Bronnen"
|
||||
msgid "Review"
|
||||
msgstr "Beoordeling"
|
||||
|
||||
#: src/views/settings/components/CreateAPIKeyForm.tsx:49
|
||||
msgid "Revoke"
|
||||
msgstr "Intrekken"
|
||||
|
||||
#: src/views/home/components/Footer.tsx:36
|
||||
#: src/views/home/components/Header.tsx:13
|
||||
msgid "Roadmap"
|
||||
@@ -1552,7 +1454,6 @@ msgid "Run on your own infrastructure"
|
||||
msgstr "Draai op je eigen infrastructuur"
|
||||
|
||||
#: src/views/card/components/Comment.tsx:165
|
||||
#: src/views/settings/components/Avatar.tsx:309
|
||||
msgid "Save"
|
||||
msgstr "Opslaan"
|
||||
|
||||
@@ -1592,62 +1493,35 @@ msgstr "Feedback versturen"
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:82
|
||||
#: src/components/SideNavigation.tsx:78
|
||||
#: src/views/settings/index.tsx:165
|
||||
msgid "Settings"
|
||||
msgstr "Instellingen"
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:25
|
||||
msgid "Settings | Account"
|
||||
msgstr "Instellingen | Account"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:18
|
||||
msgid "Settings | API"
|
||||
msgstr "Instellingen | API"
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:35
|
||||
msgid "Settings | Billing"
|
||||
msgstr "Instellingen | Facturering"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:69
|
||||
msgid "Settings | Integrations"
|
||||
msgstr "Instellingen | Integraties"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:54
|
||||
msgid "Settings | Workspace"
|
||||
msgstr "Instellingen | Werkruimte"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:327
|
||||
msgid "Share invite link"
|
||||
msgstr "Uitnodigingslink delen"
|
||||
#. placeholder {0}: workspace.name ?? "Workspace"
|
||||
#: src/views/settings/index.tsx:161
|
||||
msgid "Settings | {0}"
|
||||
msgstr "Instellingen | {0}"
|
||||
|
||||
#: src/views/home/components/Header.tsx:100
|
||||
#: src/views/home/components/Header.tsx:138
|
||||
msgid "Sign in"
|
||||
msgstr "Inloggen"
|
||||
|
||||
#: src/views/invite/index.tsx:154
|
||||
msgid "Sign In"
|
||||
msgstr "Inloggen"
|
||||
|
||||
#: src/views/invite/index.tsx:162
|
||||
msgid "Sign Up"
|
||||
msgstr "Registreren"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:34
|
||||
#: src/views/auth/signup/index.tsx:59
|
||||
#: src/views/auth/signup/index.tsx:32
|
||||
#: src/views/auth/signup/index.tsx:57
|
||||
msgid "Sign up | kan.bn"
|
||||
msgstr "Registreren | kan.bn"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:44
|
||||
#: src/views/auth/signup/index.tsx:42
|
||||
msgid "Sign up disabled"
|
||||
msgstr "Registreren uitgeschakeld"
|
||||
|
||||
#: src/views/auth/signup/index.tsx:47
|
||||
#: src/views/auth/signup/index.tsx:45
|
||||
msgid "Sign up is currently disabled. Please try again later."
|
||||
msgstr "Registreren is momenteel uitgeschakeld. Probeer het later opnieuw."
|
||||
|
||||
#: src/components/AuthForm.tsx:370
|
||||
#: src/components/AuthForm.tsx:366
|
||||
msgid "Sign up with "
|
||||
msgstr "Registreren met "
|
||||
|
||||
@@ -1671,8 +1545,8 @@ msgstr "Softwareontwikkeling"
|
||||
msgid "Star on Github"
|
||||
msgstr "Star op Github"
|
||||
|
||||
#: src/components/AuthForm.tsx:207
|
||||
#: src/components/AuthForm.tsx:224
|
||||
#: src/components/AuthForm.tsx:203
|
||||
#: src/components/AuthForm.tsx:220
|
||||
msgid "Success"
|
||||
msgstr "Geslaagd"
|
||||
|
||||
@@ -1692,7 +1566,7 @@ msgstr "Ondersteun de ontwikkeling van het project"
|
||||
msgid "System"
|
||||
msgstr "Systeem"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:301
|
||||
#: src/views/members/components/InviteMemberForm.tsx:182
|
||||
#: src/views/members/index.tsx:207
|
||||
msgid "Team Plan"
|
||||
msgstr "Teamplan"
|
||||
@@ -1745,10 +1619,6 @@ msgstr "Ze zullen geen toegang meer hebben tot deze werkruimte."
|
||||
msgid "This action can't be undone."
|
||||
msgstr "Deze actie kan niet ongedaan worden gemaakt."
|
||||
|
||||
#: src/views/settings/components/NewApiKeyModal.tsx:129
|
||||
msgid "This API key will only be shown once. Please save it in a secure location."
|
||||
msgstr "Deze API-sleutel wordt slechts één keer getoond. Bewaar deze op een veilige plaats."
|
||||
|
||||
#: src/views/public/board/index.tsx:151
|
||||
msgid "This board is private or does not exist"
|
||||
msgstr "Dit bord is privé of bestaat niet"
|
||||
@@ -1757,10 +1627,6 @@ msgstr "Dit bord is privé of bestaat niet"
|
||||
msgid "This board URL has already been taken"
|
||||
msgstr "Deze board URL is al in gebruik"
|
||||
|
||||
#: src/views/invite/index.tsx:108
|
||||
msgid "This invitation link is invalid or has expired."
|
||||
msgstr "Deze uitnodigingslink is ongeldig of verlopen."
|
||||
|
||||
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:70
|
||||
msgid "This will result in the permanent deletion of all data associated with this workspace."
|
||||
msgstr "Dit zal resulteren in het permanent verwijderen van alle gegevens die aan deze werkruimte zijn gekoppeld."
|
||||
@@ -1794,11 +1660,7 @@ msgstr "Menu in-/uitschakelen"
|
||||
msgid "Track all card changes with detailed activity history."
|
||||
msgstr "Volg alle kaartwijzigingen met gedetailleerde activiteitengeschiedenis."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:73
|
||||
msgid "Trello"
|
||||
msgstr "Trello"
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:53
|
||||
#: src/views/settings/index.tsx:119
|
||||
msgid "Trello disconnected"
|
||||
msgstr "Trello ontkoppeld"
|
||||
|
||||
@@ -1883,16 +1745,16 @@ msgstr "Kan checklistitem niet bijwerken"
|
||||
msgid "Unable to update comment"
|
||||
msgstr "Kan reactie niet bijwerken"
|
||||
|
||||
#: src/views/card/components/LabelSelector.tsx:71
|
||||
#: src/views/card/components/LabelSelector.tsx:72
|
||||
msgid "Unable to update labels"
|
||||
msgstr "Kan labels niet bijwerken"
|
||||
|
||||
#: src/views/board/index.tsx:133
|
||||
#: src/views/card/components/ListSelector.tsx:51
|
||||
#: src/views/card/components/ListSelector.tsx:52
|
||||
msgid "Unable to update list"
|
||||
msgstr "Kan lijst niet bijwerken"
|
||||
|
||||
#: src/views/card/components/MemberSelector.tsx:78
|
||||
#: src/views/card/components/MemberSelector.tsx:79
|
||||
msgid "Unable to update members"
|
||||
msgstr "Kan leden niet bijwerken"
|
||||
|
||||
@@ -1935,9 +1797,9 @@ msgid "Unlimited members"
|
||||
msgstr "Onbeperkt aantal leden"
|
||||
|
||||
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
|
||||
#: src/views/settings/components/UpdateDisplayNameForm.tsx:79
|
||||
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:90
|
||||
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:82
|
||||
#: src/views/settings/components/UpdateWorkspaceUrlForm.tsx:154
|
||||
msgid "Update"
|
||||
msgstr "Bijwerken"
|
||||
@@ -1968,7 +1830,7 @@ msgid "Upgrade"
|
||||
msgstr "Upgraden"
|
||||
|
||||
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
|
||||
#: src/views/settings/WorkspaceSettings.tsx:89
|
||||
#: src/views/settings/index.tsx:216
|
||||
msgid "Upgrade to Pro"
|
||||
msgstr "Upgraden naar Pro"
|
||||
|
||||
@@ -1976,7 +1838,7 @@ msgstr "Upgraden naar Pro"
|
||||
msgid "Upgrade to Pro ($29/month)"
|
||||
msgstr "Upgrade naar Pro ($29/maand)"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:341
|
||||
#: src/views/members/components/InviteMemberForm.tsx:224
|
||||
msgid "Upgrade to Team Plan"
|
||||
msgstr "Upgraden naar teamplan"
|
||||
|
||||
@@ -2008,7 +1870,7 @@ msgstr "Sjabloon gebruiken"
|
||||
msgid "User"
|
||||
msgstr "Gebruiker"
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:96
|
||||
#: src/views/members/components/InviteMemberForm.tsx:72
|
||||
msgid "User is already a member of this workspace"
|
||||
msgstr "Gebruiker is al lid van deze werkruimte"
|
||||
|
||||
@@ -2016,11 +1878,11 @@ msgstr "Gebruiker is al lid van deze werkruimte"
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
#: src/views/settings/ApiSettings.tsx:25
|
||||
#: src/views/settings/index.tsx:299
|
||||
msgid "View and manage your API keys."
|
||||
msgstr "Bekijk en beheer je API-sleutels."
|
||||
|
||||
#: src/views/settings/BillingSettings.tsx:42
|
||||
#: src/views/settings/index.tsx:238
|
||||
msgid "View and manage your billing and subscription."
|
||||
msgstr "Bekijk en beheer je facturering en abonnement."
|
||||
|
||||
@@ -2052,7 +1914,7 @@ msgstr "We gebruiken de <0>AGPL-3.0 licentie</0>."
|
||||
msgid "We're just getting started. "
|
||||
msgstr "We zijn nog maar net begonnen. "
|
||||
|
||||
#: src/views/auth/login/index.tsx:43
|
||||
#: src/views/auth/login/index.tsx:41
|
||||
msgid "Welcome back"
|
||||
msgstr "Welkom terug"
|
||||
|
||||
@@ -2072,7 +1934,6 @@ msgstr "Toen Trello in 2011 werd gelanceerd, blies het iedereen omver met zijn z
|
||||
msgid "Why make an open source Trello?"
|
||||
msgstr "Waarom een open source Trello maken?"
|
||||
|
||||
#: src/components/SettingsLayout.tsx:39
|
||||
#: src/views/board/index.tsx:331
|
||||
msgid "Workspace"
|
||||
msgstr "Werkruimte"
|
||||
@@ -2085,7 +1946,7 @@ msgstr "Werkruimte succesvol aangemaakt. Je kunt later upgraden in de instelling
|
||||
msgid "Workspace deleted"
|
||||
msgstr "Werkruimte verwijderd"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:75
|
||||
#: src/views/settings/index.tsx:202
|
||||
msgid "Workspace description"
|
||||
msgstr "Werkruimte beschrijving"
|
||||
|
||||
@@ -2107,7 +1968,7 @@ msgid "Workspace members"
|
||||
msgstr "Werkruimteleden"
|
||||
|
||||
#: src/components/NewWorkspaceForm.tsx:259
|
||||
#: src/views/settings/WorkspaceSettings.tsx:58
|
||||
#: src/views/settings/index.tsx:183
|
||||
msgid "Workspace name"
|
||||
msgstr "Naam werkruimte"
|
||||
|
||||
@@ -2131,7 +1992,7 @@ msgstr "Werkruimtenaam bijgewerkt"
|
||||
msgid "Workspace slug updated"
|
||||
msgstr "Werkruimte-slug bijgewerkt"
|
||||
|
||||
#: src/views/settings/WorkspaceSettings.tsx:66
|
||||
#: src/views/settings/index.tsx:192
|
||||
msgid "Workspace URL"
|
||||
msgstr "Werkruimte URL"
|
||||
|
||||
@@ -2151,7 +2012,7 @@ msgstr "Jaarlijks"
|
||||
msgid "Yes, we offer an forever free plan for individual use. No restrictions, no paywalls, no limits."
|
||||
msgstr "Ja, we bieden een voor altijd gratis plan voor individueel gebruik. Geen beperkingen, geen betaalmuren, geen limieten."
|
||||
|
||||
#: src/views/settings/AccountSettings.tsx:73
|
||||
#: src/views/settings/index.tsx:331
|
||||
msgid "You are about to change your password."
|
||||
msgstr "Je staat op het punt je wachtwoord te wijzigen."
|
||||
|
||||
@@ -2167,26 +2028,18 @@ msgstr "Je kunt teamleden uitnodigen door op de knop \"Uitnodigen\" in de rechte
|
||||
msgid "You can self-host by following the instructions in our <0>repo</0>."
|
||||
msgstr "Je kunt zelf hosten door de instructies in onze <0>repo</0> te volgen."
|
||||
|
||||
#: src/components/AuthForm.tsx:225
|
||||
#: src/components/AuthForm.tsx:221
|
||||
msgid "You have been logged in successfully."
|
||||
msgstr "Je bent succesvol ingelogd."
|
||||
|
||||
#: src/components/AuthForm.tsx:208
|
||||
#: src/components/AuthForm.tsx:204
|
||||
msgid "You have been signed up successfully."
|
||||
msgstr "Je bent succesvol geregistreerd."
|
||||
|
||||
#: src/views/members/components/InviteMemberForm.tsx:305
|
||||
#: src/views/members/components/InviteMemberForm.tsx:186
|
||||
msgid "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
|
||||
msgstr "Je hebt onbeperkte plaatsen met je Pro Plan. Er zijn geen extra kosten voor nieuwe leden!"
|
||||
|
||||
#: src/views/invite/index.tsx:134
|
||||
msgid "You've been invited to join a workspace on kan.bn."
|
||||
msgstr "Je bent uitgenodigd om deel te nemen aan een werkruimte op kan.bn."
|
||||
|
||||
#: src/views/invite/index.tsx:135
|
||||
msgid "You've been invited to join a workspace."
|
||||
msgstr "Je bent uitgenodigd om deel te nemen aan een werkruimte."
|
||||
|
||||
#: src/views/settings/components/DeleteAccountConfirmation.tsx:28
|
||||
msgid "Your account has been deleted."
|
||||
msgstr "Je account is verwijderd."
|
||||
@@ -2203,15 +2056,15 @@ msgstr "Je weergavenaam is bijgewerkt."
|
||||
msgid "Your password has been changed."
|
||||
msgstr "Je wachtwoord is gewijzigd."
|
||||
|
||||
#: src/views/settings/components/Avatar.tsx:65
|
||||
#: src/views/settings/components/Avatar.tsx:27
|
||||
msgid "Your profile image has been updated."
|
||||
msgstr "Je profielafbeelding is bijgewerkt."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:54
|
||||
#: src/views/settings/index.tsx:120
|
||||
msgid "Your Trello account has been disconnected."
|
||||
msgstr "Je Trello-account is ontkoppeld."
|
||||
|
||||
#: src/views/settings/IntegrationsSettings.tsx:102
|
||||
#: src/views/settings/index.tsx:281
|
||||
msgid "Your Trello account is connected."
|
||||
msgstr "Je Trello-account is verbonden."
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -7,7 +7,6 @@ import type { ReactElement, ReactNode } from "react";
|
||||
import { Plus_Jakarta_Sans } from "next/font/google";
|
||||
import Script from "next/script";
|
||||
import { env } from "next-runtime-env";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider } from "posthog-js/react";
|
||||
import { useEffect } from "react";
|
||||
@@ -15,6 +14,7 @@ import { useEffect } from "react";
|
||||
import { LinguiProviderWrapper } from "~/providers/lingui";
|
||||
import { ModalProvider } from "~/providers/modal";
|
||||
import { PopupProvider } from "~/providers/popup";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
const jakarta = Plus_Jakarta_Sans({
|
||||
@@ -82,8 +82,8 @@ const MyApp: AppType = ({ Component, pageProps }: AppPropsWithLayout) => {
|
||||
<main className="font-sans">
|
||||
<LinguiProviderWrapper>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<ModalProvider>
|
||||
<PopupProvider>
|
||||
<ModalProvider>
|
||||
<PopupProvider>
|
||||
{posthogKey ? (
|
||||
<PostHogProvider client={posthog}>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
@@ -91,8 +91,8 @@ const MyApp: AppType = ({ Component, pageProps }: AppPropsWithLayout) => {
|
||||
) : (
|
||||
getLayout(<Component {...pageProps} />)
|
||||
)}
|
||||
</PopupProvider>
|
||||
</ModalProvider>
|
||||
</PopupProvider>
|
||||
</ModalProvider>
|
||||
</ThemeProvider>
|
||||
</LinguiProviderWrapper>
|
||||
</main>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import InviteView from "~/views/invite";
|
||||
|
||||
export default function InvitePage() {
|
||||
return <InviteView />;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { NextPageWithLayout } from "~/pages/_app";
|
||||
import { getDashboardLayout } from "~/components/Dashboard";
|
||||
import { SettingsLayout } from "~/components/SettingsLayout";
|
||||
import AccountSettings from "~/views/settings/AccountSettings";
|
||||
|
||||
const AccountSettingsPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<SettingsLayout currentTab="account">
|
||||
<AccountSettings />
|
||||
</SettingsLayout>
|
||||
);
|
||||
};
|
||||
|
||||
AccountSettingsPage.getLayout = (page) => getDashboardLayout(page);
|
||||
|
||||
export default AccountSettingsPage;
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { NextPageWithLayout } from "~/pages/_app";
|
||||
import { getDashboardLayout } from "~/components/Dashboard";
|
||||
import { SettingsLayout } from "~/components/SettingsLayout";
|
||||
import ApiSettings from "~/views/settings/ApiSettings";
|
||||
|
||||
const ApiSettingsPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<SettingsLayout currentTab="api">
|
||||
<ApiSettings />
|
||||
</SettingsLayout>
|
||||
);
|
||||
};
|
||||
|
||||
ApiSettingsPage.getLayout = (page) => getDashboardLayout(page);
|
||||
|
||||
export default ApiSettingsPage;
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { NextPageWithLayout } from "~/pages/_app";
|
||||
import { getDashboardLayout } from "~/components/Dashboard";
|
||||
import { SettingsLayout } from "~/components/SettingsLayout";
|
||||
import BillingSettings from "~/views/settings/BillingSettings";
|
||||
|
||||
const BillingSettingsPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<SettingsLayout currentTab="billing">
|
||||
<BillingSettings />
|
||||
</SettingsLayout>
|
||||
);
|
||||
};
|
||||
|
||||
BillingSettingsPage.getLayout = (page) => getDashboardLayout(page);
|
||||
|
||||
export default BillingSettingsPage;
|
||||
17
apps/web/src/pages/settings/index.tsx
Normal file
17
apps/web/src/pages/settings/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { NextPageWithLayout } from "~/pages/_app";
|
||||
import { getDashboardLayout } from "~/components/Dashboard";
|
||||
import Popup from "~/components/Popup";
|
||||
import SettingsView from "~/views/settings";
|
||||
|
||||
const SettingsPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingsView />
|
||||
<Popup />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
SettingsPage.getLayout = (page) => getDashboardLayout(page);
|
||||
|
||||
export default SettingsPage;
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { NextPageWithLayout } from "~/pages/_app";
|
||||
import { getDashboardLayout } from "~/components/Dashboard";
|
||||
import { SettingsLayout } from "~/components/SettingsLayout";
|
||||
import IntegrationsSettings from "~/views/settings/IntegrationsSettings";
|
||||
|
||||
const IntegrationsSettingsPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<SettingsLayout currentTab="integrations">
|
||||
<IntegrationsSettings />
|
||||
</SettingsLayout>
|
||||
);
|
||||
};
|
||||
|
||||
IntegrationsSettingsPage.getLayout = (page) => getDashboardLayout(page);
|
||||
|
||||
export default IntegrationsSettingsPage;
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { NextPageWithLayout } from "~/pages/_app";
|
||||
import { getDashboardLayout } from "~/components/Dashboard";
|
||||
import { SettingsLayout } from "~/components/SettingsLayout";
|
||||
import WorkspaceSettings from "~/views/settings/WorkspaceSettings";
|
||||
|
||||
const WorkspaceSettingsPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<SettingsLayout currentTab="workspace">
|
||||
<WorkspaceSettings />
|
||||
</SettingsLayout>
|
||||
);
|
||||
};
|
||||
|
||||
WorkspaceSettingsPage.getLayout = (page) => getDashboardLayout(page);
|
||||
|
||||
export default WorkspaceSettingsPage;
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useRouter } from "next/navigation";
|
||||
import React, { createContext, useContext, useEffect, useState } from "react";
|
||||
|
||||
import { api } from "~/utils/api";
|
||||
@@ -46,8 +46,6 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
|
||||
);
|
||||
const [hasLoaded, setHasLoaded] = useState(false);
|
||||
|
||||
const workspacePublicId = useSearchParams().get("workspacePublicId");
|
||||
|
||||
const { data, isLoading } = api.workspace.all.useQuery();
|
||||
const utils = api.useUtils();
|
||||
|
||||
@@ -69,7 +67,7 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
|
||||
}
|
||||
|
||||
const storedWorkspaceId: string | null =
|
||||
workspacePublicId ?? localStorage.getItem("workspacePublicId");
|
||||
localStorage.getItem("workspacePublicId");
|
||||
|
||||
if (data.length) {
|
||||
const workspaces = data.map(({ workspace, role }) => ({
|
||||
@@ -101,11 +99,6 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
|
||||
description: selectedWorkspace.workspace.description,
|
||||
role: selectedWorkspace.role,
|
||||
});
|
||||
|
||||
if (workspacePublicId) {
|
||||
router.push(`/boards`);
|
||||
localStorage.setItem("workspacePublicId", workspacePublicId);
|
||||
}
|
||||
} else {
|
||||
const primaryWorkspace = data[0]?.workspace;
|
||||
const primaryWorkspaceRole = data[0]?.role;
|
||||
@@ -121,7 +114,7 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
|
||||
role: primaryWorkspaceRole,
|
||||
});
|
||||
}
|
||||
}, [data, isLoading, workspacePublicId, router]);
|
||||
}, [data, isLoading]);
|
||||
|
||||
return (
|
||||
<WorkspaceContext.Provider
|
||||
|
||||
@@ -18,8 +18,6 @@ const loadMessages = async (locale: Locale) => {
|
||||
return (await import("~/locales/it/messages")).messages;
|
||||
case "nl":
|
||||
return (await import("~/locales/nl/messages")).messages;
|
||||
case "ru":
|
||||
return (await import("~/locales/ru/messages")).messages;
|
||||
default:
|
||||
return enMessages;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
@@ -17,8 +17,6 @@ export default function LoginPage() {
|
||||
const [isMagicLinkSent, setIsMagicLinkSent] = useState<boolean>(false);
|
||||
const [magicLinkRecipient, setMagicLinkRecipient] = useState<string>("");
|
||||
|
||||
const redirect = useSearchParams().get("next");
|
||||
|
||||
const handleMagicLinkSent = (value: boolean, recipient: string) => {
|
||||
setIsMagicLinkSent(value);
|
||||
setMagicLinkRecipient(recipient);
|
||||
@@ -63,11 +61,7 @@ export default function LoginPage() {
|
||||
<Trans>
|
||||
Don't have an account?{" "}
|
||||
<span className="underline">
|
||||
<Link
|
||||
href={redirect ? `/signup?next=${redirect}` : "/signup"}
|
||||
>
|
||||
Sign up
|
||||
</Link>
|
||||
<Link href="/signup">Sign up</Link>
|
||||
</span>
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
@@ -17,8 +17,6 @@ export default function SignUpPage() {
|
||||
const [isMagicLinkSent, setIsMagicLinkSent] = useState<boolean>(false);
|
||||
const [magicLinkRecipient, setMagicLinkRecipient] = useState<string>("");
|
||||
|
||||
const redirect = useSearchParams().get("next");
|
||||
|
||||
const { data } = authClient.useSession();
|
||||
|
||||
if (data?.user.id) router.push("/boards");
|
||||
@@ -88,9 +86,7 @@ export default function SignUpPage() {
|
||||
<Trans>
|
||||
Already have an account?{" "}
|
||||
<span className="underline">
|
||||
<Link href={redirect ? `/login?next=${redirect}` : "/login"}>
|
||||
Sign in
|
||||
</Link>
|
||||
<Link href="/login">Sign in</Link>
|
||||
</span>
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
@@ -155,7 +155,7 @@ export function UpdateBoardSlugForm({
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
href="/settings?tab=workspace"
|
||||
href="/settings?edit=workspace_url"
|
||||
onClick={closeModal}
|
||||
>
|
||||
{t`Edit workspace URL`}
|
||||
|
||||
@@ -19,7 +19,7 @@ export function BoardsList() {
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<div className="3xl:grid-cols-4 grid h-fit w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-3">
|
||||
<div className="grid h-fit w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-7">
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
<div className="mr-5 flex h-[150px] w-full animate-pulse rounded-md bg-light-200 dark:bg-dark-100" />
|
||||
@@ -45,7 +45,7 @@ export function BoardsList() {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="3xl:grid-cols-4 grid h-fit w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-3">
|
||||
<div className="grid h-fit w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-7">
|
||||
{data?.map((board) => (
|
||||
<Link key={board.publicId} href={`boards/${board.publicId}`}>
|
||||
<div className="align-center relative mr-5 flex h-[150px] w-full items-center justify-center rounded-md border border-dashed border-light-400 bg-light-50 shadow-sm hover:bg-light-200 dark:border-dark-600 dark:bg-dark-50 dark:hover:bg-dark-100">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Menu } from "@headlessui/react";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { HiMiniPlus } from "react-icons/hi2";
|
||||
|
||||
@@ -100,19 +101,22 @@ export default function LabelSelector({
|
||||
{selectedLabels.length ? (
|
||||
<div className="flex flex-wrap gap-x-0.5">
|
||||
{selectedLabels.map((label) => (
|
||||
<Badge
|
||||
key={label.key}
|
||||
value={label.value}
|
||||
iconLeft={label.leftIcon}
|
||||
/>
|
||||
<Menu.Button key={label.key}>
|
||||
<Badge value={label.value} iconLeft={label.leftIcon} />
|
||||
</Menu.Button>
|
||||
))}
|
||||
<Badge value={t`Add label`} iconLeft={<HiMiniPlus size={14} />} />
|
||||
<Menu.Button>
|
||||
<Badge
|
||||
value={t`Add label`}
|
||||
iconLeft={<HiMiniPlus size={14} />}
|
||||
/>
|
||||
</Menu.Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 pl-2 text-left text-sm text-neutral-900 hover:border-light-300 hover:bg-light-200 dark:border-dark-50 dark:text-dark-1000 dark:hover:border-dark-200 dark:hover:bg-dark-100">
|
||||
<Menu.Button className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 pl-2 text-left text-sm text-neutral-900 hover:border-light-300 hover:bg-light-200 dark:border-dark-50 dark:text-dark-1000 dark:hover:border-dark-200 dark:hover:bg-dark-100">
|
||||
<HiMiniPlus size={22} className="pr-2" />
|
||||
{t`Add label`}
|
||||
</div>
|
||||
</Menu.Button>
|
||||
)}
|
||||
</CheckboxDropdown>
|
||||
)}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Menu } from "@headlessui/react";
|
||||
import { t } from "@lingui/core/macro";
|
||||
|
||||
import CheckboxDropdown from "~/components/CheckboxDropdown";
|
||||
@@ -78,9 +79,9 @@ export default function ListSelector({
|
||||
}}
|
||||
asChild
|
||||
>
|
||||
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 py-1 pl-2 text-left text-sm text-neutral-900 hover:border-light-300 hover:bg-light-200 dark:border-dark-50 dark:text-dark-1000 dark:hover:border-dark-200 dark:hover:bg-dark-100">
|
||||
<Menu.Button className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 py-1 pl-2 text-left text-sm text-neutral-900 hover:border-light-300 hover:bg-light-200 dark:border-dark-50 dark:text-dark-1000 dark:hover:border-dark-200 dark:hover:bg-dark-100">
|
||||
{selectedList?.value}
|
||||
</div>
|
||||
</Menu.Button>
|
||||
</CheckboxDropdown>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { Menu } from "@headlessui/react";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { HiMiniPlus } from "react-icons/hi2";
|
||||
|
||||
@@ -111,12 +112,11 @@ export default function MemberSelector({
|
||||
createNewItemLabel={t`Invite member`}
|
||||
asChild
|
||||
>
|
||||
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 py-1 pl-2 text-left text-sm text-neutral-900 hover:border-light-300 hover:bg-light-200 dark:border-dark-50 dark:text-dark-1000 dark:hover:border-dark-200 dark:hover:bg-dark-100">
|
||||
<Menu.Button className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 py-1 pl-2 text-left text-sm text-neutral-900 hover:border-light-300 hover:bg-light-200 dark:border-dark-50 dark:text-dark-1000 dark:hover:border-dark-200 dark:hover:bg-dark-100">
|
||||
{selectedMembers.length ? (
|
||||
<div className="isolate flex justify-end -space-x-1 overflow-hidden">
|
||||
{selectedMembers.map(({ value, imageUrl }) => (
|
||||
<Avatar
|
||||
key={value}
|
||||
size="sm"
|
||||
name={value}
|
||||
imageUrl={imageUrl}
|
||||
@@ -130,7 +130,7 @@ export default function MemberSelector({
|
||||
{t`Add member`}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Menu.Button>
|
||||
</CheckboxDropdown>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { authClient } from "@kan/auth/client";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import LoadingSpinner from "~/components/LoadingSpinner";
|
||||
import { PageHead } from "~/components/PageHead";
|
||||
import PatternedBackground from "~/components/PatternedBackground";
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
export default function InvitePage() {
|
||||
const router = useRouter();
|
||||
const { code } = router.query;
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { data: session, isPending: isSessionLoading } =
|
||||
authClient.useSession();
|
||||
|
||||
const isCloudEnv = env("NEXT_PUBLIC_KAN_ENV") === "cloud";
|
||||
|
||||
const inviteCode = Array.isArray(code) ? code[0] : code;
|
||||
|
||||
const acceptInviteMutation = api.member.acceptInviteLink.useMutation({
|
||||
onSuccess: (result) => {
|
||||
if (result.success) {
|
||||
return router.push(
|
||||
`/boards?workspacePublicId=${result.workspacePublicId}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
if (error.data?.code === "CONFLICT") {
|
||||
return router.push(`/boards`);
|
||||
}
|
||||
|
||||
setError(
|
||||
error.message ||
|
||||
t`Failed to accept invitation. Please try again later, or contact customer support.`,
|
||||
);
|
||||
setIsProcessing(false);
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
data: inviteInfo,
|
||||
isLoading: isInviteInfoLoading,
|
||||
isError: isInviteInfoError,
|
||||
} = api.member.getInviteByCode.useQuery(
|
||||
{ inviteCode: inviteCode ?? "" },
|
||||
{
|
||||
enabled: !!inviteCode,
|
||||
retry: false,
|
||||
},
|
||||
);
|
||||
|
||||
// Auto accept invite if user is logged in
|
||||
useEffect(() => {
|
||||
if (session?.user.id && inviteCode && inviteInfo && !error) {
|
||||
setIsProcessing(true);
|
||||
setError(null);
|
||||
|
||||
acceptInviteMutation.mutate({
|
||||
inviteCode,
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [session?.user.id, inviteCode, inviteInfo, error]);
|
||||
|
||||
if (
|
||||
!isInviteInfoError &&
|
||||
!error &&
|
||||
(session?.user.id || isInviteInfoLoading || isSessionLoading)
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<PageHead title={t`Join workspace`} />
|
||||
<PatternedBackground />
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<LoadingSpinner size="lg" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const PageWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<>
|
||||
<PageHead title={t`Join workspace | kan.bn`} />
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (isInviteInfoError || (!isInviteInfoLoading && !inviteInfo)) {
|
||||
return (
|
||||
<PageWrapper>
|
||||
<div className="relative flex min-h-screen items-center justify-center px-4 py-12 sm:px-6 lg:px-8">
|
||||
<PatternedBackground />
|
||||
<div className="z-10 w-full max-w-md space-y-8">
|
||||
<div>
|
||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-light-1000 dark:text-dark-1000">
|
||||
{t`Invalid invitation`}
|
||||
</h2>
|
||||
<p className="mt-4 text-center text-sm text-light-900 dark:text-dark-800">
|
||||
{t`This invitation link is invalid or has expired.`}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<Button href="/" variant="primary">
|
||||
{t`Go Home`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageWrapper>
|
||||
<div className="relative flex min-h-screen items-center justify-center px-4 py-12 sm:px-6 lg:px-8">
|
||||
<PatternedBackground />
|
||||
<div className="z-10 w-full max-w-[400px] space-y-8">
|
||||
<div>
|
||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-light-1000 dark:text-dark-1000">
|
||||
{t`Join workspace`}
|
||||
</h2>
|
||||
{!error ? (
|
||||
<p className="mt-4 text-center text-sm text-light-900 dark:text-dark-800">
|
||||
{isCloudEnv
|
||||
? t`You've been invited to join a workspace on kan.bn.`
|
||||
: t`You've been invited to join a workspace.`}
|
||||
</p>
|
||||
) : (
|
||||
<p className="mt-4 text-center text-sm text-red-500">{error}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-center gap-2">
|
||||
{session?.user.id ? (
|
||||
<Button href={`/boards`} variant="primary" size="md">
|
||||
{t`Go to app`}
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
href={`/login?next=/invite/${inviteCode}`}
|
||||
disabled={isProcessing}
|
||||
variant="primary"
|
||||
size="md"
|
||||
>
|
||||
{t`Sign In`}
|
||||
</Button>
|
||||
<Button
|
||||
href={`/signup?next=/invite/${inviteCode}`}
|
||||
disabled={isProcessing}
|
||||
variant="primary"
|
||||
size="md"
|
||||
>
|
||||
{t`Sign Up`}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageWrapper>
|
||||
);
|
||||
}
|
||||
@@ -3,12 +3,7 @@ import { t } from "@lingui/core/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import {
|
||||
HiInformationCircle,
|
||||
HiMiniCheck,
|
||||
HiOutlineDocumentDuplicate,
|
||||
HiXMark,
|
||||
} from "react-icons/hi2";
|
||||
import { HiXMark } from "react-icons/hi2";
|
||||
import { z } from "zod";
|
||||
|
||||
import type { InviteMemberInput } from "@kan/api/types";
|
||||
@@ -36,11 +31,7 @@ export function InviteMemberForm({
|
||||
userId: string | undefined;
|
||||
}) {
|
||||
const utils = api.useUtils();
|
||||
const [isShareInviteLinkEnabled, setIsShareInviteLinkEnabled] =
|
||||
useState(false);
|
||||
const [inviteLink, setInviteLink] = useState<string>("");
|
||||
const [isLoadingInviteLink, setIsLoadingInviteLink] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [isCreateAnotherEnabled, setIsCreateAnotherEnabled] = useState(false);
|
||||
const { closeModal } = useModal();
|
||||
const { workspace } = useWorkspace();
|
||||
const { showPopup } = usePopup();
|
||||
@@ -65,21 +56,6 @@ export function InviteMemberForm({
|
||||
|
||||
const refetchBoards = () => utils.board.all.refetch();
|
||||
|
||||
// Fetch active invite link on component mount
|
||||
const { data: activeInviteLink, refetch: refetchInviteLink } =
|
||||
api.member.getActiveInviteLink.useQuery(
|
||||
{ workspacePublicId: workspace.publicId || "" },
|
||||
{ enabled: !!workspace.publicId },
|
||||
);
|
||||
|
||||
// Set initial state based on active invite link
|
||||
useEffect(() => {
|
||||
if (activeInviteLink) {
|
||||
setIsShareInviteLinkEnabled(activeInviteLink.isActive);
|
||||
setInviteLink(activeInviteLink.inviteLink || "");
|
||||
}
|
||||
}, [activeInviteLink]);
|
||||
|
||||
const inviteMember = api.member.invite.useMutation({
|
||||
onSuccess: async () => {
|
||||
closeModal();
|
||||
@@ -88,7 +64,7 @@ export function InviteMemberForm({
|
||||
},
|
||||
onError: (error) => {
|
||||
reset();
|
||||
if (!isShareInviteLinkEnabled) closeModal();
|
||||
if (!isCreateAnotherEnabled) closeModal();
|
||||
|
||||
if (error.data?.code === "CONFLICT") {
|
||||
showPopup({
|
||||
@@ -106,36 +82,6 @@ export function InviteMemberForm({
|
||||
},
|
||||
});
|
||||
|
||||
const createInviteLink = api.member.createInviteLink.useMutation({
|
||||
onSuccess: (data) => {
|
||||
setInviteLink(data.inviteLink);
|
||||
setIsLoadingInviteLink(false);
|
||||
},
|
||||
onError: () => {
|
||||
setIsLoadingInviteLink(false);
|
||||
showPopup({
|
||||
header: t`Error creating invite link`,
|
||||
message: t`Please try again later.`,
|
||||
icon: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const deactivateInviteLink = api.member.deactivateInviteLink.useMutation({
|
||||
onSuccess: () => {
|
||||
setInviteLink("");
|
||||
setIsLoadingInviteLink(false);
|
||||
},
|
||||
onError: () => {
|
||||
setIsLoadingInviteLink(false);
|
||||
showPopup({
|
||||
header: t`Error deactivating invite link`,
|
||||
message: t`Please try again later.`,
|
||||
icon: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const teamSubscription = getSubscriptionByPlan(subscriptions, "team");
|
||||
const proSubscription = getSubscriptionByPlan(subscriptions, "pro");
|
||||
|
||||
@@ -163,43 +109,6 @@ export function InviteMemberForm({
|
||||
inviteMember.mutate(member);
|
||||
};
|
||||
|
||||
const handleInviteLinkToggle = async () => {
|
||||
setIsLoadingInviteLink(true);
|
||||
|
||||
if (isShareInviteLinkEnabled && workspace.publicId) {
|
||||
// Deactivate invite link
|
||||
await deactivateInviteLink.mutateAsync({
|
||||
workspacePublicId: workspace.publicId,
|
||||
});
|
||||
setIsShareInviteLinkEnabled(false);
|
||||
} else {
|
||||
// Create new invite link
|
||||
await createInviteLink.mutateAsync({
|
||||
workspacePublicId: workspace.publicId,
|
||||
});
|
||||
setIsShareInviteLinkEnabled(true);
|
||||
}
|
||||
};
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(inviteLink);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
showPopup({
|
||||
header: t`Invite link copied`,
|
||||
message: t`Invite link copied to clipboard`,
|
||||
icon: "success",
|
||||
});
|
||||
} catch (error) {
|
||||
showPopup({
|
||||
header: t`Error`,
|
||||
message: t`Failed to copy invite link`,
|
||||
icon: "error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpgrade = async () => {
|
||||
const { data, error } = await authClient.subscription.upgrade({
|
||||
plan: "team",
|
||||
@@ -264,34 +173,6 @@ export function InviteMemberForm({
|
||||
}}
|
||||
errorMessage={errors.email?.message}
|
||||
/>
|
||||
{isShareInviteLinkEnabled && inviteLink && (
|
||||
<div className="my-4">
|
||||
<div className="relative">
|
||||
<Input
|
||||
value={inviteLink}
|
||||
className="pr-10 text-sm text-light-900 dark:text-dark-900"
|
||||
readOnly
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute inset-y-0 right-0 flex items-center pr-3 text-light-900 hover:text-light-950 dark:text-dark-900 dark:hover:text-dark-950"
|
||||
onClick={copyToClipboard}
|
||||
>
|
||||
{copied ? (
|
||||
<HiMiniCheck className="h-5 w-5 text-green-600" />
|
||||
) : (
|
||||
<HiOutlineDocumentDuplicate className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-2 flex items-start gap-1">
|
||||
<HiInformationCircle className="mt-0.5 h-4 w-4 text-dark-900" />
|
||||
<p className="text-xs text-gray-500 dark:text-dark-900">
|
||||
{t`Anyone with this link can join your workspace`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" && (
|
||||
<div className="mt-3 rounded-md bg-light-100 p-3 text-xs text-light-900 dark:bg-dark-200 dark:text-dark-900">
|
||||
@@ -324,9 +205,11 @@ export function InviteMemberForm({
|
||||
{(hasTeamSubscription || hasProSubscription) &&
|
||||
env("NEXT_PUBLIC_KAN_ENV") === "cloud" && (
|
||||
<Toggle
|
||||
label={t`Share invite link`}
|
||||
isChecked={isShareInviteLinkEnabled}
|
||||
onChange={handleInviteLinkToggle}
|
||||
label={t`Invite another`}
|
||||
isChecked={isCreateAnotherEnabled}
|
||||
onChange={() =>
|
||||
setIsCreateAnotherEnabled(!isCreateAnotherEnabled)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
@@ -343,7 +226,7 @@ export function InviteMemberForm({
|
||||
) : (
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={inviteMember.isPending || isShareInviteLinkEnabled}
|
||||
disabled={inviteMember.isPending}
|
||||
isLoading={inviteMember.isPending}
|
||||
className="inline-flex w-full justify-center rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 shadow-sm focus-visible:outline-none dark:bg-dark-1000 dark:text-dark-50"
|
||||
>
|
||||
|
||||
@@ -129,9 +129,9 @@ export default function MembersPage() {
|
||||
{memberRole &&
|
||||
memberRole.charAt(0).toUpperCase() + memberRole.slice(1)}
|
||||
</span>
|
||||
{(memberStatus === "invited" || memberStatus === "paused") && (
|
||||
{memberStatus === "invited" && (
|
||||
<span className="mt-1 inline-flex items-center rounded-md bg-gray-500/10 px-1.5 py-0.5 text-[10px] font-medium text-gray-400 ring-1 ring-inset ring-gray-500/20 sm:ml-2 sm:mt-0 sm:text-[11px]">
|
||||
{memberStatus === "invited" ? t`Pending` : t`Paused`}
|
||||
{t`Pending`}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -183,7 +183,7 @@ export default function MembersPage() {
|
||||
<>
|
||||
{!proSubscription && (
|
||||
<Link
|
||||
href="/settings/workspace?upgrade=pro"
|
||||
href="/settings?upgrade=pro"
|
||||
className="hidden items-center rounded-full border border-emerald-300 bg-emerald-50 px-3 py-1 text-center text-xs text-emerald-400 dark:border-emerald-700 dark:bg-emerald-950 dark:text-emerald-400 lg:flex"
|
||||
>
|
||||
<HiBolt />
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import FeedbackModal from "~/components/FeedbackModal";
|
||||
import { LanguageSelector } from "~/components/LanguageSelector";
|
||||
import Modal from "~/components/modal";
|
||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||
import { PageHead } from "~/components/PageHead";
|
||||
import { useModal } from "~/providers/modal";
|
||||
import { api } from "~/utils/api";
|
||||
import Avatar from "./components/Avatar";
|
||||
import { ChangePasswordFormConfirmation } from "./components/ChangePasswordConfirmation";
|
||||
import { DeleteAccountConfirmation } from "./components/DeleteAccountConfirmation";
|
||||
import UpdateDisplayNameForm from "./components/UpdateDisplayNameForm";
|
||||
|
||||
export default function AccountSettings() {
|
||||
const { modalContentType, openModal, isOpen } = useModal();
|
||||
const isCredentialsEnabled =
|
||||
env("NEXT_PUBLIC_ALLOW_CREDENTIALS")?.toLowerCase() === "true";
|
||||
const { data } = api.user.getUser.useQuery();
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHead title={t`Settings | Account`} />
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Profile picture`}
|
||||
</h2>
|
||||
<Avatar userId={data?.id} userImage={data?.image} />
|
||||
|
||||
<div className="mb-4">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Display name`}
|
||||
</h2>
|
||||
<UpdateDisplayNameForm displayName={data?.name ?? ""} />
|
||||
</div>
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Language`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Change your language preferences.`}
|
||||
</p>
|
||||
<LanguageSelector />
|
||||
</div>
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Delete account`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Once you delete your account, there is no going back. This action cannot be undone.`}
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => openModal("DELETE_ACCOUNT")}
|
||||
>
|
||||
{t`Delete account`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isCredentialsEnabled && (
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Change Password`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`You are about to change your password.`}
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => openModal("CHANGE_PASSWORD")}
|
||||
>
|
||||
{t`Change Password`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Account-specific modals */}
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "DELETE_ACCOUNT"}
|
||||
>
|
||||
<DeleteAccountConfirmation />
|
||||
</Modal>
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "CHANGE_PASSWORD"}
|
||||
>
|
||||
<ChangePasswordFormConfirmation />
|
||||
</Modal>
|
||||
|
||||
{/* Global modals */}
|
||||
<Modal
|
||||
modalSize="md"
|
||||
isVisible={isOpen && modalContentType === "NEW_FEEDBACK"}
|
||||
>
|
||||
<FeedbackModal />
|
||||
</Modal>
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "NEW_WORKSPACE"}
|
||||
>
|
||||
<NewWorkspaceForm />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import FeedbackModal from "~/components/FeedbackModal";
|
||||
import Modal from "~/components/modal";
|
||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||
import { PageHead } from "~/components/PageHead";
|
||||
import { useModal } from "~/providers/modal";
|
||||
import ApiKeyList from "./components/ApiKeyList";
|
||||
import NewApiKeyModal from "./components/NewApiKeyModal";
|
||||
import { RevokeApiKeyConfirmation } from "./components/RevokeApiKeyConfirmation";
|
||||
|
||||
export default function ApiSettings() {
|
||||
const { modalContentType, openModal, isOpen } = useModal();
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHead title={t`Settings | API`} />
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`API keys`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`View and manage your API keys.`}
|
||||
</p>
|
||||
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<Button variant="primary" onClick={() => openModal("NEW_API_KEY")}>
|
||||
{t`Create new key`}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ApiKeyList />
|
||||
</div>
|
||||
|
||||
{/* API-specific modals */}
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "NEW_API_KEY"}
|
||||
>
|
||||
<NewApiKeyModal />
|
||||
</Modal>
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "REVOKE_API_KEY"}
|
||||
>
|
||||
<RevokeApiKeyConfirmation />
|
||||
</Modal>
|
||||
|
||||
{/* Global modals */}
|
||||
<Modal
|
||||
modalSize="md"
|
||||
isVisible={isOpen && modalContentType === "NEW_FEEDBACK"}
|
||||
>
|
||||
<FeedbackModal />
|
||||
</Modal>
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "NEW_WORKSPACE"}
|
||||
>
|
||||
<NewWorkspaceForm />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { HiMiniArrowTopRightOnSquare } from "react-icons/hi2";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import FeedbackModal from "~/components/FeedbackModal";
|
||||
import Modal from "~/components/modal";
|
||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||
import { PageHead } from "~/components/PageHead";
|
||||
import { useModal } from "~/providers/modal";
|
||||
|
||||
export default function BillingSettings() {
|
||||
const { modalContentType, isOpen } = useModal();
|
||||
|
||||
const handleOpenBillingPortal = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/stripe/create_billing_session", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const { url } = (await response.json()) as { url: string };
|
||||
|
||||
if (url) {
|
||||
window.location.href = url;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating billing session:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHead title={t`Settings | Billing`} />
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Billing`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`View and manage your billing and subscription.`}
|
||||
</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
iconRight={<HiMiniArrowTopRightOnSquare />}
|
||||
onClick={handleOpenBillingPortal}
|
||||
>
|
||||
{t`Billing portal`}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Global modals */}
|
||||
<Modal
|
||||
modalSize="md"
|
||||
isVisible={isOpen && modalContentType === "NEW_FEEDBACK"}
|
||||
>
|
||||
<FeedbackModal />
|
||||
</Modal>
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "NEW_WORKSPACE"}
|
||||
>
|
||||
<NewWorkspaceForm />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { useEffect } from "react";
|
||||
import { HiMiniArrowTopRightOnSquare } from "react-icons/hi2";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import FeedbackModal from "~/components/FeedbackModal";
|
||||
import Modal from "~/components/modal";
|
||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||
import { PageHead } from "~/components/PageHead";
|
||||
import { useModal } from "~/providers/modal";
|
||||
import { usePopup } from "~/providers/popup";
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
export default function IntegrationsSettings() {
|
||||
const { modalContentType, isOpen } = useModal();
|
||||
const { showPopup } = usePopup();
|
||||
|
||||
const {
|
||||
data: integrations,
|
||||
refetch: refetchIntegrations,
|
||||
isLoading: integrationsLoading,
|
||||
} = api.integration.providers.useQuery();
|
||||
|
||||
const { data: trelloUrl, refetch: refetchTrelloUrl } =
|
||||
api.integration.getAuthorizationUrl.useQuery(
|
||||
{ provider: "trello" },
|
||||
{
|
||||
enabled:
|
||||
!integrationsLoading &&
|
||||
!integrations?.some(
|
||||
(integration) => integration.provider === "trello",
|
||||
),
|
||||
refetchOnWindowFocus: true,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleFocus = () => {
|
||||
refetchIntegrations();
|
||||
};
|
||||
window.addEventListener("focus", handleFocus);
|
||||
return () => {
|
||||
window.removeEventListener("focus", handleFocus);
|
||||
};
|
||||
}, [refetchIntegrations]);
|
||||
|
||||
const { mutateAsync: disconnectTrello } =
|
||||
api.integration.disconnect.useMutation({
|
||||
onSuccess: () => {
|
||||
refetchIntegrations();
|
||||
refetchTrelloUrl();
|
||||
showPopup({
|
||||
header: t`Trello disconnected`,
|
||||
message: t`Your Trello account has been disconnected.`,
|
||||
icon: "success",
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
showPopup({
|
||||
header: t`Error disconnecting Trello`,
|
||||
message: t`An error occurred while disconnecting your Trello account.`,
|
||||
icon: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHead title={t`Settings | Integrations`} />
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Trello`}
|
||||
</h2>
|
||||
{!integrations?.some(
|
||||
(integration) => integration.provider === "trello",
|
||||
) && trelloUrl ? (
|
||||
<>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Connect your Trello account to import boards.`}
|
||||
</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
iconRight={<HiMiniArrowTopRightOnSquare />}
|
||||
onClick={() =>
|
||||
window.open(
|
||||
trelloUrl.url,
|
||||
"trello_auth",
|
||||
"height=800,width=600",
|
||||
)
|
||||
}
|
||||
>
|
||||
{t`Connect Trello`}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
integrations?.some(
|
||||
(integration) => integration.provider === "trello",
|
||||
) && (
|
||||
<>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Your Trello account is connected.`}
|
||||
</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => disconnectTrello({ provider: "trello" })}
|
||||
>
|
||||
{t`Disconnect Trello`}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Global modals */}
|
||||
<Modal
|
||||
modalSize="md"
|
||||
isVisible={isOpen && modalContentType === "NEW_FEEDBACK"}
|
||||
>
|
||||
<FeedbackModal />
|
||||
</Modal>
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "NEW_WORKSPACE"}
|
||||
>
|
||||
<NewWorkspaceForm />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
import { useEffect, useState } from "react";
|
||||
import { HiBolt } from "react-icons/hi2";
|
||||
|
||||
import type { Subscription } from "@kan/shared/utils";
|
||||
import { hasActiveSubscription } from "@kan/shared/utils";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import FeedbackModal from "~/components/FeedbackModal";
|
||||
import Modal from "~/components/modal";
|
||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||
import { PageHead } from "~/components/PageHead";
|
||||
import { useModal } from "~/providers/modal";
|
||||
import { useWorkspace } from "~/providers/workspace";
|
||||
import { api } from "~/utils/api";
|
||||
import { DeleteWorkspaceConfirmation } from "./components/DeleteWorkspaceConfirmation";
|
||||
import UpdateWorkspaceDescriptionForm from "./components/UpdateWorkspaceDescriptionForm";
|
||||
import UpdateWorkspaceNameForm from "./components/UpdateWorkspaceNameForm";
|
||||
import UpdateWorkspaceUrlForm from "./components/UpdateWorkspaceUrlForm";
|
||||
import { UpgradeToProConfirmation } from "./components/UpgradeToProConfirmation";
|
||||
|
||||
export default function WorkspaceSettings() {
|
||||
const { modalContentType, openModal, isOpen } = useModal();
|
||||
const { workspace } = useWorkspace();
|
||||
const router = useRouter();
|
||||
const { data } = api.user.getUser.useQuery();
|
||||
const [hasOpenedUpgradeModal, setHasOpenedUpgradeModal] = useState(false);
|
||||
|
||||
const { data: workspaceData } = api.workspace.byId.useQuery({
|
||||
workspacePublicId: workspace.publicId,
|
||||
});
|
||||
|
||||
const subscriptions = workspaceData?.subscriptions as
|
||||
| Subscription[]
|
||||
| undefined;
|
||||
|
||||
// Open upgrade modal if upgrade=pro is in URL params
|
||||
useEffect(() => {
|
||||
if (
|
||||
router.query.upgrade === "pro" &&
|
||||
env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
|
||||
!hasActiveSubscription(subscriptions, "pro") &&
|
||||
!hasOpenedUpgradeModal
|
||||
) {
|
||||
openModal("UPGRADE_TO_PRO");
|
||||
setHasOpenedUpgradeModal(true);
|
||||
}
|
||||
}, [router.query.upgrade, subscriptions, openModal, hasOpenedUpgradeModal]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHead title={t`Settings | Workspace`} />
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace name`}
|
||||
</h2>
|
||||
<UpdateWorkspaceNameForm
|
||||
workspacePublicId={workspace.publicId}
|
||||
workspaceName={workspace.name}
|
||||
/>
|
||||
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace URL`}
|
||||
</h2>
|
||||
<UpdateWorkspaceUrlForm
|
||||
workspacePublicId={workspace.publicId}
|
||||
workspaceUrl={workspace.slug ?? ""}
|
||||
workspacePlan={workspace.plan ?? "free"}
|
||||
/>
|
||||
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace description`}
|
||||
</h2>
|
||||
<UpdateWorkspaceDescriptionForm
|
||||
workspacePublicId={workspace.publicId}
|
||||
workspaceDescription={workspace.description ?? ""}
|
||||
/>
|
||||
|
||||
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
|
||||
!hasActiveSubscription(subscriptions, "pro") && (
|
||||
<div className="my-8">
|
||||
<Button
|
||||
onClick={() => openModal("UPGRADE_TO_PRO")}
|
||||
iconRight={<HiBolt />}
|
||||
>
|
||||
{t`Upgrade to Pro`}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
|
||||
{t`Delete workspace`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Once you delete your workspace, there is no going back. This action cannot be undone.`}
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => openModal("DELETE_WORKSPACE")}
|
||||
disabled={workspace.role !== "admin"}
|
||||
>
|
||||
{t`Delete workspace`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Workspace-specific modals */}
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "DELETE_WORKSPACE"}
|
||||
>
|
||||
<DeleteWorkspaceConfirmation />
|
||||
</Modal>
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "UPGRADE_TO_PRO"}
|
||||
>
|
||||
<UpgradeToProConfirmation
|
||||
userId={data?.id ?? ""}
|
||||
workspacePublicId={workspace.publicId}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
{/* Global modals */}
|
||||
<Modal
|
||||
modalSize="md"
|
||||
isVisible={isOpen && modalContentType === "NEW_FEEDBACK"}
|
||||
>
|
||||
<FeedbackModal />
|
||||
</Modal>
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "NEW_WORKSPACE"}
|
||||
>
|
||||
<NewWorkspaceForm />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { HiEllipsisHorizontal } from "react-icons/hi2";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { authClient } from "@kan/auth/client";
|
||||
|
||||
import Dropdown from "~/components/Dropdown";
|
||||
import { useModal } from "~/providers/modal";
|
||||
|
||||
export default function ApiKeyList() {
|
||||
const { openModal } = useModal();
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ["apiKeys"],
|
||||
queryFn: () => authClient.apiKey.list(),
|
||||
});
|
||||
|
||||
const TableRow = ({
|
||||
keyId,
|
||||
keyName,
|
||||
keyStart,
|
||||
createdAt,
|
||||
lastRequest,
|
||||
isLastRow,
|
||||
showSkeleton,
|
||||
}: {
|
||||
keyId?: string;
|
||||
keyName?: string | null | undefined;
|
||||
keyStart?: string | null | undefined;
|
||||
createdAt?: Date | null;
|
||||
lastRequest?: Date | null;
|
||||
isLastRow?: boolean | undefined;
|
||||
showSkeleton?: boolean | undefined;
|
||||
}) => {
|
||||
const formatDate = (date?: Date | string | null) => {
|
||||
if (!date) return "Never";
|
||||
const dateObj = date instanceof Date ? date : new Date(date);
|
||||
return dateObj.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<tr className="rounded-b-lg">
|
||||
<td className={twMerge("w-[30%]", isLastRow ? "rounded-bl-lg" : "")}>
|
||||
<div className="flex items-center p-4">
|
||||
<div className="ml-2 min-w-0 flex-1">
|
||||
<div>
|
||||
<div className="flex items-center">
|
||||
<p
|
||||
className={twMerge(
|
||||
"mr-2 text-sm font-medium text-light-900 dark:text-dark-900",
|
||||
showSkeleton &&
|
||||
"md mb-2 h-3 w-[125px] animate-pulse rounded-sm bg-light-200 dark:bg-dark-200",
|
||||
)}
|
||||
>
|
||||
{keyName}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="w-[20%] px-3 py-4">
|
||||
<p
|
||||
className={twMerge(
|
||||
"text-sm text-light-900 dark:text-dark-900",
|
||||
showSkeleton &&
|
||||
"h-3 w-[80px] animate-pulse rounded-sm bg-light-200 dark:bg-dark-200",
|
||||
)}
|
||||
>
|
||||
{formatDate(createdAt)}
|
||||
</p>
|
||||
</td>
|
||||
<td className="w-[20%] px-3 py-4">
|
||||
<p
|
||||
className={twMerge(
|
||||
"text-sm text-light-900 dark:text-dark-900",
|
||||
showSkeleton &&
|
||||
"h-3 w-[80px] animate-pulse rounded-sm bg-light-200 dark:bg-dark-200",
|
||||
)}
|
||||
>
|
||||
{formatDate(lastRequest)}
|
||||
</p>
|
||||
</td>
|
||||
<td className="w-[25%] px-3 py-4">
|
||||
<div>
|
||||
<span
|
||||
className={twMerge(
|
||||
"inline-flex items-center rounded-md bg-emerald-500/10 px-1.5 py-0.5 text-[11px] font-medium text-emerald-400 ring-1 ring-inset ring-emerald-500/20",
|
||||
showSkeleton &&
|
||||
"h-5 w-[50px] animate-pulse bg-light-200 ring-0 dark:bg-dark-200",
|
||||
)}
|
||||
>
|
||||
{keyStart}...
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className={twMerge(
|
||||
"w-[5%] min-w-[50px]",
|
||||
isLastRow && "rounded-br-lg",
|
||||
)}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center px-3">
|
||||
<div className="relative z-50">
|
||||
<Dropdown
|
||||
items={[
|
||||
{
|
||||
label: "Revoke",
|
||||
action: () =>
|
||||
openModal("REVOKE_API_KEY", keyId, keyName ?? ""),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<HiEllipsisHorizontal
|
||||
size={25}
|
||||
className="text-light-900 dark:text-dark-900"
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
if (!isLoading && (!data?.data || data.data.length === 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-8 flow-root">
|
||||
<div className="overflow-x-auto overflow-y-visible">
|
||||
<div className="inline-block min-w-full py-2 pb-12 align-middle">
|
||||
<div className="relative h-full shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg">
|
||||
<table className="min-w-[600px] divide-y divide-light-600 dark:divide-dark-600">
|
||||
<thead className="rounded-t-lg bg-light-300 dark:bg-dark-200">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
className="w-[30%] rounded-tl-lg py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-light-900 dark:text-dark-900 sm:pl-6"
|
||||
>
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="w-[20%] px-3 py-3.5 text-left text-sm font-semibold text-light-900 dark:text-dark-900"
|
||||
>
|
||||
Created
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="w-[20%] px-3 py-3.5 text-left text-sm font-semibold text-light-900 dark:text-dark-900"
|
||||
>
|
||||
Last Used
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="w-[25%] px-3 py-3.5 text-left text-sm font-semibold text-light-900 dark:text-dark-900"
|
||||
>
|
||||
Key
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="w-[5%] rounded-tr-lg px-3 py-3.5 text-center text-sm font-semibold text-light-900 dark:text-dark-900"
|
||||
>
|
||||
{/* Actions column */}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-light-600 bg-light-50 dark:divide-dark-600 dark:bg-dark-100">
|
||||
{!isLoading &&
|
||||
data?.data?.map((apiKey, index) => (
|
||||
<TableRow
|
||||
key={apiKey.id}
|
||||
keyId={apiKey.id}
|
||||
keyName={apiKey.name}
|
||||
keyStart={apiKey.start}
|
||||
createdAt={apiKey.createdAt}
|
||||
lastRequest={apiKey.lastRequest}
|
||||
isLastRow={index === data.data.length - 1}
|
||||
/>
|
||||
))}
|
||||
|
||||
{isLoading && (
|
||||
<>
|
||||
<TableRow showSkeleton />
|
||||
<TableRow showSkeleton />
|
||||
<TableRow showSkeleton isLastRow />
|
||||
</>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,45 +1,14 @@
|
||||
import Image from "next/image";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import ReactCrop from "react-image-crop";
|
||||
|
||||
import "react-image-crop/dist/ReactCrop.css";
|
||||
import { useState } from "react";
|
||||
|
||||
import { generateUID } from "@kan/shared/utils";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Modal from "~/components/modal";
|
||||
import { usePopup } from "~/providers/popup";
|
||||
import { api } from "~/utils/api";
|
||||
import { getAvatarUrl } from "~/utils/helpers";
|
||||
|
||||
interface PercentCrop {
|
||||
unit: "%";
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface LocalPixelCrop {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface ReactCropProps {
|
||||
crop: PercentCrop | undefined;
|
||||
onChange: (crop: LocalPixelCrop, percentCrop: PercentCrop) => void;
|
||||
aspect?: number;
|
||||
className?: string;
|
||||
circularCrop?: boolean;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const AnyReactCrop = ReactCrop as unknown as React.FC<ReactCropProps>;
|
||||
|
||||
export default function Avatar({
|
||||
userId,
|
||||
userImage,
|
||||
@@ -50,13 +19,6 @@ export default function Avatar({
|
||||
const utils = api.useUtils();
|
||||
const { showPopup } = usePopup();
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [cropDialogOpen, setCropDialogOpen] = useState(false);
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [selectedPreviewUrl, setSelectedPreviewUrl] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [crop, setCrop] = useState<PercentCrop>();
|
||||
const imgRef = useRef<HTMLImageElement | null>(null);
|
||||
|
||||
const updateUser = api.user.update.useMutation({
|
||||
onSuccess: async () => {
|
||||
@@ -83,109 +45,24 @@ export default function Avatar({
|
||||
|
||||
const avatarUrl = userImage ? getAvatarUrl(userImage) : undefined;
|
||||
|
||||
const onFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
event.preventDefault();
|
||||
const file = event.target.files?.[0] ?? null;
|
||||
if (!file || !userId) {
|
||||
return showPopup({
|
||||
header: t`Error uploading profile image`,
|
||||
message: t`Please select a file to upload.`,
|
||||
icon: "error",
|
||||
});
|
||||
}
|
||||
// Open crop dialog with preview
|
||||
setSelectedFile(file);
|
||||
const objUrl = URL.createObjectURL(file);
|
||||
setSelectedPreviewUrl(objUrl);
|
||||
setCropDialogOpen(true);
|
||||
};
|
||||
|
||||
const onImageLoad = useCallback(
|
||||
(e: React.SyntheticEvent<HTMLImageElement>) => {
|
||||
const { naturalWidth, naturalHeight } = e.currentTarget;
|
||||
// Create a centered square crop at ~90% of the smaller dimension
|
||||
// Compute width% so that the square fits within the image
|
||||
let widthPercent: number;
|
||||
let heightPercent: number;
|
||||
if (naturalWidth >= naturalHeight) {
|
||||
// landscape: height is limiting
|
||||
heightPercent = 90;
|
||||
widthPercent = (naturalHeight / naturalWidth) * heightPercent;
|
||||
} else {
|
||||
// portrait: width is limiting
|
||||
widthPercent = 90;
|
||||
heightPercent = (naturalWidth / naturalHeight) * widthPercent;
|
||||
}
|
||||
const x = (100 - widthPercent) / 2;
|
||||
const y = (100 - heightPercent) / 2;
|
||||
setCrop({ unit: "%", x, y, width: widthPercent, height: heightPercent });
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const getCroppedBlob = useCallback(async (): Promise<Blob> => {
|
||||
if (!imgRef.current || !crop) throw new Error("No crop to save");
|
||||
const image = imgRef.current;
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const cropXpx = (crop.x / 100) * image.naturalWidth;
|
||||
const cropYpx = (crop.y / 100) * image.naturalHeight;
|
||||
const cropWpx = (crop.width / 100) * image.naturalWidth;
|
||||
const cropHpx = (crop.height / 100) * image.naturalHeight;
|
||||
canvas.width = Math.max(1, Math.floor(cropWpx));
|
||||
canvas.height = Math.max(1, Math.floor(cropHpx));
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) throw new Error("Canvas not supported");
|
||||
|
||||
// For better quality on HiDPI screens
|
||||
const pixelRatio = window.devicePixelRatio || 1;
|
||||
canvas.width = canvas.width * pixelRatio;
|
||||
canvas.height = canvas.height * pixelRatio;
|
||||
ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
||||
ctx.imageSmoothingQuality = "high";
|
||||
|
||||
ctx.drawImage(
|
||||
image,
|
||||
cropXpx,
|
||||
cropYpx,
|
||||
cropWpx,
|
||||
cropHpx,
|
||||
0,
|
||||
0,
|
||||
canvas.width / pixelRatio,
|
||||
canvas.height / pixelRatio,
|
||||
);
|
||||
|
||||
const mime = selectedFile?.type ?? "image/jpeg";
|
||||
const blob: Blob = await new Promise((resolve, reject) => {
|
||||
canvas.toBlob(
|
||||
(b) => (b ? resolve(b) : reject(new Error("toBlob failed"))),
|
||||
mime,
|
||||
);
|
||||
});
|
||||
return blob;
|
||||
}, [crop, selectedFile]);
|
||||
|
||||
const resetCropState = useCallback(() => {
|
||||
setCrop(undefined);
|
||||
setSelectedFile(null);
|
||||
if (selectedPreviewUrl) URL.revokeObjectURL(selectedPreviewUrl);
|
||||
setSelectedPreviewUrl(null);
|
||||
}, [selectedPreviewUrl]);
|
||||
|
||||
const handleCancelCrop = useCallback(() => {
|
||||
setCropDialogOpen(false);
|
||||
resetCropState();
|
||||
}, [resetCropState]);
|
||||
|
||||
const handleSaveCrop = useCallback(async () => {
|
||||
const uploadAvatar = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
try {
|
||||
if (!userId || !selectedFile) return;
|
||||
setUploading(true);
|
||||
const blob = await getCroppedBlob();
|
||||
event.preventDefault();
|
||||
|
||||
const originalExt = selectedFile.name.split(".").pop() ?? "jpg";
|
||||
const fileName = `${userId}/avatar-${generateUID()}.${originalExt}`;
|
||||
const file = event.target.files?.[0];
|
||||
|
||||
if (!file || !userId) {
|
||||
return showPopup({
|
||||
header: t`Error uploading profile image`,
|
||||
message: t`Please select a file to upload.`,
|
||||
icon: "error",
|
||||
});
|
||||
}
|
||||
|
||||
const fileExt = file.name.split(".").pop();
|
||||
const fileName = `${userId}/avatar-${generateUID()}.${fileExt}`;
|
||||
|
||||
setUploading(true);
|
||||
|
||||
const response = await fetch(
|
||||
env("NEXT_PUBLIC_BASE_URL") + "/api/upload/image",
|
||||
@@ -194,24 +71,26 @@ export default function Avatar({
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ filename: fileName, contentType: blob.type }),
|
||||
body: JSON.stringify({ filename: fileName, contentType: file.type }),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) throw new Error("Failed to get pre-signed URL");
|
||||
|
||||
const { url } = (await response.json()) as { url: string };
|
||||
const { url } = (await response.json()) as {
|
||||
url: string;
|
||||
};
|
||||
|
||||
const uploadResponse = await fetch(url, {
|
||||
method: "PUT",
|
||||
body: blob,
|
||||
body: file,
|
||||
});
|
||||
|
||||
if (!uploadResponse.ok) throw new Error("Failed to upload profile image");
|
||||
|
||||
updateUser.mutate({ image: fileName });
|
||||
setCropDialogOpen(false);
|
||||
resetCropState();
|
||||
updateUser.mutate({
|
||||
image: fileName,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showPopup({
|
||||
@@ -222,14 +101,7 @@ export default function Avatar({
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
}, [
|
||||
getCroppedBlob,
|
||||
resetCropState,
|
||||
selectedFile,
|
||||
showPopup,
|
||||
updateUser,
|
||||
userId,
|
||||
]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -239,7 +111,7 @@ export default function Avatar({
|
||||
type="file"
|
||||
id="single"
|
||||
accept="image/*"
|
||||
onChange={onFileChange}
|
||||
onChange={uploadAvatar}
|
||||
disabled={uploading}
|
||||
/>
|
||||
{avatarUrl ? (
|
||||
@@ -262,56 +134,6 @@ export default function Avatar({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Crop Dialog */}
|
||||
{cropDialogOpen && (
|
||||
<Modal modalSize="md" positionFromTop="sm" isVisible>
|
||||
<div className="p-4 sm:p-6">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-base font-semibold text-light-1000 dark:text-dark-1000">
|
||||
{t`Crop your avatar`}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-light-800 dark:text-dark-800">
|
||||
{t`Adjust the square crop to fit your avatar.`}
|
||||
</p>
|
||||
</div>
|
||||
<div className="max-h-[80vh]">
|
||||
<div className="rounded-md border border-light-600 p-2 dark:border-dark-600">
|
||||
<AnyReactCrop
|
||||
crop={crop}
|
||||
onChange={(_crop: LocalPixelCrop, percentCrop: PercentCrop) =>
|
||||
setCrop(percentCrop)
|
||||
}
|
||||
aspect={1}
|
||||
circularCrop
|
||||
className="w-full"
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
ref={imgRef}
|
||||
src={selectedPreviewUrl ?? undefined}
|
||||
alt="Avatar to crop"
|
||||
onLoad={onImageLoad}
|
||||
className="h-auto max-h-[50vh] w-full object-contain"
|
||||
/>
|
||||
</AnyReactCrop>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 flex items-center justify-end gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={handleCancelCrop}
|
||||
disabled={uploading}
|
||||
>
|
||||
{t`Cancel`}
|
||||
</Button>
|
||||
<Button onClick={handleSaveCrop} isLoading={uploading}>
|
||||
{t`Save`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
60
apps/web/src/views/settings/components/CreateAPIKeyForm.tsx
Normal file
60
apps/web/src/views/settings/components/CreateAPIKeyForm.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
|
||||
import { authClient } from "@kan/auth/client";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Input from "~/components/Input";
|
||||
|
||||
const CreateAPIKeyForm = ({
|
||||
apiKey,
|
||||
refetchUser,
|
||||
}: {
|
||||
apiKey:
|
||||
| {
|
||||
id: number;
|
||||
prefix: string | null;
|
||||
key: string;
|
||||
}
|
||||
| null
|
||||
| undefined;
|
||||
refetchUser: () => void;
|
||||
}) => {
|
||||
const handleCreateAPIKey = async () => {
|
||||
await authClient.apiKey.create({
|
||||
name: "Kan API Key",
|
||||
prefix: "kan_",
|
||||
});
|
||||
|
||||
refetchUser();
|
||||
};
|
||||
|
||||
const handleRevokeAPIKey = async () => {
|
||||
if (!apiKey) return;
|
||||
await authClient.apiKey.delete({
|
||||
keyId: apiKey.id.toString(),
|
||||
});
|
||||
|
||||
refetchUser();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{apiKey ? (
|
||||
<div className="flex gap-2">
|
||||
<div className="mb-4 flex w-full max-w-[325px] items-center gap-2">
|
||||
<Input value={apiKey.key} readOnly type="password" />
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="danger" onClick={handleRevokeAPIKey}>
|
||||
{t`Revoke`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Button onClick={handleCreateAPIKey}>{t`Create new key`}</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateAPIKeyForm;
|
||||
@@ -1,69 +0,0 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
import { authClient } from "@kan/auth/client";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Input from "~/components/Input";
|
||||
import { useModal } from "~/providers/modal";
|
||||
|
||||
const newApiKeySchema = z.object({
|
||||
name: z.string().min(1),
|
||||
});
|
||||
|
||||
export default function NewApiKeyForm() {
|
||||
const { openModal } = useModal();
|
||||
const form = useForm({
|
||||
resolver: zodResolver(newApiKeySchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
},
|
||||
});
|
||||
|
||||
const qc = useQueryClient();
|
||||
|
||||
const createApiKeyMutation = useMutation({
|
||||
mutationFn: ({ name }: { name: string }) =>
|
||||
authClient.apiKey.create({ name, prefix: "kan_" }),
|
||||
onSuccess: ({ data: apiKey }) => {
|
||||
qc.invalidateQueries({
|
||||
queryKey: ["apiKeys"],
|
||||
});
|
||||
openModal("API_KEY_CREATED", apiKey?.key, apiKey?.name ?? "");
|
||||
},
|
||||
onError: () => {
|
||||
form.setError("name", {
|
||||
type: "manual",
|
||||
message: "Failed to create API key",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (data: z.infer<typeof newApiKeySchema>) => {
|
||||
createApiKeyMutation.mutate({ name: data.name });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="px-2 py-2">
|
||||
<form
|
||||
onSubmit={form.handleSubmit(handleSubmit)}
|
||||
className="flex flex-col gap-2"
|
||||
>
|
||||
<h2 className="text-sm font-bold text-neutral-900 dark:text-dark-1000">
|
||||
New API key
|
||||
</h2>
|
||||
<Input
|
||||
{...form.register("name")}
|
||||
placeholder="Name"
|
||||
className="w-full"
|
||||
errorMessage={form.formState.errors.name?.message}
|
||||
/>
|
||||
<Button type="submit" isLoading={createApiKeyMutation.isPending}>
|
||||
Create
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import {
|
||||
HiInformationCircle,
|
||||
HiMiniCheck,
|
||||
HiOutlineDocumentDuplicate,
|
||||
HiXMark,
|
||||
} from "react-icons/hi2";
|
||||
import { z } from "zod";
|
||||
|
||||
import { authClient } from "@kan/auth/client";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import Input from "~/components/Input";
|
||||
import { useClipboard } from "~/hooks/useClipboard";
|
||||
import { useModal } from "~/providers/modal";
|
||||
|
||||
const newApiKeySchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1, { message: t`API key name is required` })
|
||||
.max(30, { message: t`API key name cannot exceed 30 characters` }),
|
||||
});
|
||||
|
||||
export default function NewApiKeyModal() {
|
||||
const { closeModal } = useModal();
|
||||
const { copied, copy } = useClipboard({ timeout: 2000 });
|
||||
const [createdApiKey, setCreatedApiKey] = useState<{
|
||||
key: string;
|
||||
name: string;
|
||||
} | null>(null);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm<z.infer<typeof newApiKeySchema>>({
|
||||
resolver: zodResolver(newApiKeySchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
},
|
||||
});
|
||||
|
||||
const qc = useQueryClient();
|
||||
|
||||
const createApiKeyMutation = useMutation({
|
||||
mutationFn: ({ name }: { name: string }) =>
|
||||
authClient.apiKey.create({ name, prefix: "kan_" }),
|
||||
onSuccess: ({ data: apiKey }) => {
|
||||
void qc.invalidateQueries({
|
||||
queryKey: ["apiKeys"],
|
||||
});
|
||||
if (apiKey && apiKey.key && apiKey.name) {
|
||||
setCreatedApiKey({
|
||||
key: apiKey.key,
|
||||
name: apiKey.name,
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
// Handle error if needed
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data: z.infer<typeof newApiKeySchema>) => {
|
||||
createApiKeyMutation.mutate({ name: data.name });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Reset state and form when modal opens
|
||||
setCreatedApiKey(null);
|
||||
reset();
|
||||
}, [reset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!createdApiKey) {
|
||||
const nameElement = document.querySelector<HTMLElement>("#name");
|
||||
if (nameElement) nameElement.focus();
|
||||
}
|
||||
}, [createdApiKey]);
|
||||
|
||||
if (createdApiKey) {
|
||||
return (
|
||||
<div>
|
||||
<div className="px-5 pt-5">
|
||||
<div className="flex w-full items-center justify-between pb-4 text-neutral-900 dark:text-dark-1000">
|
||||
<h2 className="text-sm font-bold">{t`API key created`}</h2>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded p-1 hover:bg-light-300 focus:outline-none dark:hover:bg-dark-300"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
<HiXMark
|
||||
size={18}
|
||||
className="text-light-900 dark:text-dark-900"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<div className="relative">
|
||||
<Input
|
||||
value={createdApiKey.key}
|
||||
className="pr-10 text-sm text-light-900 dark:text-dark-900"
|
||||
readOnly
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute inset-y-0 right-0 flex items-center pr-3 text-light-900 hover:text-light-950 dark:text-dark-900 dark:hover:text-dark-950"
|
||||
onClick={() => copy(createdApiKey.key)}
|
||||
>
|
||||
{copied ? (
|
||||
<HiMiniCheck className="h-5 w-5 text-green-600" />
|
||||
) : (
|
||||
<HiOutlineDocumentDuplicate className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-2 flex items-start gap-1">
|
||||
<HiInformationCircle className="mt-0.5 h-4 w-4 text-dark-900" />
|
||||
<p className="text-xs text-gray-500 dark:text-dark-900">
|
||||
{t`This API key will only be shown once. Please save it in a secure location.`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||
<div>
|
||||
<Button onClick={() => closeModal()}>{t`Close`}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="px-5 pt-5">
|
||||
<div className="flex w-full items-center justify-between pb-4 text-neutral-900 dark:text-dark-1000">
|
||||
<h2 className="text-sm font-bold">{t`New API key`}</h2>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded p-1 hover:bg-light-300 focus:outline-none dark:hover:bg-dark-300"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
<HiXMark size={18} className="text-light-900 dark:text-dark-900" />
|
||||
</button>
|
||||
</div>
|
||||
<Input
|
||||
id="name"
|
||||
placeholder={t`API key name`}
|
||||
{...register("name", { required: true })}
|
||||
errorMessage={errors.name?.message}
|
||||
onKeyDown={async (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
await handleSubmit(onSubmit)();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||
<div>
|
||||
<Button type="submit" isLoading={createApiKeyMutation.isPending}>
|
||||
{t`Create API key`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
import { authClient } from "@kan/auth/client";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import { useModal } from "~/providers/modal";
|
||||
import { usePopup } from "~/providers/popup";
|
||||
|
||||
export function RevokeApiKeyConfirmation() {
|
||||
const { closeModal, entityId, entityLabel } = useModal();
|
||||
const { showPopup } = usePopup();
|
||||
const qc = useQueryClient();
|
||||
|
||||
const [isAcknowledgmentChecked, setIsAcknowledgmentChecked] = useState(false);
|
||||
|
||||
const deleteApiKeyMutation = useMutation({
|
||||
mutationFn: () => authClient.apiKey.delete({ keyId: entityId }),
|
||||
onSuccess: async () => {
|
||||
closeModal();
|
||||
showPopup({
|
||||
header: "API key revoked",
|
||||
message: `Your API key: ${entityLabel} has been revoked.`,
|
||||
icon: "success",
|
||||
});
|
||||
qc.invalidateQueries({
|
||||
queryKey: ["apiKeys"],
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
closeModal();
|
||||
showPopup({
|
||||
header: "Error revoking API key",
|
||||
message: "Please try again later, or contact customer support.",
|
||||
icon: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const handleRevokeApiKey = () => {
|
||||
deleteApiKeyMutation.mutate();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-5">
|
||||
<div className="flex w-full flex-col justify-between pb-4">
|
||||
<h2 className="text-md pb-4 font-medium text-neutral-900 dark:text-dark-1000">
|
||||
{`Are you sure you want to revoke this API key: ${entityLabel}?`}
|
||||
</h2>
|
||||
<p className="mb-4 text-sm text-light-900 dark:text-dark-900">
|
||||
Keep in mind that this action is irreversible.
|
||||
</p>
|
||||
<p className="text-sm text-light-900 dark:text-dark-900">
|
||||
This will result in the permanent revocation of this API key.
|
||||
</p>
|
||||
</div>
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex h-6 items-center">
|
||||
<input
|
||||
id="acknowledgment"
|
||||
name="acknowledgment"
|
||||
type="checkbox"
|
||||
aria-describedby="acknowledgment-description"
|
||||
className="mt-2 h-[14px] w-[14px] rounded border-gray-300 bg-transparent text-indigo-600 focus:shadow-none focus:ring-0 focus:ring-offset-0"
|
||||
checked={isAcknowledgmentChecked}
|
||||
onChange={() =>
|
||||
setIsAcknowledgmentChecked(!isAcknowledgmentChecked)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 text-sm leading-6">
|
||||
<p
|
||||
id="comments-description"
|
||||
className="text-light-900 dark:text-dark-1000"
|
||||
>
|
||||
I acknowledge that this API key will be permanently revoked and want
|
||||
to proceed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 flex justify-end space-x-2 sm:mt-6">
|
||||
<Button variant="secondary" onClick={() => closeModal()}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="danger"
|
||||
onClick={handleRevokeApiKey}
|
||||
disabled={!isAcknowledgmentChecked}
|
||||
isLoading={deleteApiKeyMutation.isPending}
|
||||
>
|
||||
Revoke API key
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -69,18 +69,16 @@ const UpdateDisplayNameForm = ({ displayName }: { displayName: string }) => {
|
||||
<div className="mb-4 flex w-full max-w-[325px] items-center gap-2">
|
||||
<Input {...register("name")} errorMessage={errors.name?.message} />
|
||||
</div>
|
||||
{isDirty && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="primary"
|
||||
disabled={updateDisplayName.isPending}
|
||||
isLoading={updateDisplayName.isPending}
|
||||
>
|
||||
{t`Update`}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="primary"
|
||||
disabled={!isDirty || updateDisplayName.isPending}
|
||||
isLoading={updateDisplayName.isPending}
|
||||
>
|
||||
{t`Update`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -80,18 +80,16 @@ const UpdateWorkspaceDescriptionForm = ({
|
||||
errorMessage={errors.description?.message}
|
||||
/>
|
||||
</div>
|
||||
{isDirty && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="primary"
|
||||
disabled={updateWorkspaceDescription.isPending}
|
||||
isLoading={updateWorkspaceDescription.isPending}
|
||||
>
|
||||
{t`Update`}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="primary"
|
||||
disabled={!isDirty || updateWorkspaceDescription.isPending}
|
||||
isLoading={updateWorkspaceDescription.isPending}
|
||||
>
|
||||
{t`Update`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -72,18 +72,16 @@ const UpdateWorkspaceNameForm = ({
|
||||
<div className="mb-4 flex w-full max-w-[325px] items-center gap-2">
|
||||
<Input {...register("name")} errorMessage={errors.name?.message} />
|
||||
</div>
|
||||
{isDirty && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="primary"
|
||||
disabled={updateWorkspaceName.isPending}
|
||||
isLoading={updateWorkspaceName.isPending}
|
||||
>
|
||||
{t`Update`}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="primary"
|
||||
disabled={!isDirty || updateWorkspaceName.isPending}
|
||||
isLoading={updateWorkspaceName.isPending}
|
||||
>
|
||||
{t`Update`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -138,23 +138,22 @@ const UpdateWorkspaceUrlForm = ({
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{isDirty && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="primary"
|
||||
disabled={
|
||||
updateWorkspaceSlug.isPending ||
|
||||
checkWorkspaceSlugAvailability.isPending ||
|
||||
isWorkspaceSlugAvailable?.isAvailable === false ||
|
||||
isTyping
|
||||
}
|
||||
isLoading={updateWorkspaceSlug.isPending}
|
||||
>
|
||||
{t`Update`}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
variant="primary"
|
||||
disabled={
|
||||
!isDirty ||
|
||||
updateWorkspaceSlug.isPending ||
|
||||
checkWorkspaceSlugAvailability.isPending ||
|
||||
isWorkspaceSlugAvailable?.isAvailable === false ||
|
||||
isTyping
|
||||
}
|
||||
isLoading={updateWorkspaceSlug.isPending}
|
||||
>
|
||||
{t`Update`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
412
apps/web/src/views/settings/index.tsx
Normal file
412
apps/web/src/views/settings/index.tsx
Normal file
@@ -0,0 +1,412 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { HiBolt, HiMiniArrowTopRightOnSquare } from "react-icons/hi2";
|
||||
|
||||
import type { Subscription } from "@kan/shared/utils";
|
||||
import { hasActiveSubscription } from "@kan/shared/utils";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
import FeedbackModal from "~/components/FeedbackModal";
|
||||
import { LanguageSelector } from "~/components/LanguageSelector";
|
||||
import Modal from "~/components/modal";
|
||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||
import { PageHead } from "~/components/PageHead";
|
||||
import { useModal } from "~/providers/modal";
|
||||
import { usePopup } from "~/providers/popup";
|
||||
import { useWorkspace } from "~/providers/workspace";
|
||||
import { api } from "~/utils/api";
|
||||
import Avatar from "./components/Avatar";
|
||||
import { ChangePasswordFormConfirmation } from "./components/ChangePasswordConfirmation";
|
||||
import CreateAPIKeyForm from "./components/CreateAPIKeyForm";
|
||||
import { DeleteAccountConfirmation } from "./components/DeleteAccountConfirmation";
|
||||
import { DeleteWorkspaceConfirmation } from "./components/DeleteWorkspaceConfirmation";
|
||||
import UpdateDisplayNameForm from "./components/UpdateDisplayNameForm";
|
||||
import UpdateWorkspaceDescriptionForm from "./components/UpdateWorkspaceDescriptionForm";
|
||||
import UpdateWorkspaceNameForm from "./components/UpdateWorkspaceNameForm";
|
||||
import UpdateWorkspaceUrlForm from "./components/UpdateWorkspaceUrlForm";
|
||||
import { UpgradeToProConfirmation } from "./components/UpgradeToProConfirmation";
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { modalContentType, openModal, isOpen } = useModal();
|
||||
const { workspace } = useWorkspace();
|
||||
const utils = api.useUtils();
|
||||
const { showPopup } = usePopup();
|
||||
const router = useRouter();
|
||||
const workspaceUrlSectionRef = useRef<HTMLDivElement>(null);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
const [hasOpenedUpgradeModal, setHasOpenedUpgradeModal] = useState(false);
|
||||
const isCredentialsEnabled =
|
||||
env("NEXT_PUBLIC_ALLOW_CREDENTIALS")?.toLowerCase() === "true";
|
||||
const { data } = api.user.getUser.useQuery();
|
||||
|
||||
const { data: workspaceData } = api.workspace.byId.useQuery({
|
||||
workspacePublicId: workspace.publicId,
|
||||
});
|
||||
|
||||
const subscriptions = workspaceData?.subscriptions as
|
||||
| Subscription[]
|
||||
| undefined;
|
||||
|
||||
const {
|
||||
data: integrations,
|
||||
refetch: refetchIntegrations,
|
||||
isLoading: integrationsLoading,
|
||||
} = api.integration.providers.useQuery();
|
||||
|
||||
const { data: trelloUrl, refetch: refetchTrelloUrl } =
|
||||
api.integration.getAuthorizationUrl.useQuery(
|
||||
{ provider: "trello" },
|
||||
{
|
||||
enabled:
|
||||
!integrationsLoading &&
|
||||
!integrations?.some(
|
||||
(integration) => integration.provider === "trello",
|
||||
),
|
||||
refetchOnWindowFocus: true,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleFocus = () => {
|
||||
refetchIntegrations();
|
||||
};
|
||||
window.addEventListener("focus", handleFocus);
|
||||
return () => {
|
||||
window.removeEventListener("focus", handleFocus);
|
||||
};
|
||||
}, [refetchIntegrations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
router.query.edit === "workspace_url" &&
|
||||
workspaceUrlSectionRef.current &&
|
||||
scrollContainerRef.current
|
||||
) {
|
||||
const element = workspaceUrlSectionRef.current;
|
||||
const container = scrollContainerRef.current;
|
||||
|
||||
container.scrollTop = element.offsetTop - 40;
|
||||
|
||||
const input = element.querySelector('input[type="text"]');
|
||||
if (input instanceof HTMLInputElement) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}, [router.query.edit]);
|
||||
|
||||
// Open upgrade modal if upgrade=pro is in URL params
|
||||
useEffect(() => {
|
||||
if (
|
||||
router.query.upgrade === "pro" &&
|
||||
env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
|
||||
!hasActiveSubscription(subscriptions, "pro") &&
|
||||
!hasOpenedUpgradeModal
|
||||
) {
|
||||
openModal("UPGRADE_TO_PRO");
|
||||
setHasOpenedUpgradeModal(true);
|
||||
}
|
||||
}, [router.query.upgrade, subscriptions, openModal, hasOpenedUpgradeModal]);
|
||||
|
||||
const { mutateAsync: disconnectTrello } =
|
||||
api.integration.disconnect.useMutation({
|
||||
onSuccess: () => {
|
||||
refetchUser();
|
||||
refetchIntegrations();
|
||||
refetchTrelloUrl();
|
||||
showPopup({
|
||||
header: t`Trello disconnected`,
|
||||
message: t`Your Trello account has been disconnected.`,
|
||||
icon: "success",
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
showPopup({
|
||||
header: t`Error disconnecting Trello`,
|
||||
message: t`An error occurred while disconnecting your Trello account.`,
|
||||
icon: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const refetchUser = () => utils.user.getUser.refetch();
|
||||
|
||||
const handleOpenBillingPortal = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/stripe/create_billing_session", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const { url } = (await response.json()) as { url: string };
|
||||
|
||||
if (url) {
|
||||
window.location.href = url;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating billing session:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className="h-full max-h-[calc(100vdh-3rem)] overflow-y-auto md:max-h-[calc(100vdh-4rem)]"
|
||||
>
|
||||
<PageHead title={t`Settings | ${workspace.name ?? "Workspace"}`} />
|
||||
<div className="m-auto max-w-[1100px] px-5 py-6 md:px-28 md:py-12">
|
||||
<div className="mb-8 flex w-full justify-between">
|
||||
<h1 className="font-bold tracking-tight text-neutral-900 dark:text-dark-1000 sm:text-[1.2rem]">
|
||||
{t`Settings`}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Profile picture`}
|
||||
</h2>
|
||||
<Avatar userId={data?.id} userImage={data?.image} />
|
||||
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Display name`}
|
||||
</h2>
|
||||
<UpdateDisplayNameForm displayName={data?.name ?? ""} />
|
||||
</div>
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace name`}
|
||||
</h2>
|
||||
<UpdateWorkspaceNameForm
|
||||
workspacePublicId={workspace.publicId}
|
||||
workspaceName={workspace.name}
|
||||
/>
|
||||
|
||||
<div ref={workspaceUrlSectionRef}>
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace URL`}
|
||||
</h2>
|
||||
<UpdateWorkspaceUrlForm
|
||||
workspacePublicId={workspace.publicId}
|
||||
workspaceUrl={workspace.slug ?? ""}
|
||||
workspacePlan={workspace.plan ?? "free"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace description`}
|
||||
</h2>
|
||||
<UpdateWorkspaceDescriptionForm
|
||||
workspacePublicId={workspace.publicId}
|
||||
workspaceDescription={workspace.description ?? ""}
|
||||
/>
|
||||
|
||||
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
|
||||
!hasActiveSubscription(subscriptions, "pro") && (
|
||||
<div className="mt-8">
|
||||
<Button
|
||||
onClick={() => openModal("UPGRADE_TO_PRO")}
|
||||
iconRight={<HiBolt />}
|
||||
>
|
||||
{t`Upgrade to Pro`}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Language`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Change the language of the app.`}
|
||||
</p>
|
||||
<LanguageSelector />
|
||||
</div>
|
||||
|
||||
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" && (
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Billing`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`View and manage your billing and subscription.`}
|
||||
</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
iconRight={<HiMiniArrowTopRightOnSquare />}
|
||||
onClick={handleOpenBillingPortal}
|
||||
>
|
||||
{t`Billing portal`}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
Trello
|
||||
</h2>
|
||||
{!integrations?.some(
|
||||
(integration) => integration.provider === "trello",
|
||||
) && trelloUrl ? (
|
||||
<>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Connect your Trello account to import boards.`}
|
||||
</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
iconRight={<HiMiniArrowTopRightOnSquare />}
|
||||
onClick={() =>
|
||||
window.open(
|
||||
trelloUrl.url,
|
||||
"trello_auth",
|
||||
"height=800,width=600",
|
||||
)
|
||||
}
|
||||
>
|
||||
{t`Connect Trello`}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
integrations?.some(
|
||||
(integration) => integration.provider === "trello",
|
||||
) && (
|
||||
<>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Your Trello account is connected.`}
|
||||
</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => disconnectTrello({ provider: "trello" })}
|
||||
>
|
||||
{t`Disconnect Trello`}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`API keys`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`View and manage your API keys.`}
|
||||
</p>
|
||||
<CreateAPIKeyForm
|
||||
apiKey={data?.apiKey}
|
||||
refetchUser={refetchUser}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Delete workspace`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Once you delete your workspace, there is no going back. This action cannot be undone.`}
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => openModal("DELETE_WORKSPACE")}
|
||||
disabled={workspace.role !== "admin"}
|
||||
>
|
||||
{t`Delete workspace`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isCredentialsEnabled && (
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Change Password`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`You are about to change your password.`}
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => openModal("CHANGE_PASSWORD")}
|
||||
>
|
||||
{t`Change Password`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Delete account`}
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
|
||||
{t`Once you delete your account, there is no going back. This action cannot be undone.`}
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => openModal("DELETE_ACCOUNT")}
|
||||
>
|
||||
{t`Delete account`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<Modal
|
||||
modalSize="md"
|
||||
isVisible={isOpen && modalContentType === "NEW_FEEDBACK"}
|
||||
>
|
||||
<FeedbackModal />
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "NEW_WORKSPACE"}
|
||||
>
|
||||
<NewWorkspaceForm />
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "DELETE_WORKSPACE"}
|
||||
>
|
||||
<DeleteWorkspaceConfirmation />
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "UPGRADE_TO_PRO"}
|
||||
>
|
||||
<UpgradeToProConfirmation
|
||||
userId={data?.id ?? ""}
|
||||
workspacePublicId={workspace.publicId}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "DELETE_ACCOUNT"}
|
||||
>
|
||||
<DeleteAccountConfirmation />
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
modalSize="sm"
|
||||
isVisible={isOpen && modalContentType === "CHANGE_PASSWORD"}
|
||||
>
|
||||
<ChangePasswordFormConfirmation />
|
||||
</Modal>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +1,14 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
|
||||
import * as inviteLinkRepo from "@kan/db/repository/inviteLink.repo";
|
||||
import * as memberRepo from "@kan/db/repository/member.repo";
|
||||
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
|
||||
import * as userRepo from "@kan/db/repository/user.repo";
|
||||
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
||||
import {
|
||||
generateUID,
|
||||
getSubscriptionByPlan,
|
||||
hasUnlimitedSeats,
|
||||
} from "@kan/shared/utils";
|
||||
import { getSubscriptionByPlan, hasUnlimitedSeats } from "@kan/shared/utils";
|
||||
import { updateSubscriptionSeats } from "@kan/stripe";
|
||||
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||
import { assertUserInWorkspace } from "../utils/auth";
|
||||
|
||||
export const memberRouter = createTRPCRouter({
|
||||
@@ -246,379 +241,4 @@ export const memberRouter = createTRPCRouter({
|
||||
|
||||
return { success: true };
|
||||
}),
|
||||
getActiveInviteLink: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get active invite link for workspace",
|
||||
method: "GET",
|
||||
path: "/workspaces/{workspacePublicId}/invite",
|
||||
description: "Gets the active invite link for a workspace",
|
||||
tags: ["Invites"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
workspacePublicId: z.string().min(12),
|
||||
}),
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
id: z.number().optional(),
|
||||
inviteCode: z.string().optional(),
|
||||
inviteLink: z.string().optional(),
|
||||
isActive: z.boolean(),
|
||||
expiresAt: z.date().optional(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const userId = ctx.user?.id;
|
||||
|
||||
if (!userId)
|
||||
throw new TRPCError({
|
||||
message: `User not authenticated`,
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
|
||||
const workspace = await workspaceRepo.getByPublicId(
|
||||
ctx.db,
|
||||
input.workspacePublicId,
|
||||
);
|
||||
|
||||
if (!workspace)
|
||||
throw new TRPCError({
|
||||
message: `Workspace not found`,
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
// Check if user is in workspace
|
||||
await assertUserInWorkspace(ctx.db, userId, workspace.id);
|
||||
|
||||
// Get active invite link for this workspace
|
||||
const activeInviteLink = await inviteLinkRepo.getActiveForWorkspace(
|
||||
ctx.db,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (
|
||||
activeInviteLink &&
|
||||
(!activeInviteLink.expiresAt || new Date() < activeInviteLink.expiresAt)
|
||||
) {
|
||||
return {
|
||||
id: activeInviteLink.id,
|
||||
inviteCode: activeInviteLink.code,
|
||||
inviteLink: `${process.env.NEXT_PUBLIC_BASE_URL}/invite/${activeInviteLink.code}`,
|
||||
isActive: true,
|
||||
expiresAt: activeInviteLink.expiresAt ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return { isActive: false };
|
||||
}),
|
||||
createInviteLink: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create invite link for workspace",
|
||||
method: "POST",
|
||||
path: "/workspaces/{workspacePublicId}/invites",
|
||||
description: "Create invite link for a workspace",
|
||||
tags: ["Invites"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
workspacePublicId: z.string().min(12),
|
||||
}),
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
publicId: z.string().min(12),
|
||||
inviteCode: z.string(),
|
||||
inviteLink: z.string(),
|
||||
expiresAt: z.date().nullable(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const userId = ctx.user?.id;
|
||||
|
||||
if (!userId)
|
||||
throw new TRPCError({
|
||||
message: `User not authenticated`,
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
|
||||
const workspace = await workspaceRepo.getByPublicId(
|
||||
ctx.db,
|
||||
input.workspacePublicId,
|
||||
);
|
||||
|
||||
if (!workspace)
|
||||
throw new TRPCError({
|
||||
message: `Workspace not found`,
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
// Check if user is in workspace
|
||||
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
|
||||
|
||||
// Deactivate any existing active invite links
|
||||
await inviteLinkRepo.deactivateAllActiveForWorkspace(ctx.db, {
|
||||
workspaceId: workspace.id,
|
||||
updatedBy: userId,
|
||||
});
|
||||
|
||||
// Generate new invite code
|
||||
const inviteCode = generateUID();
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setDate(expiresAt.getDate() + 7);
|
||||
|
||||
// Create new invite link
|
||||
const inviteLink = await inviteLinkRepo.createInviteLink(ctx.db, {
|
||||
workspaceId: workspace.id,
|
||||
code: inviteCode,
|
||||
expiresAt,
|
||||
createdBy: userId,
|
||||
});
|
||||
|
||||
if (!inviteLink) {
|
||||
throw new TRPCError({
|
||||
message: `Failed to create invite link`,
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
publicId: inviteLink.publicId,
|
||||
inviteCode: inviteLink.code,
|
||||
inviteLink: `${process.env.NEXT_PUBLIC_BASE_URL}/invite/${inviteLink.code}`,
|
||||
expiresAt: inviteLink.expiresAt,
|
||||
};
|
||||
}),
|
||||
deactivateInviteLink: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deactivate invite link for workspace",
|
||||
method: "DELETE",
|
||||
path: "/workspaces/{workspacePublicId}/invites",
|
||||
description: "Deactivates the invite link for a workspace",
|
||||
tags: ["Invites"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
workspacePublicId: z.string().min(12),
|
||||
}),
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
success: z.boolean(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const userId = ctx.user?.id;
|
||||
|
||||
if (!userId)
|
||||
throw new TRPCError({
|
||||
message: `User not authenticated`,
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
|
||||
const workspace = await workspaceRepo.getByPublicId(
|
||||
ctx.db,
|
||||
input.workspacePublicId,
|
||||
);
|
||||
|
||||
if (!workspace)
|
||||
throw new TRPCError({
|
||||
message: `Workspace not found`,
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
// Check if user is in workspace
|
||||
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
|
||||
|
||||
// Deactivate all active invite links
|
||||
await inviteLinkRepo.deactivateAllActiveForWorkspace(ctx.db, {
|
||||
workspaceId: workspace.id,
|
||||
updatedBy: userId,
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
}),
|
||||
getInviteByCode: publicProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get invite information by code",
|
||||
method: "GET",
|
||||
path: "/workspaces/{workspacePublicId}/invites/{inviteCode}",
|
||||
description: "Get invite information by invite code",
|
||||
tags: ["Invites"],
|
||||
protect: false,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
inviteCode: z.string().min(12),
|
||||
}),
|
||||
)
|
||||
.output(
|
||||
z
|
||||
.object({
|
||||
publicId: z.string().min(12),
|
||||
status: z.string(),
|
||||
expiresAt: z.date().nullable(),
|
||||
})
|
||||
.optional(),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const invite = await inviteLinkRepo.getByCode(ctx.db, input.inviteCode);
|
||||
|
||||
if (
|
||||
!invite ||
|
||||
invite.status !== "active" ||
|
||||
(invite.expiresAt && new Date() > invite.expiresAt)
|
||||
) {
|
||||
throw new TRPCError({
|
||||
message: `Invalid or expired invite link`,
|
||||
code: "BAD_REQUEST",
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
publicId: invite.publicId,
|
||||
status: invite.status,
|
||||
expiresAt: invite.expiresAt ?? null,
|
||||
};
|
||||
}),
|
||||
acceptInviteLink: publicProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Accept an invite link",
|
||||
method: "POST",
|
||||
path: "/workspaces/{workspacePublicId}/invites/accept",
|
||||
description: "Accepts an invitation via invite link",
|
||||
tags: ["Invites"],
|
||||
protect: false,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
inviteCode: z.string().min(12),
|
||||
}),
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
success: z.boolean(),
|
||||
workspacePublicId: z.string().optional(),
|
||||
workspaceSlug: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const userId = ctx.user?.id;
|
||||
|
||||
if (!userId)
|
||||
throw new TRPCError({
|
||||
message: `User not authenticated`,
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
|
||||
const invite = await inviteLinkRepo.getByCode(ctx.db, input.inviteCode);
|
||||
|
||||
if (
|
||||
!invite ||
|
||||
invite.status !== "active" ||
|
||||
(invite.expiresAt && new Date() > invite.expiresAt)
|
||||
)
|
||||
throw new TRPCError({
|
||||
message: `Invalid or expired invite link`,
|
||||
code: "BAD_REQUEST",
|
||||
});
|
||||
|
||||
const workspace = await workspaceRepo.getById(ctx.db, invite.workspaceId);
|
||||
|
||||
if (!workspace)
|
||||
throw new TRPCError({
|
||||
message: `Workspace not found`,
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
const isMember = await workspaceRepo.isUserInWorkspace(
|
||||
ctx.db,
|
||||
userId,
|
||||
invite.workspaceId,
|
||||
);
|
||||
|
||||
if (isMember) {
|
||||
throw new TRPCError({
|
||||
message: `User is already a member of this workspace`,
|
||||
code: "CONFLICT",
|
||||
});
|
||||
}
|
||||
|
||||
const user = await userRepo.getById(ctx.db, userId);
|
||||
|
||||
if (!user)
|
||||
throw new TRPCError({
|
||||
message: `User not found`,
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
if (process.env.NEXT_PUBLIC_KAN_ENV === "cloud") {
|
||||
const subscriptions = await subscriptionRepo.getByReferenceId(
|
||||
ctx.db,
|
||||
workspace.publicId,
|
||||
);
|
||||
|
||||
// get the active subscriptions
|
||||
const activeTeamSubscription = getSubscriptionByPlan(
|
||||
subscriptions,
|
||||
"team",
|
||||
);
|
||||
const activeProSubscription = getSubscriptionByPlan(
|
||||
subscriptions,
|
||||
"pro",
|
||||
);
|
||||
const unlimitedSeats = hasUnlimitedSeats(subscriptions);
|
||||
|
||||
if (!activeTeamSubscription && !activeProSubscription) {
|
||||
throw new TRPCError({
|
||||
message: `Workspace with public ID ${workspace.publicId} does not have an active subscription`,
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
}
|
||||
|
||||
// Update the Stripe subscription
|
||||
if (activeTeamSubscription?.stripeSubscriptionId && !unlimitedSeats) {
|
||||
try {
|
||||
await updateSubscriptionSeats(
|
||||
activeTeamSubscription.stripeSubscriptionId,
|
||||
1,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Failed to update Stripe subscription seats:", error);
|
||||
throw new TRPCError({
|
||||
message: `Failed to update subscription for the new member.`,
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await memberRepo.create(ctx.db, {
|
||||
workspaceId: invite.workspaceId,
|
||||
email: user.email,
|
||||
userId: user.id,
|
||||
createdBy: user.id,
|
||||
role: "member",
|
||||
status: "active",
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
workspacePublicId: workspace.publicId,
|
||||
workspaceSlug: workspace.slug,
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -220,19 +220,6 @@ export const initAuth = (db: dbClient) => {
|
||||
console.log(
|
||||
`Pro subscription ${stripeSubscription.id} activated with unlimited seats`,
|
||||
);
|
||||
|
||||
const workspace = await workspaceRepo.getByPublicId(
|
||||
db,
|
||||
subscription.referenceId,
|
||||
);
|
||||
|
||||
if (workspace?.id) {
|
||||
await memberRepo.unpauseAllMembers(db, workspace.id);
|
||||
|
||||
console.log(
|
||||
`Unpausing all members for workspace ${workspace.id}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TYPE "public"."member_status" ADD VALUE 'paused';
|
||||
@@ -1,34 +0,0 @@
|
||||
CREATE TYPE "public"."invite_link_status" AS ENUM('active', 'inactive');--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "workspace_invite_links" (
|
||||
"id" bigserial PRIMARY KEY NOT NULL,
|
||||
"publicId" varchar(12) NOT NULL,
|
||||
"workspaceId" bigint NOT NULL,
|
||||
"code" varchar(12) NOT NULL,
|
||||
"status" "invite_link_status" DEFAULT 'active' NOT NULL,
|
||||
"expiresAt" timestamp,
|
||||
"createdAt" timestamp DEFAULT now() NOT NULL,
|
||||
"createdBy" uuid,
|
||||
"updatedAt" timestamp,
|
||||
"updatedBy" uuid,
|
||||
CONSTRAINT "workspace_invite_links_publicId_unique" UNIQUE("publicId"),
|
||||
CONSTRAINT "workspace_invite_links_code_unique" UNIQUE("code")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "workspace_invite_links" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "workspace_invite_links" ADD CONSTRAINT "workspace_invite_links_workspaceId_workspace_id_fk" FOREIGN KEY ("workspaceId") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "workspace_invite_links" ADD CONSTRAINT "workspace_invite_links_createdBy_user_id_fk" FOREIGN KEY ("createdBy") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "workspace_invite_links" ADD CONSTRAINT "workspace_invite_links_updatedBy_user_id_fk" FOREIGN KEY ("updatedBy") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -99,20 +99,6 @@
|
||||
"when": 1757535838766,
|
||||
"tag": "20250910202358_AddCascadeSetNullToReferenceIdOnSubsriptions",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"version": "7",
|
||||
"when": 1758226671081,
|
||||
"tag": "20250918201751_AddPausedMemberStatus",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"version": "7",
|
||||
"when": 1758662398166,
|
||||
"tag": "20250923211958_AddWorkspaceInviteLinks",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import { and, eq, gt } from "drizzle-orm";
|
||||
|
||||
import type { dbClient } from "@kan/db/client";
|
||||
import { workspaceInviteLinks } from "@kan/db/schema";
|
||||
import { generateUID } from "@kan/shared/utils";
|
||||
|
||||
export const createInviteLink = async (
|
||||
db: dbClient,
|
||||
args: {
|
||||
workspaceId: number;
|
||||
code: string;
|
||||
expiresAt: Date | null;
|
||||
createdBy: string;
|
||||
},
|
||||
) => {
|
||||
const [result] = await db
|
||||
.insert(workspaceInviteLinks)
|
||||
.values({
|
||||
publicId: generateUID(),
|
||||
workspaceId: args.workspaceId,
|
||||
code: args.code,
|
||||
expiresAt: args.expiresAt ?? null,
|
||||
status: "active",
|
||||
createdBy: args.createdBy,
|
||||
})
|
||||
.returning({
|
||||
publicId: workspaceInviteLinks.publicId,
|
||||
code: workspaceInviteLinks.code,
|
||||
status: workspaceInviteLinks.status,
|
||||
expiresAt: workspaceInviteLinks.expiresAt,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export const deactivateAllActiveForWorkspace = async (
|
||||
db: dbClient,
|
||||
args: { workspaceId: number; updatedBy: string },
|
||||
) => {
|
||||
await db
|
||||
.update(workspaceInviteLinks)
|
||||
.set({
|
||||
status: "inactive",
|
||||
updatedBy: args.updatedBy,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(workspaceInviteLinks.workspaceId, args.workspaceId),
|
||||
eq(workspaceInviteLinks.status, "active"),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
export const getActiveForWorkspace = async (
|
||||
db: dbClient,
|
||||
workspaceId: number,
|
||||
) => {
|
||||
return db.query.workspaceInviteLinks.findFirst({
|
||||
where: and(
|
||||
eq(workspaceInviteLinks.workspaceId, workspaceId),
|
||||
eq(workspaceInviteLinks.status, "active"),
|
||||
),
|
||||
orderBy: (links, { desc }) => [desc(links.createdAt)],
|
||||
});
|
||||
};
|
||||
|
||||
export const getByCode = async (db: dbClient, code: string) => {
|
||||
return db.query.workspaceInviteLinks.findFirst({
|
||||
where: eq(workspaceInviteLinks.code, code),
|
||||
});
|
||||
};
|
||||
@@ -59,32 +59,9 @@ export const create = async (
|
||||
.having(gt(countExpr, 1));
|
||||
|
||||
if (duplicateIndices.length > 0) {
|
||||
// Compact indices to sequential values (0..n-1) to resolve duplicates while preserving order
|
||||
await tx.execute(sql`
|
||||
WITH ordered AS (
|
||||
SELECT id, ROW_NUMBER() OVER (ORDER BY "index", id) - 1 AS new_index
|
||||
FROM "list"
|
||||
WHERE "boardId" = ${result.boardId} AND "deletedAt" IS NULL
|
||||
)
|
||||
UPDATE "list" l
|
||||
SET "index" = o.new_index
|
||||
FROM ordered o
|
||||
WHERE l.id = o.id;
|
||||
`);
|
||||
|
||||
// Last resort: verify fix; if duplicates persist (e.g., due to race conditions), rollback
|
||||
const postFixDupes = await tx
|
||||
.select({ index: lists.index, count: countExpr })
|
||||
.from(lists)
|
||||
.where(and(eq(lists.boardId, result.boardId), isNull(lists.deletedAt)))
|
||||
.groupBy(lists.index)
|
||||
.having(gt(countExpr, 1));
|
||||
|
||||
if (postFixDupes.length > 0) {
|
||||
throw new Error(
|
||||
`Invariant violation: duplicate indices remain after compaction in board ${result.boardId}`,
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Duplicate indices found after reordering in board ${result.boardId}`,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -102,98 +79,7 @@ export const bulkCreate = async (
|
||||
importId?: number;
|
||||
}[],
|
||||
) => {
|
||||
if (listInput.length === 0) return [];
|
||||
|
||||
return db.transaction(async (tx) => {
|
||||
// Group incoming rows by board to compute safe, sequential indices per board
|
||||
const byBoard = new Map<number, typeof listInput>();
|
||||
for (const item of listInput) {
|
||||
const arr = byBoard.get(item.boardId) ?? [];
|
||||
arr.push(item);
|
||||
byBoard.set(item.boardId, arr);
|
||||
}
|
||||
|
||||
const allValuesToInsert: {
|
||||
publicId: string;
|
||||
name: string;
|
||||
createdBy: string;
|
||||
boardId: number;
|
||||
index: number;
|
||||
importId?: number;
|
||||
}[] = [];
|
||||
|
||||
// For each board, append incoming lists after the current max index, preserving their relative order
|
||||
for (const [boardId, items] of byBoard.entries()) {
|
||||
// Find current max index for non-deleted lists in this board
|
||||
const last = await tx.query.lists.findFirst({
|
||||
columns: { index: true },
|
||||
where: and(eq(lists.boardId, boardId), isNull(lists.deletedAt)),
|
||||
orderBy: [desc(lists.index)],
|
||||
});
|
||||
|
||||
let nextIndex = last ? last.index + 1 : 0;
|
||||
|
||||
// Sort incoming by their provided index to preserve Trello order, then reassign sequential indices
|
||||
const sorted = [...items].sort((a, b) => a.index - b.index);
|
||||
for (const it of sorted) {
|
||||
allValuesToInsert.push({
|
||||
publicId: it.publicId,
|
||||
name: it.name,
|
||||
createdBy: it.createdBy,
|
||||
boardId: it.boardId,
|
||||
index: nextIndex++,
|
||||
importId: it.importId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Insert all rows in one go
|
||||
const inserted = await tx
|
||||
.insert(lists)
|
||||
.values(allValuesToInsert)
|
||||
.returning();
|
||||
|
||||
// Post-insert check: if duplicates exist, compact indices per board instead of failing
|
||||
const countExpr = sql<number>`COUNT(*)`.mapWith(Number);
|
||||
for (const boardId of byBoard.keys()) {
|
||||
const duplicateIndices = await tx
|
||||
.select({ index: lists.index, count: countExpr })
|
||||
.from(lists)
|
||||
.where(and(eq(lists.boardId, boardId), isNull(lists.deletedAt)))
|
||||
.groupBy(lists.index)
|
||||
.having(gt(countExpr, 1));
|
||||
|
||||
if (duplicateIndices.length > 0) {
|
||||
await tx.execute(sql`
|
||||
WITH ordered AS (
|
||||
SELECT id, ROW_NUMBER() OVER (ORDER BY "index", id) - 1 AS new_index
|
||||
FROM "list"
|
||||
WHERE "boardId" = ${boardId} AND "deletedAt" IS NULL
|
||||
)
|
||||
UPDATE "list" l
|
||||
SET "index" = o.new_index
|
||||
FROM ordered o
|
||||
WHERE l.id = o.id;
|
||||
`);
|
||||
|
||||
// Last resort: verify fix; if duplicates persist (e.g., due to race conditions), rollback
|
||||
const postFixDupes = await tx
|
||||
.select({ index: lists.index, count: countExpr })
|
||||
.from(lists)
|
||||
.where(and(eq(lists.boardId, boardId), isNull(lists.deletedAt)))
|
||||
.groupBy(lists.index)
|
||||
.having(gt(countExpr, 1));
|
||||
|
||||
if (postFixDupes.length > 0) {
|
||||
throw new Error(
|
||||
`Invariant violation: duplicate indices remain after compaction in board ${boardId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inserted;
|
||||
});
|
||||
return db.insert(lists).values(listInput).returning();
|
||||
};
|
||||
|
||||
export const getByPublicId = async (db: dbClient, listPublicId: string) => {
|
||||
@@ -294,32 +180,9 @@ export const reorder = async (
|
||||
.having(gt(countExpr, 1));
|
||||
|
||||
if (duplicateIndices.length > 0) {
|
||||
// Attempt to auto-heal by compacting indices to sequential values (0..n-1) while preserving order
|
||||
await tx.execute(sql`
|
||||
WITH ordered AS (
|
||||
SELECT id, ROW_NUMBER() OVER (ORDER BY "index", id) - 1 AS new_index
|
||||
FROM "list"
|
||||
WHERE "boardId" = ${list.boardId} AND "deletedAt" IS NULL
|
||||
)
|
||||
UPDATE "list" l
|
||||
SET "index" = o.new_index
|
||||
FROM ordered o
|
||||
WHERE l.id = o.id;
|
||||
`);
|
||||
|
||||
// Last resort verification: if duplicates persist, rollback
|
||||
const postFixDupes = await tx
|
||||
.select({ index: lists.index, count: countExpr })
|
||||
.from(lists)
|
||||
.where(and(eq(lists.boardId, list.boardId), isNull(lists.deletedAt)))
|
||||
.groupBy(lists.index)
|
||||
.having(gt(countExpr, 1));
|
||||
|
||||
if (postFixDupes.length > 0) {
|
||||
throw new Error(
|
||||
`Invariant violation: duplicate indices remain after compaction in board ${list.boardId}`,
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Duplicate indices found after reordering in board ${list.boardId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedList = await tx.query.lists.findFirst({
|
||||
|
||||
@@ -95,15 +95,3 @@ export const softDelete = async (
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const unpauseAllMembers = async (db: dbClient, workspaceId: number) => {
|
||||
await db
|
||||
.update(workspaceMembers)
|
||||
.set({ status: "active" })
|
||||
.where(
|
||||
and(
|
||||
eq(workspaceMembers.workspaceId, workspaceId),
|
||||
eq(workspaceMembers.status, "paused"),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -87,25 +87,11 @@ export const getByPublicId = (db: dbClient, workspacePublicId: string) => {
|
||||
publicId: true,
|
||||
name: true,
|
||||
plan: true,
|
||||
slug: true,
|
||||
},
|
||||
where: eq(workspaces.publicId, workspacePublicId),
|
||||
});
|
||||
};
|
||||
|
||||
export const getById = (db: dbClient, workspaceId: number) => {
|
||||
return db.query.workspaces.findFirst({
|
||||
columns: {
|
||||
id: true,
|
||||
publicId: true,
|
||||
name: true,
|
||||
plan: true,
|
||||
slug: true,
|
||||
},
|
||||
where: eq(workspaces.id, workspaceId),
|
||||
});
|
||||
};
|
||||
|
||||
export const getByPublicIdWithMembers = (
|
||||
db: dbClient,
|
||||
workspacePublicId: string,
|
||||
|
||||
@@ -11,4 +11,3 @@ export * from "./users";
|
||||
export * from "./integrations";
|
||||
export * from "./workspaces";
|
||||
export * from "./subscriptions";
|
||||
export * from "./workspaceInviteLinks";
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import {
|
||||
bigint,
|
||||
bigserial,
|
||||
pgEnum,
|
||||
pgTable,
|
||||
timestamp,
|
||||
uuid,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
import { users } from "./users";
|
||||
import { workspaces } from "./workspaces";
|
||||
|
||||
export const inviteLinkStatuses = ["active", "inactive"] as const;
|
||||
export type InviteLinkStatus = (typeof inviteLinkStatuses)[number];
|
||||
export const inviteLinkStatusEnum = pgEnum(
|
||||
"invite_link_status",
|
||||
inviteLinkStatuses,
|
||||
);
|
||||
|
||||
export const workspaceInviteLinks = pgTable("workspace_invite_links", {
|
||||
id: bigserial("id", { mode: "number" }).primaryKey(),
|
||||
publicId: varchar("publicId", { length: 12 }).notNull().unique(),
|
||||
workspaceId: bigint("workspaceId", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => workspaces.id, { onDelete: "cascade" }),
|
||||
code: varchar("code", { length: 12 }).notNull().unique(),
|
||||
status: inviteLinkStatusEnum("status").notNull().default("active"),
|
||||
expiresAt: timestamp("expiresAt"),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
createdBy: uuid("createdBy").references(() => users.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
updatedAt: timestamp("updatedAt"),
|
||||
updatedBy: uuid("updatedBy").references(() => users.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
}).enableRLS();
|
||||
@@ -19,12 +19,7 @@ export const memberRoles = ["admin", "member", "guest"] as const;
|
||||
export type MemberRole = (typeof memberRoles)[number];
|
||||
export const memberRoleEnum = pgEnum("role", memberRoles);
|
||||
|
||||
export const memberStatuses = [
|
||||
"invited",
|
||||
"active",
|
||||
"removed",
|
||||
"paused",
|
||||
] as const;
|
||||
export const memberStatuses = ["invited", "active", "removed"] as const;
|
||||
export type MemberStatus = (typeof memberStatuses)[number];
|
||||
export const memberStatusEnum = pgEnum("member_status", memberStatuses);
|
||||
|
||||
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -205,9 +205,6 @@ importers:
|
||||
react-icons:
|
||||
specifier: ^5.5.0
|
||||
version: 5.5.0(react@18.3.1)
|
||||
react-image-crop:
|
||||
specifier: ^11.0.10
|
||||
version: 11.0.10(react@18.3.1)
|
||||
react-lottie-player:
|
||||
specifier: ^1.5.5
|
||||
version: 1.5.6(react@18.3.1)
|
||||
@@ -6119,11 +6116,6 @@ packages:
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
|
||||
react-image-crop@11.0.10:
|
||||
resolution: {integrity: sha512-+5FfDXUgYLLqBh1Y/uQhIycpHCbXkI50a+nbfkB1C0xXXUTwkisHDo2QCB1SQJyHCqIuia4FeyReqXuMDKWQTQ==}
|
||||
peerDependencies:
|
||||
react: '>=16.13.1'
|
||||
|
||||
react-is@16.13.1:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
|
||||
@@ -13704,10 +13696,6 @@ snapshots:
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
|
||||
react-image-crop@11.0.10(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
|
||||
react-is@16.13.1: {}
|
||||
|
||||
react-is@17.0.2: {}
|
||||
|
||||
Reference in New Issue
Block a user