Compare commits

..

29 Commits

Author SHA1 Message Date
Henry
902a539d73 fix: temp ignore eslint errors on build 2025-12-13 21:56:27 +00:00
Henry
7fef9be638 chore: update lockfile 2025-12-13 21:31:17 +00:00
Henry
c7473062aa chore: remove old due date filters 2025-12-13 21:30:15 +00:00
Henry
920e9b18a2 fix: enable sessions for API keys 2025-12-13 21:15:54 +00:00
Henry
bcd20b58c6 chore: remove instrumentation (#279)
* chore: remove instrumentation

* chore: clean apk cache after installation

* chore: fix docker builds
2025-12-10 22:16:38 +00:00
Henry
36675604f9 chore: upgrade next, react-email and better-auth (#278)
* feat: upgrade next and react-email

* chore: upgrade better-auth
2025-12-10 20:34:10 +00:00
Jovânio Júnior
94760f1e4c Add Brazilian Portuguese (pt-BR) translation support to Kan (#276)
* adding Brazilian Portuguese translation; added a repository on Docker Hub to use in Umbrel — version ptbr jovaniojunior/kan-ptbr:latest

* add notes to changelog.md

* adjust pt-BR date format in date-fns

* improve Portuguese integration across code functions and other small pt-BR tweaks

* chore: gen missing translations

---------

Co-authored-by: Henry <henry_ball@hotmail.co.uk>
2025-12-09 20:24:30 +00:00
Henry
38f477bb5a fix: delete attachment from s3 (#272) 2025-12-05 22:04:53 +00:00
Henry
d208952d15 feat: card due dates (#271)
* feat: add card due dates to schema

* feat: update repo funcs

* feat: update card router to support due dates

* chore: update migration journal

* feat: add date selector

* feat: display date icon and label on cards

* feat: add due date filters

* feat: add due date to new card form

* feat: light mode tweaks

* feat: improve text eligibility on light mode

* feat: reduce selector font size

* feat: display date updates in card activity

* chore: gen translations
2025-12-05 21:49:45 +00:00
Eliott Herbert-Byrnes
88f1aa0ec4 feat: implemented addOrRemoveLabel mutation on CardPage #266 (#270)
* feat: implemented addOrRemoveLabel mutation on CardPage to automatically add new labels #266

* Remove .env.backup
2025-12-05 19:39:18 +00:00
Owais Rizvi
06ca6e38b3 feat: add IRSA support for S3 authentication (#265) 2025-12-01 19:54:58 +00:00
Patrick McEvoy
915877b8e6 feat: limit oidc domains (#263) 2025-12-01 19:51:08 +00:00
Henry
bb8f3de0ad feat: add env var to allow invalid SSL certs (#262)
* feat: add env var to allow invalid SSL certs

* Update readme
2025-11-30 23:00:55 +00:00
Henry
d3c8cbd6c0 fix: persist filters when opening card modal (#261) 2025-11-30 22:32:59 +00:00
Henry
6c3dbaefd3 Update README.md 2025-11-30 22:13:44 +00:00
Ivan Shelepugin
f938723d9a fix(api): correct values of Content-Disposition attributes (#260) 2025-11-30 21:58:40 +00:00
Henry
0928709173 chore: revert hardcoded baseUrl 2025-11-27 23:34:01 +00:00
Henry
ade9aad4bf feat: set next public base url as optional 2025-11-27 23:25:50 +00:00
Henry
5fbaf26707 fix: add missing dependency 2025-11-27 23:15:54 +00:00
Henry
b69835f7de fix: hardcode baseUrl 2025-11-27 23:11:48 +00:00
Henry
58ff6ad041 fix: use next-runtime-env for baseUrl 2025-11-27 22:37:26 +00:00
Spencer Simpson
adbc945ed6 feat: add support for keyboard shortcuts (#255)
* feat: add support for keyboard shortcuts

* fix: recenter icons on button

* feat: extend tooltip delay

* chore: add translations

* refactor: tweak kbd styling

* feat: move shortcuts to user menu

---------

Co-authored-by: Henry <henry_ball@hotmail.co.uk>
2025-11-27 22:09:37 +00:00
Henry
c5d85cdb2d feat(cloud): email unsubscribe (#258)
* feat(cloud): add email unsubscribe

* chore: add translations
2025-11-27 21:44:51 +00:00
Henry
56e4c43cfa chore: update translations 2025-11-27 11:15:12 +00:00
Henry
07fc41761b fix: remove unused params from invite paths (#256) 2025-11-26 20:09:58 +00:00
Henry
50382e9186 feat(cloud): free trial notifications (#254) 2025-11-25 23:29:04 +00:00
Henry
00be5fb058 feat(cloud): add discord creds to subscribers 2025-11-25 13:22:43 +00:00
Henry
fc65f53e0c feat(cloud): add notification client (#253) 2025-11-24 22:20:34 +00:00
Henry
64aace6e3b feat: add breadcrumb navigation header to card view (#250)
* feat: add card breadcrumb navigation

* feat: wrap title onto new line
2025-11-21 21:34:12 +00:00
95 changed files with 11425 additions and 2309 deletions

View File

@@ -17,6 +17,7 @@ SMTP_USER=
SMTP_PASSWORD=
EMAIL_FROM= # e.g. "Kan <hello@mail.kan.bn>"
SMTP_SECURE= # set to "false" to use port 587
SMTP_REJECT_UNAUTHORIZED= # set to "false" to accept invalid certs
# Switch email features off entirely (optional)
NEXT_PUBLIC_DISABLE_EMAIL=
@@ -42,6 +43,8 @@ TRELLO_APP_SECRET=
# OAuth providers (optional)
BETTER_AUTH_TRUSTED_ORIGINS=
# Optional: Restrict OIDC/Social sign-ins to specific email domains (comma-separated)
BETTER_AUTH_ALLOWED_DOMAINS=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
DISCORD_CLIENT_ID=
@@ -79,4 +82,3 @@ TWITCH_CLIENT_SECRET=
APPLE_CLIENT_ID=
APPLE_CLIENT_SECRET=
APPLE_APP_BUNDLE_IDENTIFIER=

View File

@@ -34,7 +34,7 @@ See our [roadmap](https://kan.bn/kan/roadmap) for upcoming features.
## Screenshot 👁️
<img width="1507" alt="hero-dark" src="https://github.com/user-attachments/assets/5f7b6ad3-f31d-4b45-93dc-0132b3f2afd4" />
<img width="1507" alt="hero-dark" src="https://github.com/user-attachments/assets/8490104a-cd5d-49de-afc2-152fd8a93119" />
## Made With 🛠️
@@ -138,42 +138,44 @@ pnpm dev
## Environment Variables 🔐
| Variable | Description | Required | Example |
| ----------------------------------------- | -------------------------------------------------------- | ------------------------ | ----------------------------------------------------------- |
| `POSTGRES_URL` | PostgreSQL connection URL | To use external database | `postgres://user:pass@localhost:5432/db` |
| `EMAIL_FROM` | Sender email address | For Email | `"Kan <hello@mail.kan.bn>"` |
| `SMTP_HOST` | SMTP server hostname | For Email | `smtp.resend.com` |
| `SMTP_PORT` | SMTP server port | For Email | `465` |
| `SMTP_USER` | SMTP username/email | No | `resend` |
| `SMTP_PASSWORD` | SMTP password/token | No | `re_xxxx` |
| `SMTP_SECURE` | Use secure SMTP connection (defaults to true if not set) | For Email | `true` |
| `NEXT_PUBLIC_DISABLE_EMAIL` | To disable all email features | For Email | `true` |
| `NEXT_PUBLIC_BASE_URL` | Base URL of your installation | Yes | `http://localhost:3000` |
| `BETTER_AUTH_SECRET` | Auth encryption secret | Yes | Random 32+ char string |
| `BETTER_AUTH_TRUSTED_ORIGINS` | Allowed callback origins | No | `http://localhost:3000,http://localhost:3001` |
| `GOOGLE_CLIENT_ID` | Google OAuth client ID | For Google login | `xxx.apps.googleusercontent.com` |
| `GOOGLE_CLIENT_SECRET` | Google OAuth client secret | For Google login | `xxx` |
| `DISCORD_CLIENT_ID` | Discord OAuth client ID | For Discord login | `xxx` |
| `DISCORD_CLIENT_SECRET` | Discord OAuth client secret | For Discord login | `xxx` |
| `GITHUB_CLIENT_ID` | GitHub OAuth client ID | For GitHub login | `xxx` |
| `GITHUB_CLIENT_SECRET` | GitHub OAuth client secret | For GitHub login | `xxx` |
| `OIDC_CLIENT_ID` | Generic OIDC client ID | For OIDC login | `xxx` |
| `OIDC_CLIENT_SECRET` | Generic OIDC client secret | For OIDC login | `xxx` |
| `OIDC_DISCOVERY_URL` | OIDC discovery URL | For OIDC login | `https://auth.example.com/.well-known/openid-configuration` |
| `TRELLO_APP_API_KEY` | Trello app API key | For Trello import | `xxx` |
| `TRELLO_APP_API_SECRET` | Trello app API secret | For Trello import | `xxx` |
| `S3_REGION` | S3 storage region | For file uploads | `WEUR` |
| `S3_ENDPOINT` | S3 endpoint URL | For file uploads | `https://xxx.r2.cloudflarestorage.com` |
| `S3_ACCESS_KEY_ID` | S3 access key | For file uploads | `xxx` |
| `S3_SECRET_ACCESS_KEY` | S3 secret key | For file uploads | `xxx` |
| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 | For file uploads | `true` |
| `NEXT_PUBLIC_STORAGE_URL` | Storage service URL | For file uploads | `https://storage.kanbn.com` |
| `NEXT_PUBLIC_STORAGE_DOMAIN` | Storage domain name | For file uploads | `kanbn.com` |
| `NEXT_PUBLIC_AVATAR_BUCKET_NAME` | S3 bucket name for avatars | For file uploads | `avatars` |
| `NEXT_PUBLIC_ATTATCHMENTS_BUCKET_NAME` | S3 bucket name for attatchments | For file uploads | `attatchments` |
| `NEXT_PUBLIC_ALLOW_CREDENTIALS` | Allow email & password login | For authentication | `true` |
| `NEXT_PUBLIC_DISABLE_SIGN_UP` | Disable sign up | For authentication | `false` |
| `NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY` | Hide “Powered by kan.bn” on public boards (self-host) | For white labelling | `true` |
| Variable | Description | Required | Example |
| ----------------------------------------- | --------------------------------------------------------- | ------------------------ | ----------------------------------------------------------- |
| `POSTGRES_URL` | PostgreSQL connection URL | To use external database | `postgres://user:pass@localhost:5432/db` |
| `EMAIL_FROM` | Sender email address | For Email | `"Kan <hello@mail.kan.bn>"` |
| `SMTP_HOST` | SMTP server hostname | For Email | `smtp.resend.com` |
| `SMTP_PORT` | SMTP server port | For Email | `465` |
| `SMTP_USER` | SMTP username/email | No | `resend` |
| `SMTP_PASSWORD` | SMTP password/token | No | `re_xxxx` |
| `SMTP_SECURE` | Use secure SMTP connection (defaults to true if not set) | For Email | `true` |
| `SMTP_REJECT_UNAUTHORIZED` | Reject invalid certificates (defaults to true if not set) | For Email | `false` |
| `NEXT_PUBLIC_DISABLE_EMAIL` | To disable all email features | For Email | `true` |
| `NEXT_PUBLIC_BASE_URL` | Base URL of your installation | Yes | `http://localhost:3000` |
| `BETTER_AUTH_ALLOWED_DOMAINS` | Comma-separated list of allowed domains for OIDC logins | For OIDC/Social login | `example.com,subsidiary.com` |
| `BETTER_AUTH_SECRET` | Auth encryption secret | Yes | Random 32+ char string |
| `BETTER_AUTH_TRUSTED_ORIGINS` | Allowed callback origins | No | `http://localhost:3000,http://localhost:3001` |
| `GOOGLE_CLIENT_ID` | Google OAuth client ID | For Google login | `xxx.apps.googleusercontent.com` |
| `GOOGLE_CLIENT_SECRET` | Google OAuth client secret | For Google login | `xxx` |
| `DISCORD_CLIENT_ID` | Discord OAuth client ID | For Discord login | `xxx` |
| `DISCORD_CLIENT_SECRET` | Discord OAuth client secret | For Discord login | `xxx` |
| `GITHUB_CLIENT_ID` | GitHub OAuth client ID | For GitHub login | `xxx` |
| `GITHUB_CLIENT_SECRET` | GitHub OAuth client secret | For GitHub login | `xxx` |
| `OIDC_CLIENT_ID` | Generic OIDC client ID | For OIDC login | `xxx` |
| `OIDC_CLIENT_SECRET` | Generic OIDC client secret | For OIDC login | `xxx` |
| `OIDC_DISCOVERY_URL` | OIDC discovery URL | For OIDC login | `https://auth.example.com/.well-known/openid-configuration` |
| `TRELLO_APP_API_KEY` | Trello app API key | For Trello import | `xxx` |
| `TRELLO_APP_API_SECRET` | Trello app API secret | For Trello import | `xxx` |
| `S3_REGION` | S3 storage region | For file uploads | `WEUR` |
| `S3_ENDPOINT` | S3 endpoint URL | For file uploads | `https://xxx.r2.cloudflarestorage.com` |
| `S3_ACCESS_KEY_ID` | S3 access key | For file uploads (optional with IRSA) | `xxx` |
| `S3_SECRET_ACCESS_KEY` | S3 secret key | For file uploads (optional with IRSA) | `xxx` |
| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 | For file uploads | `true` |
| `NEXT_PUBLIC_STORAGE_URL` | Storage service URL | For file uploads | `https://storage.kanbn.com` |
| `NEXT_PUBLIC_STORAGE_DOMAIN` | Storage domain name | For file uploads | `kanbn.com` |
| `NEXT_PUBLIC_AVATAR_BUCKET_NAME` | S3 bucket name for avatars | For file uploads | `avatars` |
| `NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME` | S3 bucket name for attachments | For file uploads | `attachments` |
| `NEXT_PUBLIC_ALLOW_CREDENTIALS` | Allow email & password login | For authentication | `true` |
| `NEXT_PUBLIC_DISABLE_SIGN_UP` | Disable sign up | For authentication | `false` |
| `NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY` | Hide “Powered by kan.bn” on public boards (self-host) | For white labelling | `true` |
See `.env.example` for a complete list of supported environment variables.

View File

@@ -4,8 +4,9 @@ ARG PROJECT=@kan/web
# 1. Alpine image
FROM node:${NODE_VERSION}-alpine AS alpine
RUN apk update
RUN apk add --no-cache libc6-compat python3 make g++
RUN apk update && \
apk add --no-cache --virtual .build-deps libc6-compat python3 make g++ && \
rm -rf /var/cache/apk/* /tmp/* || true
# Setup pnpm and turbo on the alpine base
FROM alpine AS base

View File

@@ -2,7 +2,7 @@
"version": 0,
"locale": {
"source": "en",
"targets": ["fr", "de", "es", "it", "nl", "ru", "pl"]
"targets": ["fr", "de", "es", "it", "nl", "ru", "pl", "ptbr"]
},
"buckets": {
"po": {

View File

@@ -15,6 +15,7 @@ checksums:
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
Actions/singular: c46571856723b03262fd33f511116298
Activity/singular: 1948763de8e531483a798b68195e297e
Activity%20log/singular: 3ec6755040306fdc9ce801bcf6ab28e1
Activity%20logs/singular: 8b1f0bb96a905646ecfad1cfdfa42168
@@ -91,6 +92,7 @@ checksums:
Card%20title/singular: 7c34f59f4005e6cb3a6ff546ea0b96e3
Change%20Password/singular: a552fc5c4189ebc3e2e6018edda7d18f
Change%20your%20language%20preferences./singular: 293d49fc3c75e9c425b64bd7126e6b46
changed%20the%20due%20date%20to%20%3C0%3E%7BformattedDate%7D%3C%2F0%3E/singular: 32189aa87452ab18f90f02742bfa9910
Check%20out%20some%20of%20our%20favorite%20open%20source%20projects./singular: b0d0be4f63c16552b3aa795af29240d1
Check%20your%20inbox/singular: e9a430fcd298def74212238df0f680d6
Checklist%20name/singular: 5eb5de823f7ca5a4d97bb41e6a3f675a
@@ -109,6 +111,7 @@ checksums:
Complete%20control%20and%20ownership%3A/singular: 0d8b682ba873272217425ccfc96aa9cd
completed%20a%20checklist%20item/singular: 757b04c6c80cc927e1c597c0ad4fda33
completed%20checklist%20item%20%3C0%3E%7B0%7D%3C%2F0%3E/singular: 71ec18acf051fc909a48a07ca3578673
Confirm%20your%20email%20preferences%3A/singular: 043c161dd9866231ae2418fdd9f61b9c
Confirm%20your%20new%20password/singular: a0d2935d7b63f8dd19d7c0de47524416
Connect%20Trello/singular: 4440a0b9e387ef7136e3958e7a089213
Connect%20your%20favorite%20tools%20to%20streamline%20your%20workflow./singular: 033c5dcdb059ccb634aef7fe63f2f45d
@@ -167,12 +170,18 @@ checksums:
Display%20name%20must%20be%20at%20least%203%20characters%20long/singular: 84178b79845a70937af5e26020403052
Display%20name%20updated/singular: 4e27503ca24ef5f009713951ca9e5b97
Do%20you%20offer%20a%20free%20plan%3F/singular: 3ef16b0994f0d9ae17067782ca9bed1b
Do%20you%20want%20to%20unsubscribe%3F/singular: 8173b82c731ba885939d223eeef2e40b
docs/singular: 307bff5678ada918da1f1ec3edc76cac
Docs/singular: 55fea190a0c0fb32acd3dc986cd3cc91
Documentation/singular: 1563fcb5ddb5037b0709ccd3dd384a92
Don't%20have%20an%20account%3F%20%3C0%3E%3C1%3ESign%20up%3C%2F1%3E%3C%2F0%3E/singular: 6ae8282ab8b4a01b4ce87b119e13714b
Done/singular: ffd408fa29d5bc9039ef8ea1b9b699bb
Download%20failed/singular: 142b391cca3e05f2af9617092f7358c5
Due%20date/singular: b098b52aff38516c5838c2ac4419f958
Due%20next%20month/singular: 223ab6f1211e1dfdf39ea68d25fe380b
Due%20next%20week/singular: 2f8fac5719df18d25a466ee913dc6487
Due%20today/singular: a14cb9dd0003485894d328bb803c80e5
Due%20tomorrow/singular: 0b6c8ad7aba0873b7e212d5f1949ca97
Edit%20board%20URL/singular: d8276dfc0189f371ec7d80a2047c07aa
Edit%20comment/singular: 7e4b46525fcb6b47b71798e31c46e374
Edit%20label/singular: 0309e0be1512b1e0b0ceb87c69a53d03
@@ -229,6 +238,7 @@ checksums:
Free%2C%20forever/singular: ae4f2f81e88a50b5f250ef8e8e77e74b
Full-time/singular: fe6e201f6676ae354111435cf6c76910
Fun/singular: 395bdc48e943762b6cde649f4a96771d
General/singular: b891e8f15579fc5d97bcaf3637f5ae59
Get%20started/singular: 5c783951b0100a168bdd2161ff294833
Get%20Started/singular: 1d5f030c4ec9c869e647ae060518b948
Get%20started%20by%20creating%20a%20new%20%7B0%7D/singular: 4ca5ef637c08104e9dbb5ff9f49f0680
@@ -240,6 +250,10 @@ checksums:
GitHub/singular: 6e1cf3c00fa6fbe24afcc78ea3b5f3e4
Go%20Home/singular: 6251589da1964d55afabdfd64c84c335
Go%20to%20app/singular: 896d0441384dcd2bfb3b23d61ff1944d
Go%20to%20boards/singular: dc1362de207d67fe84fb0caf60262e73
Go%20to%20members/singular: 445f4efbc4b1e7509f4fd79ebfbb1476
Go%20to%20settings/singular: 24a7f96880650c9b37099d69f4b7e2a9
Go%20to%20templates/singular: e4e58e33d637282d141df466d729bc7c
High%20Priority/singular: 5d231ff8254aabc875f194c4b4f49c97
Hired/singular: e5a9b1bd409b007141fe3d7890022f9a
Host%20Kan%20on%20your%20own%20infrastructure.%20Ideal%20for%20organisations%20that%20need%20complete%20control%20over%20their%20data./singular: 8e7ae0783d60ef4624d3caf9bfc3747f
@@ -283,6 +297,7 @@ checksums:
Kanban%20is%20better%20with%20a%20team.%20Perfect%20for%20small%20and%20growing%20teams%20looking%20to%20collaborate./singular: a77bee43046b260797c8936ad23e9223
Kanban%20reimagined/singular: 613ccfdd9f54c66cbf68cfa313498766
Keep%20in%20mind%20that%20this%20action%20is%20irreversible./singular: 79702b2ad3be71cb8b87f1122a60c199
Keyboard%20Shortcuts/singular: ef00d7494b69def6841620bd6554d040
Labels/singular: 6f15627a90002323eac018274b6922d6
Labels%20%26%20filters/singular: e1ceda0c6cc32fb04cb9423582ad8fe4
Labels%20%26%20Filters/singular: 8f0bdd6084516f8241cb613178114390
@@ -316,6 +331,7 @@ checksums:
moved%20the%20card%20from%20%3C0%3E%7B0%7D%3C%2F0%3E%20to%3C1%3E%7B1%7D%3C%2F1%3E/singular: 11edf0427766c26db541d46379dc3c16
moved%20the%20card%20to%20another%20list/singular: 2e8c41bfafb42fa299ce56e4300205ff
Name/singular: 9368b5a047572b6051f334af5aa76819
Navigation/singular: 0373afd8238db1c49f4be4fa6cdf5cd3
Need%20help%3F/singular: 04e7322f2d3ffb2d73ff2f64b71637c8
New/singular: 126d036fae5fb6b629728ecb97e6195b
New%20%7B0%7D/singular: 67b6a22a65e0956f2091d00e6966652b
@@ -336,18 +352,23 @@ checksums:
No%20authentication%20methods%20are%20currently%20available/singular: f718c896810da3612202887f9a4374fa
No%20boards%20found/singular: e044088d2c51b6bdf660fafa86ee1e17
No%20credit%20card%20required/singular: 2090aa4171dc0b60735069f89b592883
No%20dates/singular: bd0ec82b3b1f4a5fa89d362b71f8141d
No%20download%20URL%20available%20for%20this%20attachment./singular: e367d39420b2242f9d2fd749c87f446a
No%20keyboard%20shortcuts%20registered./singular: 7f1ed5d777cade7d62303e9e591bbf63
No%20lists/singular: cedf633d99c77ff4356e089f2d98c0a6
No%20results%20found%20for%20%22%7BdebouncedQuery%7D%22./singular: 5db6294712528cd897b15ae36f4fd834
Offer/singular: 82b4e0c9a3f5b4bd93590847de7c32a1
Onboarding/singular: 52b23f9c62ff199d4c09920e7641829e
Once%20you%20delete%20your%20account%2C%20there%20is%20no%20going%20back.%20This%20action%20cannot%20be%20undone./singular: 9cf7aa6ef30890e5124e266c081bae1c
Once%20you%20delete%20your%20workspace%2C%20there%20is%20no%20going%20back.%20This%20action%20cannot%20be%20undone./singular: c9ce6a516ed1f361653e8f8db3dd5ffd
Open%20command%20menu/singular: cf00563ea1a994e7c36a761d56664acc
Open%20keyboard%20shortcuts/singular: 638ae0999fd03d2c611064274859422e
Open%20source/singular: 81a747dc664de1a3389b495c8f84843b
Open%20Source%20Friends/singular: ffe3d75e5d03b8469c9dcb019dceb164
or/singular: 7b133c38bec0d5ee23cc6bcf9a8de50b
Organize%20and%20find%20cards%20quickly%20with%20powerful%20filtering%20tools./singular: 1b9898c4b21e9dff413b4f76dc59db56
OSS%20Friends/singular: 706e10666dfe26130c17fedb5366a25d
Overdue/singular: 24caaa2b5d7a2447ab7664e3771cf98c
Own%20your%20data/singular: cc2178dac4bdf6b07f030cfc2a7510e6
Owned%20by%20Atlassian/singular: ace4ed076a5318ad48c296fa09afed69
Part-time/singular: 213d63da450f35dabb3ab0e35e29feed
@@ -391,6 +412,7 @@ checksums:
removed%20a%20label%20from%20the%20card/singular: f83ec18850a2a8a3f8242a740a04df06
removed%20a%20member%20from%20the%20card/singular: 0bd7561bb79358fde3d595c3e60a315a
removed%20label%20%3C0%3E%7B0%7D%3C%2F0%3E/singular: 7e0936b15831e65754588b8512f62fcb
removed%20the%20due%20date/singular: 000c084844718540fcad542dd8caf8b4
renamed%20a%20checklist/singular: 4d208de1857740e63469007c5b8b491a
renamed%20checklist%20%3C0%3E%7B0%7D%3C%2F0%3E/singular: e95d119ef65b0af6c0a96bef182be671
renamed%20checklist%20item%20to%20%3C0%3E%7B0%7D%3C%2F0%3E/singular: 50f55f71f9245890afee434d7adc2140
@@ -415,12 +437,15 @@ checksums:
Self-hostable/singular: 3e3597145cc17f03b8b5646b80d59592
Send%20feedback/singular: 9631cc08d49da04475b30a0d320ce97c
Senior/singular: 3fff865dc00435f82896fc302ea45630
Set%20due%20date/singular: 3cb81c4829857556f5d117c9a23f7bcc
set%20the%20due%20date/singular: b5d9a1edd988f455041aee9fa7fb3d63
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
Shortcuts/singular: db3330ed3240c398054f3be23c52851f
Sign%20in/singular: cb8757c7450e17de1e226e82fb0fa4a2
Sign%20In/singular: ec7b8f314fe9bc6591006707484ede61
Sign%20Up/singular: 0dd2ae69be4618c1f9e615774a4509ca
@@ -495,6 +520,7 @@ checksums:
Unable%20to%20update%20checklist/singular: 44c10f49f448909ceca481ed9b4b07e7
Unable%20to%20update%20checklist%20item/singular: 5c86b3523531c30d58856e3ecde0cb55
Unable%20to%20update%20comment/singular: 05e798ed6b0f3fee41f3e8832eb699ea
Unable%20to%20update%20due%20date/singular: af20d7482cb639a8c8264c681a18abed
Unable%20to%20update%20labels/singular: dca2bdc3dcf74bc9d95e05156039a291
Unable%20to%20update%20list/singular: 14aa802f91b9b4c05236c8c75afb33da
Unable%20to%20update%20members/singular: 9a851a6b0c75ee16d25cf2ff4b67b255
@@ -508,10 +534,12 @@ checksums:
Unlimited%20lists/singular: 5c418701b27b9acbc5dd199719007362
Unlimited%20members/singular: f2949ca2dc18b0063af92e60779abb65
Unlimited%20workspaces/singular: b504d01997bf908af1d486d9942fed36
Unsubscribe/singular: d1a0b07295687d1f08d4df1c411072dd
Update/singular: 079fc039262fd31b10532929685c2d1b
Update%20label/singular: 97880b503dfe956941c5f23025a1c102
updated%20a%20checklist%20item/singular: 198e1d1d503f4b69b752dd84d0cc0e9d
updated%20the%20description/singular: 70fd604b6dc957a75be90ea77572d76d
updated%20the%20due%20date/singular: 7636c200d50fed58a4295ed1e9b5c1e0
updated%20the%20title/singular: 98cf8f12923ec1a58e767e3a40863b21
updated%20the%20title%20to%20%3C0%3E%7B0%7D%3C%2F0%3E/singular: aca196d729401d8adc5c8ca9d558840e
Upgrade%20to%20Pro/singular: 972773025e763ddf53273df6d37a729a
@@ -533,6 +561,7 @@ checksums:
View%20workspace/singular: c9e4b789330b08c18ca8db7044409e5f
Visibility/singular: 1ff412f8fcde0f460acbf11153649646
We%20are%20using%20the%20%3C0%3EAGPL-3.0%20license%3C%2F0%3E./singular: 08c5ddd938a2a70f61d684e41e18697a
We%20couldn't%20update%20your%20preferences.%20Please%20try%20again./singular: 69d1d2575f2e73b08ac622bde0a45b4d
We're%20just%20getting%20started.%20/singular: 2dfa121e0ff1914e08068308a81973c0
Welcome%20back/singular: 4928884739ba559e6e4b960e80fe1452
What%20license%20are%20you%20using%3F/singular: 59d00e1bf28edc21b77fec94875f57ca
@@ -565,6 +594,7 @@ checksums:
You%20can%20self-host%20by%20following%20the%20instructions%20in%20our%20%3C0%3Erepo%3C%2F0%3E./singular: a6152a41b2d8f64d5a657e5b8bc808a9
You%20have%20been%20logged%20in%20successfully./singular: ef8fad1dce13ae4112f17c5258655fea
You%20have%20been%20signed%20up%20successfully./singular: f614a6e3b45f5ffb9a3b0fb420fef84b
You%20have%20been%20unsubscribed!/singular: e0b9985faa4e7f25b71acc77750923a6
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
@@ -576,6 +606,7 @@ checksums:
Your%20profile%20image%20has%20been%20updated./singular: 099e1780348cd7bf617721344371a430
Your%20Trello%20account%20has%20been%20disconnected./singular: 126aeeb73f7e4cad338056e4c933f39b
Your%20Trello%20account%20is%20connected./singular: 0499d193001fce2fbec445ae034ce512
Your%20unsubscribe%20link%20is%20missing%20a%20token.%20Please%20open%20the%20latest%20email%20and%20try%20again./singular: 3e4cce96bc1197a1caa2b7fb4d3c7e53
Your%20workspace%20description%20has%20been%20updated./singular: 98de6f65c567c6654cc0adce3c9d5656
Your%20workspace%20has%20been%20deleted./singular: e7a3efcfc7dd18cb3e917acb67498292
Your%20workspace%20name%20has%20been%20updated./singular: a87ea3b0d71e6dc5dd525d77114a9322

View File

@@ -1,7 +1,7 @@
import type { LinguiConfig } from "@lingui/conf";
const config: LinguiConfig = {
locales: ["en", "fr", "de", "es", "it", "nl", "ru", "pl"],
locales: ["en", "fr", "de", "es", "it", "nl", "ru", "pl","ptbr"],
sourceLocale: "en",
catalogs: [
{

View File

@@ -26,9 +26,11 @@ const config = {
],
/** We already do linting and typechecking as separate tasks in CI */
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
// temporarily ignore eslint errors during build until we fix all the errors sigh
eslint: { ignoreDuringBuilds: true },
images: {
remotePatterns: (() => {
/** @type {Array<{protocol: "http" | "https", hostname: string}>} */
@@ -75,12 +77,13 @@ const config = {
return patterns;
})(),
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ["@svgr/webpack"],
});
return config;
turbopack: {
rules: {
"*.svg": {
loaders: ["@svgr/webpack"],
as: "*.js",
},
},
},
experimental: {
// instrumentationHook: true,

View File

@@ -4,9 +4,9 @@
"private": true,
"type": "module",
"scripts": {
"build": "pnpm with-env next build",
"build": "pnpm with-env next build --turbo",
"clean": "git clean -xdf .cache .next .turbo node_modules",
"dev": "pnpm with-env next dev",
"dev": "pnpm with-env next dev --turbo",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"start": "pnpm with-env next start",
@@ -29,6 +29,7 @@
"@lingui/conf": "^5.3.2",
"@lingui/macro": "^5.3.2",
"@lingui/react": "^5.3.2",
"@novu/api": "^3.11.0",
"@t3-oss/env-nextjs": "^0.11.1",
"@tailwindcss/typography": "^0.5.16",
"@tanstack/react-query": "catalog:",
@@ -43,15 +44,13 @@
"@trpc/next": "^11.0.0-rc.660",
"@trpc/react-query": "catalog:",
"@trpc/server": "catalog:",
"aws-sdk": "^2.1692.0",
"date-fns": "^4.1.0",
"geist": "^1.3.1",
"next": "^15.3.4",
"next-logger": "^5.0.1",
"jose": "^6.1.2",
"next": "15.5.9",
"next-runtime-env": "^1.7.2",
"next-themes": "^0.4.6",
"nextjs-cors": "^2.2.0",
"pino": "^9.6.0",
"posthog-js": "^1.254.0",
"react": "catalog:react18",
"react-beautiful-dnd": "^13.1.1",
@@ -85,7 +84,6 @@
"dotenv-cli": "^7.4.4",
"eslint": "catalog:",
"jiti": "^1.21.6",
"pino-pretty": "^13.0.0",
"prettier": "catalog:",
"tailwindcss": "catalog:",
"typescript": "catalog:"

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,136 @@
import {
addMonths,
eachDayOfInterval,
endOfMonth,
endOfWeek,
format,
isSameDay,
isToday,
startOfMonth,
startOfWeek,
subMonths,
} from "date-fns";
import { useMemo, useState } from "react";
import { HiChevronLeft, HiChevronRight } from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
interface DateSelectorProps {
selectedDate?: Date | null;
onDateSelect?: (date: Date | undefined) => void;
}
const DateSelector = ({ selectedDate, onDateSelect }: DateSelectorProps) => {
const [currentMonth, setCurrentMonth] = useState(() => {
return selectedDate ? startOfMonth(selectedDate) : startOfMonth(new Date());
});
const monthName = format(currentMonth, "MMMM");
const year = format(currentMonth, "yyyy");
const dayHeaders = useMemo(() => {
const weekStart = startOfWeek(new Date(), { weekStartsOn: 1 }); // Monday
return eachDayOfInterval({
start: weekStart,
end: new Date(weekStart.getTime() + 6 * 24 * 60 * 60 * 1000),
}).map((date) => format(date, "EEEEEE")); // Shortest localized day name
}, []);
const days = useMemo(() => {
const monthStart = startOfMonth(currentMonth);
const monthEnd = endOfMonth(currentMonth);
const calendarStart = startOfWeek(monthStart, { weekStartsOn: 1 }); // Monday
const calendarEnd = endOfWeek(monthEnd, { weekStartsOn: 1 }); // Monday
return eachDayOfInterval({ start: calendarStart, end: calendarEnd }).map(
(date) => {
const dateString = format(date, "yyyy-MM-dd");
return {
date: dateString,
isToday: isToday(date),
isSelected: selectedDate ? isSameDay(date, selectedDate) : false,
isCurrentMonth: date >= monthStart && date <= monthEnd,
dateObj: date,
};
},
);
}, [currentMonth, selectedDate]);
const handlePreviousMonth = () => {
setCurrentMonth(subMonths(currentMonth, 1));
};
const handleNextMonth = () => {
setCurrentMonth(addMonths(currentMonth, 1));
};
const handleDateClick = (date: Date, e: React.MouseEvent) => {
e.stopPropagation();
// If clicking the same date that's already selected, unselect it
if (selectedDate && isSameDay(date, selectedDate)) {
onDateSelect?.(undefined);
} else {
onDateSelect?.(date);
}
};
return (
<div className="w-[250px] p-4">
<div className="flex items-center text-light-1000 dark:text-dark-1000">
<button
type="button"
onClick={handlePreviousMonth}
className="flex flex-none items-center justify-center p-1.5 text-light-700 hover:text-light-900 dark:text-dark-700 dark:hover:text-dark-1000"
>
<span className="sr-only">Previous month</span>
<HiChevronLeft aria-hidden="true" className="h-4 w-4" />
</button>
<div className="flex-1 text-center text-sm font-semibold">
{monthName} {year}
</div>
<button
type="button"
onClick={handleNextMonth}
className="flex flex-none items-center justify-center p-1.5 text-light-700 hover:text-light-900 dark:text-dark-700 dark:hover:text-dark-1000"
>
<span className="sr-only">Next month</span>
<HiChevronRight aria-hidden="true" className="h-4 w-4" />
</button>
</div>
<div className="mt-6 grid grid-cols-7 text-center text-xs/6 text-light-950 dark:text-dark-950">
{dayHeaders.map((day, index) => (
<div key={index}>{day}</div>
))}
</div>
<div className="isolate mt-2 grid grid-cols-7 text-sm">
{days.map((day) => (
<button
key={day.date}
type="button"
onClick={(e) => handleDateClick(day.dateObj, e)}
className={twMerge(
"flex aspect-square items-center justify-center rounded-lg focus:z-10",
day.isSelected
? "bg-light-1000 hover:bg-light-1000 dark:bg-dark-1000 dark:hover:bg-dark-1000"
: "bg-transparent hover:bg-light-200 dark:bg-transparent dark:hover:bg-dark-200",
)}
>
<time
dateTime={day.date}
className={twMerge(
"mx-auto flex size-7 items-center justify-center rounded-full text-light-900 dark:text-dark-900",
day.isCurrentMonth
? "text-light-900 dark:text-dark-900"
: "text-light-700 dark:text-dark-600",
day.isSelected && "text-light-50 dark:text-dark-50",
)}
>
{day.date.split("-").pop()?.replace(/^0/, "")}
</time>
</button>
))}
</div>
</div>
);
};
export default DateSelector;

View File

@@ -2,8 +2,10 @@ import Link from "next/link";
import { useState } from "react";
import { twMerge } from "tailwind-merge";
import type { KeyboardShortcut } from "~/providers/keyboard-shortcuts";
import LottieIcon from "~/components/LottieIcon";
import { useIsMobile } from "~/hooks/useMediaQuery";
import { useKeyboardShortcut } from "~/providers/keyboard-shortcuts";
const Button: React.FC<{
href: string;
@@ -12,10 +14,20 @@ const Button: React.FC<{
json: object;
isCollapsed?: boolean;
onCloseSideNav?: () => void;
}> = ({ href, current, name, json, isCollapsed = false, onCloseSideNav }) => {
keyboardShortcut: KeyboardShortcut;
}> = ({
href,
current,
name,
json,
isCollapsed = false,
keyboardShortcut,
onCloseSideNav,
}) => {
const [isHovered, setIsHovered] = useState(false);
const [index, setIndex] = useState(0);
const isMobile = useIsMobile();
const { keys: shortcutKeys } = useKeyboardShortcut(keyboardShortcut);
const handleMouseEnter = () => {
setIsHovered(true);
@@ -34,18 +46,27 @@ const Button: React.FC<{
onMouseEnter={handleMouseEnter}
onClick={handleClick}
className={twMerge(
"group flex h-[34px] items-center rounded-md p-1.5 text-sm font-normal leading-6 hover:bg-light-200 hover:text-light-1000 dark:hover:bg-dark-200 dark:hover:text-dark-1000",
"group flex h-[34px] items-center justify-between rounded-md p-1.5 text-sm font-normal leading-6 hover:bg-light-200 hover:text-light-1000 dark:hover:bg-dark-200 dark:hover:text-dark-1000",
current
? "bg-light-200 text-light-1000 dark:bg-dark-200 dark:text-dark-1000"
: "text-neutral-600 dark:bg-dark-100 dark:text-dark-900",
isCollapsed
? "justify-start gap-x-3 md:justify-center md:gap-x-0"
: "gap-x-3",
)}
title={isCollapsed ? name : undefined}
>
<LottieIcon index={index} json={json} isPlaying={isHovered} />
<span className={twMerge(isCollapsed && "md:hidden")}>{name}</span>
<div
className={twMerge(
"flex items-center",
isCollapsed
? "justify-start gap-x-3 md:justify-center md:gap-x-0"
: "gap-x-3",
)}
>
<LottieIcon index={index} json={json} isPlaying={isHovered} />
<span className={twMerge(isCollapsed && "md:hidden")}>{name}</span>
</div>
{!isCollapsed && (
<div className="hidden md:group-hover:inline-flex">{shortcutKeys}</div>
)}
</Link>
);
};

View File

@@ -15,6 +15,7 @@ import { twMerge } from "tailwind-merge";
import type { Subscription } from "@kan/shared/utils";
import { hasActiveSubscription } from "@kan/shared/utils";
import type { KeyboardShortcut } from "~/providers/keyboard-shortcuts";
import boardsIconDark from "~/assets/boards-dark.json";
import boardsIconLight from "~/assets/boards-light.json";
import membersIconDark from "~/assets/members-dark.json";
@@ -86,26 +87,59 @@ export default function SideNavigation({
const isDarkMode = resolvedTheme === "dark";
const navigation = [
const navigation: {
name: string;
href: string;
icon: object;
keyboardShortcut: KeyboardShortcut;
}[] = [
{
name: t`Boards`,
href: "/boards",
icon: isDarkMode ? boardsIconDark : boardsIconLight,
keyboardShortcut: {
type: "SEQUENCE",
strokes: [{ key: "G" }, { key: "B" }],
action: () => router.push("/boards"),
group: "NAVIGATION",
description: t`Go to boards`,
},
},
{
name: t`Templates`,
href: "/templates",
icon: isDarkMode ? templatesIconDark : templatesIconLight,
keyboardShortcut: {
type: "SEQUENCE",
strokes: [{ key: "G" }, { key: "T" }],
action: () => router.push("/templates"),
group: "NAVIGATION",
description: t`Go to templates`,
},
},
{
name: t`Members`,
href: "/members",
icon: isDarkMode ? membersIconDark : membersIconLight,
keyboardShortcut: {
type: "SEQUENCE",
strokes: [{ key: "G" }, { key: "M" }],
action: () => router.push("/members"),
group: "NAVIGATION",
description: t`Go to members`,
},
},
{
name: t`Settings`,
href: "/settings",
icon: isDarkMode ? settingsIconDark : settingsIconLight,
keyboardShortcut: {
type: "SEQUENCE",
strokes: [{ key: "G" }, { key: "S" }],
action: () => router.push("/settings"),
group: "NAVIGATION",
description: t`Go to settings`,
},
},
];
@@ -163,6 +197,7 @@ export default function SideNavigation({
json={item.icon}
isCollapsed={isCollapsed}
onCloseSideNav={onCloseSideNav}
keyboardShortcut={item.keyboardShortcut}
/>
</li>
))}

View File

@@ -0,0 +1,51 @@
import type { ReactNode } from "react";
import type { Root } from "react-dom/client";
import type { Placement } from "tippy.js";
import { useEffect, useRef } from "react";
import { createRoot } from "react-dom/client";
import tippy from "tippy.js";
interface TooltipProps {
children: ReactNode;
content: ReactNode;
placement?: Placement;
delay?: number | [number, number];
}
export function Tooltip({
children,
content,
placement = "bottom",
delay = [500, 0],
}: TooltipProps) {
const triggerRef = useRef<HTMLDivElement>(null);
const rootRef = useRef<Root | null>(null);
useEffect(() => {
if (!triggerRef.current) return;
const container = document.createElement("div");
const root = createRoot(container);
rootRef.current = root;
root.render(content);
const instance = tippy(triggerRef.current, {
content: container,
placement,
delay,
interactive: false,
theme: "tooltip",
});
return () => {
instance.destroy();
rootRef.current?.unmount();
};
}, [content, placement, delay]);
return (
<div ref={triggerRef} className="inline-flex">
{children}
</div>
);
}

View File

@@ -10,6 +10,7 @@ import { twMerge } from "tailwind-merge";
import { authClient } from "@kan/auth/client";
import { useIsMobile } from "~/hooks/useMediaQuery";
import { useKeyboardShortcuts } from "~/providers/keyboard-shortcuts";
import { useModal } from "~/providers/modal";
import { getAvatarUrl } from "~/utils/helpers";
@@ -31,6 +32,7 @@ export default function UserMenu({
const router = useRouter();
const { theme, setTheme } = useTheme();
const { openModal } = useModal();
const { openLegend } = useKeyboardShortcuts();
const isMobile = useIsMobile();
const handleLogout = async () => {
@@ -169,6 +171,19 @@ export default function UserMenu({
</Menu.Item>
</div>
<div className="light-border-600 border-t-[1px] p-1 dark:border-dark-600">
<Menu.Item>
<button
onClick={() => {
if (onCloseSideNav && isMobile) {
onCloseSideNav();
}
openLegend();
}}
className="flex w-full items-center rounded-[5px] px-3 py-2 text-left text-xs hover:bg-light-200 dark:hover:bg-dark-400"
>
{t`Shortcuts`}
</button>
</Menu.Item>
<Menu.Item>
<Link
href="mailto:support@kan.bn"

View File

@@ -1,12 +1,14 @@
import { Button, Menu, Transition } from "@headlessui/react";
import { t } from "@lingui/core/macro";
import { Fragment, useEffect, useState } from "react";
import { Fragment, useState } from "react";
import { HiCheck, HiMagnifyingGlass } from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
import { useKeyboardShortcut } from "~/providers/keyboard-shortcuts";
import { useModal } from "~/providers/modal";
import { useWorkspace } from "~/providers/workspace";
import CommandPallette from "./CommandPallette";
import { Tooltip } from "./Tooltip";
export default function WorkspaceMenu({
isCollapsed = false,
@@ -18,17 +20,17 @@ export default function WorkspaceMenu({
const { openModal } = useModal();
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if ((event.metaKey || event.ctrlKey) && event.key === "k") {
event.preventDefault();
setIsOpen(true);
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, []);
const { tooltipContent: commandPaletteShortcutTooltipContent } =
useKeyboardShortcut({
type: "PRESS",
stroke: {
key: "k",
modifiers: ["META"],
},
action: () => setIsOpen(true),
description: t`Open command menu`,
group: "GENERAL",
});
return (
<>
@@ -84,15 +86,17 @@ export default function WorkspaceMenu({
</span>
)}
</Menu.Button>
<Button
className={twMerge(
"mb-1 h-[34px] w-[34px] flex-shrink-0 rounded-lg bg-light-200 p-2 hover:bg-light-300 focus:outline-none dark:bg-dark-200 dark:hover:bg-dark-300",
isCollapsed && "md:mb-2 md:h-9 md:w-9",
)}
onClick={() => setIsOpen(true)}
>
<HiMagnifyingGlass className="h-4 w-4" aria-hidden="true" />
</Button>
<Tooltip content={commandPaletteShortcutTooltipContent}>
<Button
className={twMerge(
"mb-1 h-[34px] w-[34px] flex-shrink-0 rounded-lg bg-light-200 p-2 hover:bg-light-300 focus:outline-none dark:bg-dark-200 dark:hover:bg-dark-300",
isCollapsed && "md:mb-2 md:h-9 md:w-9",
)}
onClick={() => setIsOpen(true)}
>
<HiMagnifyingGlass className="h-4 w-4" aria-hidden="true" />
</Button>
</Tooltip>
</div>
)}
</div>

View File

@@ -51,6 +51,8 @@ export const env = createEnv({
VK_CLIENT_SECRET: z.string().optional(),
LINKEDIN_CLIENT_ID: z.string().optional(),
LINKEDIN_CLIENT_SECRET: z.string().optional(),
NOVU_API_KEY: z.string().optional(),
EMAIL_UNSUBSCRIBE_SECRET: z.string().optional(),
// Generic OIDC Provider
OIDC_CLIENT_ID: z.string().optional(),
OIDC_CLIENT_SECRET: z.string().optional(),
@@ -87,7 +89,7 @@ export const env = createEnv({
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
NEXT_PUBLIC_POSTHOG_HOST: z.string().optional(),
NEXT_PUBLIC_USE_STANDALONE_OUTPUT: z.string().optional(),
NEXT_PUBLIC_BASE_URL: z.string().url(),
NEXT_PUBLIC_BASE_URL: z.string().url().optional(),
NEXT_PUBLIC_STORAGE_URL: z.string().url().optional(),
NEXT_PUBLIC_AVATAR_BUCKET_NAME: z.string().optional(),
NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME: z.string().optional(),

View File

@@ -1,4 +1,6 @@
import type { Locale as DateFnsLocale } from "date-fns";
import { useLingui } from "@lingui/react";
import { de, enGB, es, fr, it, nl, pl, ru, ptBR} from "date-fns/locale";
import type { Locale } from "~/locales";
import { useLinguiContext } from "~/providers/lingui";
@@ -8,6 +10,20 @@ export function useLocalisation() {
const { i18n } = useLingui();
const { locale, setLocale, availableLocales } = useLinguiContext();
const dateLocaleMap: Partial<Record<Locale, DateFnsLocale>> = {
en: enGB,
fr,
de,
es,
it,
nl,
ru,
pl,
ptbr: ptBR,
};
const currentDateLocale = dateLocaleMap[locale] ?? enGB;
const handleSetLocale = async (newLocale: Locale) => {
await activateLocale(newLocale);
setLocale(newLocale);
@@ -15,9 +31,9 @@ export function useLocalisation() {
return {
locale,
dateLocale: currentDateLocale,
setLocale: handleSetLocale,
availableLocales,
formatDate: i18n.date,
formatNumber: i18n.number,
};
}

View File

@@ -1,6 +0,0 @@
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await require("pino");
await require("next-logger");
}
}

View File

@@ -22,7 +22,7 @@ msgid ", or see our"
msgstr ", oder besuche unsere"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#: src/views/boards/index.tsx:27
#: src/views/boards/index.tsx:38
msgid "{0}"
msgstr "{0}"
@@ -30,18 +30,18 @@ msgstr "{0}"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:218
#: src/views/boards/index.tsx:33
#: src/views/card/index.tsx:272
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:443
#: src/views/board/components/NewCardForm.tsx:452
msgid "{0} labels"
msgstr "{0} Labels"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:389
#: src/views/board/index.tsx:412
msgid "{0} not found"
msgstr "{0} nicht gefunden"
@@ -79,8 +79,12 @@ msgstr "Konto"
msgid "Account deleted"
msgstr "Konto gelöscht"
#: src/views/card/index.tsx:308
#: src/views/public/board/CardModal.tsx:157
#: src/providers/keyboard-shortcuts.tsx:55
msgid "Actions"
msgstr "Aktionen"
#: src/views/card/index.tsx:386
#: src/views/public/board/CardModal.tsx:158
msgid "Activity"
msgstr "Aktivität"
@@ -127,19 +131,19 @@ msgstr "Label hinzufügen"
msgid "Add member"
msgstr "Mitglied hinzufügen"
#: src/views/card/components/ActivityList.tsx:69
#: src/views/card/components/ActivityList.tsx:66
msgid "added a checklist"
msgstr "hat eine Checkliste hinzugefügt"
#: src/views/card/components/ActivityList.tsx:72
#: src/views/card/components/ActivityList.tsx:69
msgid "added a checklist item"
msgstr "hat ein Checklistenelement hinzugefügt"
#: src/views/card/components/ActivityList.tsx:65
#: src/views/card/components/ActivityList.tsx:62
msgid "added a label to the card"
msgstr "hat ein Label zur Karte hinzugefügt"
#: src/views/card/components/ActivityList.tsx:67
#: src/views/card/components/ActivityList.tsx:64
msgid "added a member to the card"
msgstr "hat ein Mitglied zur Karte hinzugefügt"
@@ -308,9 +312,9 @@ msgstr "Abrechnungsportal"
msgid "Blog Post"
msgstr "Blogbeitrag"
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:218
#: src/views/public/board/index.tsx:101
#: src/views/board/index.tsx:386
#: src/views/card/index.tsx:272
#: src/views/public/board/index.tsx:113
msgid "Board"
msgstr "Board"
@@ -327,7 +331,7 @@ msgstr "Der Name des Boards darf 100 Zeichen nicht überschreiten"
msgid "Board name is required"
msgstr "Boardname ist erforderlich"
#: src/views/public/board/index.tsx:153
#: src/views/public/board/index.tsx:165
msgid "Board not found"
msgstr "Board nicht gefunden"
@@ -343,7 +347,7 @@ msgstr "Board-URL darf nur Buchstaben, Zahlen und Bindestriche enthalten"
msgid "Board URL cannot exceed 60 characters"
msgstr "Board-URL darf 60 Zeichen nicht überschreiten"
#: src/views/public/board/index.tsx:68
#: src/views/public/board/index.tsx:79
msgid "Board URL copied to clipboard"
msgstr "Board-URL in die Zwischenablage kopiert"
@@ -360,7 +364,7 @@ msgstr "Board-Sichtbarkeit"
msgid "Board visibility updated"
msgstr "Board-Sichtbarkeit aktualisiert"
#: src/components/SideNavigation.tsx:91
#: src/components/SideNavigation.tsx:97
msgid "Boards"
msgstr "Boards"
@@ -391,15 +395,16 @@ msgstr "Fehlerbericht"
msgid "Cancel"
msgstr "Abbrechen"
#: src/views/card/index.tsx:218
#: src/views/card/index.tsx:272
msgid "Card"
msgstr "Karte"
#: src/views/card/index.tsx:264
#: src/views/card/index.tsx:306
#: src/views/card/index.tsx:342
msgid "Card not found"
msgstr "Karte nicht gefunden"
#: src/views/board/components/NewCardForm.tsx:310
#: src/views/board/components/NewCardForm.tsx:319
msgid "Card title"
msgstr "Kartentitel"
@@ -414,6 +419,11 @@ msgstr "Passwort ändern"
msgid "Change your language preferences."
msgstr "Ändern Sie Ihre Spracheinstellungen."
#: src/views/card/components/ActivityList.tsx:220
#: src/views/card/components/ActivityList.tsx:234
msgid "changed the due date to <0>{formattedDate}</0>"
msgstr "hat den Fälligkeitstermin auf <0>{formattedDate}</0> geändert"
#: src/views/oss-friends/index.tsx:61
msgid "Check out some of our favorite open source projects."
msgstr "Entdecken Sie einige unserer liebsten Open-Source-Projekte."
@@ -432,7 +442,7 @@ msgstr "Checklistenname"
msgid "Checklists"
msgstr "Checklisten"
#: src/views/board/components/Filters.tsx:180
#: src/views/board/components/Filters.tsx:227
msgid "Clear filters"
msgstr "Filter löschen"
@@ -484,7 +494,7 @@ msgstr "Abgeschlossen"
msgid "Complete control and ownership:"
msgstr "Vollständige Kontrolle und Eigentum:"
#: src/views/card/components/ActivityList.tsx:74
#: src/views/card/components/ActivityList.tsx:71
msgid "completed a checklist item"
msgstr "hat ein Checklistenelement abgeschlossen"
@@ -493,6 +503,10 @@ msgstr "hat ein Checklistenelement abgeschlossen"
msgid "completed checklist item <0>{0}</0>"
msgstr "hat Checklistenelement <0>{0}</0> abgeschlossen"
#: src/pages/unsubscribe/index.tsx:81
msgid "Confirm your email preferences:"
msgstr "Bestätigen sie ihre e-mail-einstellungen:"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:150
msgid "Confirm your new password"
msgstr "Bestätigen Sie Ihr neues Passwort"
@@ -523,12 +537,12 @@ msgstr "Kontaktiere uns"
msgid "Content Creation"
msgstr "Content-Erstellung"
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Continue with "
msgstr "Fortfahren mit "
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
#: src/components/AuthForm.tsx:351
#: src/components/AuthForm.tsx:355
msgid "Continue with {0}"
msgstr "Fortfahren mit {0}"
@@ -546,7 +560,7 @@ msgid "Create {0}"
msgstr "{0} erstellen"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewCardForm.tsx:516
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Weitere erstellen"
@@ -555,7 +569,7 @@ msgstr "Weitere erstellen"
msgid "Create API key"
msgstr "API-Schlüssel erstellen"
#: src/views/board/components/NewCardForm.tsx:479
#: src/views/board/components/NewCardForm.tsx:526
msgid "Create card"
msgstr "Karte erstellen"
@@ -577,6 +591,7 @@ msgstr "Liste erstellen"
#. placeholder {0}: isTemplate ? "template" : "board"
#: src/views/boards/components/BoardsList.tsx:45
#: src/views/boards/index.tsx:26
msgid "Create new {0}"
msgstr "Neue(n) {0} erstellen"
@@ -584,12 +599,13 @@ msgstr "Neue(n) {0} erstellen"
msgid "Create new key"
msgstr "Neuen Schlüssel erstellen"
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/board/components/NewCardForm.tsx:414
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Neues Label erstellen"
#: src/views/board/index.tsx:478
#: src/views/board/index.tsx:65
#: src/views/board/index.tsx:503
msgid "Create new list"
msgstr "Neue Liste erstellen"
@@ -598,11 +614,11 @@ msgid "Create template"
msgstr "Vorlage erstellen"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:149
#: src/components/WorkspaceMenu.tsx:153
msgid "Create workspace"
msgstr "Arbeitsbereich erstellen"
#: src/views/card/components/ActivityList.tsx:61
#: src/views/card/components/ActivityList.tsx:58
msgid "created the card"
msgstr "hat die Karte erstellt"
@@ -648,7 +664,7 @@ msgstr "Benutzerdefinierte Workspace-URL"
msgid "Customer Support"
msgstr "Kundensupport"
#: src/components/UserMenu.tsx:153
#: src/components/UserMenu.tsx:155
msgid "Dark"
msgstr "Dunkel"
@@ -697,11 +713,11 @@ msgstr "Vorlage löschen"
msgid "Delete workspace"
msgstr "Workspace löschen"
#: src/views/card/components/ActivityList.tsx:71
#: src/views/card/components/ActivityList.tsx:68
msgid "deleted a checklist"
msgstr "hat eine Checkliste gelöscht"
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:73
msgid "deleted a checklist item"
msgstr "hat ein Checklistenelement gelöscht"
@@ -747,6 +763,10 @@ msgstr "Anzeigename aktualisiert"
msgid "Do you offer a free plan?"
msgstr "Bietet ihr einen kostenlosen Plan an?"
#: src/pages/unsubscribe/index.tsx:78
msgid "Do you want to unsubscribe?"
msgstr "Möchten sie sich abmelden?"
#: src/components/FeedbackModal.tsx:109
msgid "docs"
msgstr "Dokumente"
@@ -755,7 +775,7 @@ msgstr "Dokumente"
msgid "Docs"
msgstr "Dokumente"
#: src/components/UserMenu.tsx:191
#: src/components/UserMenu.tsx:206
#: src/views/boards/components/TemplateBoards.tsx:24
#: src/views/home/components/Footer.tsx:78
#: src/views/home/components/Header.tsx:36
@@ -777,6 +797,28 @@ msgstr "Erledigt"
msgid "Download failed"
msgstr "Download fehlgeschlagen"
#: src/views/board/components/Filters.tsx:171
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/card/index.tsx:138
msgid "Due date"
msgstr "Fälligkeitsdatum"
#: src/views/board/components/Filters.tsx:132
msgid "Due next month"
msgstr "Fällig nächsten Monat"
#: src/views/board/components/Filters.tsx:127
msgid "Due next week"
msgstr "Fällig nächste Woche"
#: src/views/board/components/Filters.tsx:117
msgid "Due today"
msgstr "Heute fällig"
#: src/views/board/components/Filters.tsx:122
msgid "Due tomorrow"
msgstr "Morgen fällig"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
@@ -798,7 +840,7 @@ msgstr "Workspace-URL bearbeiten"
msgid "Editing"
msgstr "Bearbeitung"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "E-Mail"
@@ -818,11 +860,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:398
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Gib deine E-Mail-Adresse ein"
#: src/components/AuthForm.tsx:384
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Gib deinen Namen ein"
@@ -830,7 +872,7 @@ msgstr "Gib deinen Namen ein"
msgid "Enter your new password"
msgstr "Geben Sie Ihr neues Passwort ein"
#: src/components/AuthForm.tsx:411
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Gib dein Passwort ein"
@@ -979,7 +1021,7 @@ msgid "Features"
msgstr "Features"
#: src/components/FeedbackModal.tsx:59
#: src/components/UserMenu.tsx:199
#: src/components/UserMenu.tsx:214
msgid "Feedback"
msgstr "Feedback"
@@ -987,7 +1029,7 @@ msgstr "Feedback"
msgid "Feedback sent"
msgstr "Feedback gesendet"
#: src/views/board/components/Filters.tsx:174
#: src/views/board/components/Filters.tsx:221
msgid "Filter"
msgstr "Filter"
@@ -1024,6 +1066,10 @@ msgstr "Vollzeit"
msgid "Fun"
msgstr "Spaß"
#: src/providers/keyboard-shortcuts.tsx:53
msgid "General"
msgstr "Allgemein"
#: src/views/auth/signup/index.tsx:69
#: src/views/home/components/Cta.tsx:61
#: src/views/home/components/Header.tsx:106
@@ -1043,7 +1089,7 @@ msgstr "Loslegen"
msgid "Get started by creating a new {0}"
msgstr "Beginnen Sie, indem Sie eine(n) neue(n) {0} erstellen"
#: src/views/board/index.tsx:470
#: src/views/board/index.tsx:495
msgid "Get started by creating a new list"
msgstr "Beginne mit dem Erstellen einer neuen Liste"
@@ -1077,6 +1123,22 @@ msgstr "Zur Startseite"
msgid "Go to app"
msgstr "Zur App"
#: src/components/SideNavigation.tsx:105
msgid "Go to boards"
msgstr "Zu Boards gehen"
#: src/components/SideNavigation.tsx:129
msgid "Go to members"
msgstr "Zu Mitgliedern gehen"
#: src/components/SideNavigation.tsx:141
msgid "Go to settings"
msgstr "Zu Einstellungen gehen"
#: src/components/SideNavigation.tsx:117
msgid "Go to templates"
msgstr "Zu Vorlagen gehen"
#: src/views/boards/components/TemplateBoards.tsx:18
msgid "High Priority"
msgstr "Hohe Priorität"
@@ -1126,7 +1188,7 @@ msgstr "Ideen"
msgid "Ideas to improve this page..."
msgstr "Ideen zur Verbesserung dieser Seite..."
#: src/views/boards/index.tsx:39
#: src/views/boards/index.tsx:50
msgid "Import"
msgstr "Importieren"
@@ -1257,9 +1319,13 @@ msgstr "Kanban neu gedacht"
msgid "Keep in mind that this action is irreversible."
msgstr "Beachten Sie, dass diese Aktion nicht rückgängig gemacht werden kann."
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
#: src/providers/keyboard-shortcuts.tsx:320
msgid "Keyboard Shortcuts"
msgstr "Tastaturkürzel"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/NewCardForm.tsx:418
#: src/views/card/index.tsx:120
msgid "Labels"
msgstr "Labels"
@@ -1305,15 +1371,15 @@ msgstr "Rechtliches"
msgid "License"
msgstr "Lizenz"
#: src/components/UserMenu.tsx:167
#: src/components/UserMenu.tsx:169
msgid "Light"
msgstr "Hell"
#: src/views/public/board/index.tsx:66
#: src/views/public/board/index.tsx:77
msgid "Link copied"
msgstr "Link kopiert"
#: src/views/card/index.tsx:111
#: src/views/card/index.tsx:112
msgid "List"
msgstr "Liste"
@@ -1321,7 +1387,7 @@ msgstr "Liste"
msgid "List name"
msgstr "Listenname"
#: src/views/board/components/Filters.tsx:123
#: src/views/board/components/Filters.tsx:163
msgid "Lists"
msgstr "Listen"
@@ -1329,7 +1395,7 @@ msgstr "Listen"
msgid "Login | kan.bn"
msgstr "Login | kan.bn"
#: src/components/UserMenu.tsx:209
#: src/components/UserMenu.tsx:224
msgid "Logout"
msgstr "Abmelden"
@@ -1345,7 +1411,7 @@ msgstr "Von Teams weltweit geliebt"
msgid "Low Priority"
msgstr "Niedrige Priorität"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "Magic Link"
@@ -1353,7 +1419,7 @@ msgstr "Magic Link"
msgid "Make template"
msgstr "Als Vorlage speichern"
#: src/views/card/components/ActivityList.tsx:75
#: src/views/card/components/ActivityList.tsx:72
msgid "marked a checklist item as incomplete"
msgstr "hat ein Checklistenelement als unvollständig markiert"
@@ -1366,10 +1432,10 @@ msgstr "hat Checklistenelement <0>{0}</0> als unvollständig markiert"
msgid "Medium Priority"
msgstr "Mittlere Priorität"
#: src/components/SideNavigation.tsx:101
#: src/views/board/components/Filters.tsx:107
#: src/views/board/components/NewCardForm.tsx:367
#: src/views/card/index.tsx:128
#: src/components/SideNavigation.tsx:121
#: src/views/board/components/Filters.tsx:147
#: src/views/board/components/NewCardForm.tsx:376
#: src/views/card/index.tsx:129
#: src/views/members/index.tsx:178
msgid "Members"
msgstr "Mitglieder"
@@ -1394,7 +1460,7 @@ msgstr "monatliche Abrechnung"
msgid "moved the card from <0>{0}</0> to<1>{1}</1>"
msgstr "hat die Karte von <0>{0}</0> nach <1>{1}</1> verschoben"
#: src/views/card/components/ActivityList.tsx:64
#: src/views/card/components/ActivityList.tsx:61
msgid "moved the card to another list"
msgstr "hat die Karte in eine andere Liste verschoben"
@@ -1404,11 +1470,15 @@ msgstr "hat die Karte in eine andere Liste verschoben"
msgid "Name"
msgstr "Name"
#: src/providers/keyboard-shortcuts.tsx:54
msgid "Navigation"
msgstr "Navigation"
#: src/components/FeedbackModal.tsx:95
msgid "Need help?"
msgstr "Brauchst du Hilfe?"
#: src/views/boards/index.tsx:50
#: src/views/boards/index.tsx:62
#: src/views/home/components/Features.tsx:73
msgid "New"
msgstr "Neu"
@@ -1422,7 +1492,7 @@ msgstr "Neue(r) {0}"
msgid "New API key"
msgstr "Neuer API-Schlüssel"
#: src/views/board/components/NewCardForm.tsx:293
#: src/views/board/components/NewCardForm.tsx:302
msgid "New card"
msgstr "Neue Karte"
@@ -1439,7 +1509,7 @@ msgid "New label"
msgstr "Neues Label"
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
#: src/views/board/index.tsx:466
msgid "New list"
msgstr "Neue Liste"
@@ -1476,7 +1546,7 @@ msgstr "Nächste Schritte"
msgid "No {0}"
msgstr "Keine {0}"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Derzeit sind keine Authentifizierungsmethoden verfügbar"
@@ -1488,11 +1558,19 @@ msgstr "Keine Boards gefunden"
msgid "No credit card required"
msgstr "Keine Kreditkarte erforderlich"
#: src/views/board/components/Filters.tsx:137
msgid "No dates"
msgstr "Keine Termine"
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "Für diesen Anhang ist keine Download-URL verfügbar."
#: src/views/board/index.tsx:467
#: src/providers/keyboard-shortcuts.tsx:333
msgid "No keyboard shortcuts registered."
msgstr "Keine Tastaturkürzel registriert."
#: src/views/board/index.tsx:492
msgid "No lists"
msgstr "Keine Listen"
@@ -1516,6 +1594,14 @@ msgstr "Sobald Sie Ihr Konto löschen, gibt es kein Zurück mehr. Diese Aktion k
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/WorkspaceMenu.tsx:31
msgid "Open command menu"
msgstr "Befehlsmenü öffnen"
#: src/providers/keyboard-shortcuts.tsx:171
msgid "Open keyboard shortcuts"
msgstr "Tastaturkürzel öffnen"
#: src/views/pricing/components/FeatureComparisonTable.tsx:150
msgid "Open source"
msgstr "Open Source"
@@ -1524,7 +1610,7 @@ msgstr "Open Source"
msgid "Open Source Friends"
msgstr "Open-Source-Freunde"
#: src/components/AuthForm.tsx:374
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "oder"
@@ -1537,6 +1623,10 @@ msgstr "Organisiere und finde Karten schnell mit leistungsstarken Filterwerkzeug
msgid "OSS Friends"
msgstr "OSS-Freunde"
#: src/views/board/components/Filters.tsx:112
msgid "Overdue"
msgstr "Überfällig"
#: src/views/pricing/components/Pricing.tsx:73
#: src/views/pricing/components/PricingTiers.tsx:70
msgid "Own your data"
@@ -1606,15 +1696,15 @@ msgstr "Plattform"
msgid "Please confirm your new password"
msgstr "Bitte bestätigen Sie Ihr neues Passwort"
#: src/components/AuthForm.tsx:402
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Bitte gib eine gültige E-Mail-Adresse ein"
#: src/components/AuthForm.tsx:388
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Bitte gib einen gültigen Namen ein"
#: src/components/AuthForm.tsx:416
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Bitte gib ein gültiges Passwort ein"
@@ -1625,14 +1715,14 @@ msgstr "Bitte wähle eine Datei zum Hochladen aus."
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:177
#: src/views/board/components/NewCardForm.tsx:184
#: src/views/board/components/NewListForm.tsx:79
#: src/views/board/components/NewTemplateForm.tsx:61
#: src/views/board/components/NewTemplateForm.tsx:77
#: src/views/board/components/UpdateBoardSlugForm.tsx:73
#: src/views/board/components/VisibilityButton.tsx:54
#: src/views/board/index.tsx:151
#: src/views/board/index.tsx:207
#: src/views/board/index.tsx:174
#: src/views/board/index.tsx:230
#: src/views/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
@@ -1642,13 +1732,15 @@ 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/DueDateSelector.tsx:59
#: 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/NewChecklistForm.tsx:70
#: src/views/card/components/NewChecklistItemForm.tsx:89
#: src/views/card/components/NewCommentForm.tsx:31
#: src/views/card/index.tsx:177
#: src/views/card/index.tsx:187
#: src/views/card/index.tsx:200
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:241
@@ -1748,11 +1840,11 @@ msgstr "Entfernen"
msgid "Remove member"
msgstr "Mitglied entfernen"
#: src/views/card/components/ActivityList.tsx:66
#: src/views/card/components/ActivityList.tsx:63
msgid "removed a label from the card"
msgstr "hat ein Label von der Karte entfernt"
#: src/views/card/components/ActivityList.tsx:68
#: src/views/card/components/ActivityList.tsx:65
msgid "removed a member from the card"
msgstr "hat ein Mitglied von der Karte entfernt"
@@ -1761,7 +1853,12 @@ msgstr "hat ein Mitglied von der Karte entfernt"
msgid "removed label <0>{0}</0>"
msgstr "hat Label <0>{0}</0> entfernt"
#: src/views/card/components/ActivityList.tsx:70
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:241
msgid "removed the due date"
msgstr "hat den Fälligkeitstermin entfernt"
#: src/views/card/components/ActivityList.tsx:67
msgid "renamed a checklist"
msgstr "hat eine Checkliste umbenannt"
@@ -1864,8 +1961,16 @@ msgstr "Feedback senden"
msgid "Senior"
msgstr "Senior"
#: src/views/card/components/DueDateSelector.tsx:116
msgid "Set due date"
msgstr "Fälligkeitsdatum festlegen"
#: src/views/card/components/ActivityList.tsx:74
msgid "set the due date"
msgstr "hat den Fälligkeitstermin festgelegt"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:106
#: src/components/SideNavigation.tsx:133
msgid "Settings"
msgstr "Einstellungen"
@@ -1889,6 +1994,10 @@ msgstr "Einstellungen | Integrationen"
msgid "Settings | Workspace"
msgstr "Einstellungen | Arbeitsbereich"
#: src/components/UserMenu.tsx:184
msgid "Shortcuts"
msgstr "Kürzel"
#: src/views/home/components/Header.tsx:104
msgid "Sign in"
msgstr "Anmelden"
@@ -1914,7 +2023,7 @@ msgstr "Registrierung deaktiviert"
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:434
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Registrieren mit "
@@ -1957,7 +2066,7 @@ msgstr "Erfolg"
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Optimiere deinen Workspace für nur $29/Monat. Das bekommst du dafür:"
#: src/components/UserMenu.tsx:180
#: src/components/UserMenu.tsx:195
msgid "Support"
msgstr "Support"
@@ -1966,7 +2075,7 @@ msgstr "Support"
msgid "Support the development of the project"
msgstr "Unterstütze die Entwicklung des Projekts"
#: src/components/UserMenu.tsx:139
#: src/components/UserMenu.tsx:141
msgid "System"
msgstr "System"
@@ -1981,8 +2090,8 @@ msgstr "Team-Plan"
msgid "Teams"
msgstr "Teams"
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
#: src/views/board/index.tsx:386
#: src/views/board/index.tsx:421
msgid "Template"
msgstr "Vorlage"
@@ -2002,7 +2111,7 @@ msgstr "Vorlagenname darf 100 Zeichen nicht überschreiten"
msgid "Template name is required"
msgstr "Vorlagenname ist erforderlich"
#: src/components/SideNavigation.tsx:96
#: src/components/SideNavigation.tsx:109
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Vorlagen"
@@ -2036,7 +2145,7 @@ msgstr "Die open source <0/> alternative zu Trello"
msgid "The visibility of your board has been set to {0}."
msgstr "Die Sichtbarkeit deines Boards wurde auf {0} gesetzt."
#: src/components/UserMenu.tsx:126
#: src/components/UserMenu.tsx:128
msgid "Theme"
msgstr "Design"
@@ -2056,7 +2165,7 @@ msgstr "Diese Aktion kann nicht rückgängig gemacht werden."
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:156
#: src/views/public/board/index.tsx:168
msgid "This board is private or does not exist"
msgstr "Dieses Board ist privat oder existiert nicht"
@@ -2130,7 +2239,11 @@ msgstr "Checklistenelement kann nicht hinzugefügt werden"
msgid "Unable to add comment"
msgstr "Kommentar konnte nicht hinzugefügt werden"
#: src/views/board/components/NewCardForm.tsx:174
#: src/views/card/index.tsx:199
msgid "Unable to add label"
msgstr ""
#: src/views/board/components/NewCardForm.tsx:181
msgid "Unable to create card"
msgstr "Karte konnte nicht erstellt werden"
@@ -2187,8 +2300,8 @@ msgstr "Board-URL kann nicht aktualisiert werden"
msgid "Unable to update board visibility"
msgstr "Board-Sichtbarkeit konnte nicht aktualisiert werden"
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
#: src/views/board/index.tsx:229
#: src/views/card/index.tsx:186
msgid "Unable to update card"
msgstr "Karte konnte nicht aktualisiert werden"
@@ -2204,11 +2317,15 @@ msgstr "Checklistenelement kann nicht aktualisiert werden"
msgid "Unable to update comment"
msgstr "Kommentar konnte nicht aktualisiert werden"
#: src/views/card/components/DueDateSelector.tsx:58
msgid "Unable to update due date"
msgstr "Fälligkeitsdatum konnte nicht aktualisiert werden"
#: src/views/card/components/LabelSelector.tsx:71
msgid "Unable to update labels"
msgstr "Labels konnten nicht aktualisiert werden"
#: src/views/board/index.tsx:150
#: src/views/board/index.tsx:173
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Liste konnte nicht aktualisiert werden"
@@ -2264,6 +2381,11 @@ msgstr "Unbegrenzte Mitglieder"
msgid "Unlimited workspaces"
msgstr "Unbegrenzte Workspaces"
#: src/pages/unsubscribe/index.tsx:68
#: src/pages/unsubscribe/index.tsx:93
msgid "Unsubscribe"
msgstr "Abmelden"
#: src/views/board/components/UpdateBoardSlugForm.tsx:175
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
@@ -2276,15 +2398,19 @@ msgstr "Aktualisieren"
msgid "Update label"
msgstr "Label aktualisieren"
#: src/views/card/components/ActivityList.tsx:73
#: src/views/card/components/ActivityList.tsx:70
msgid "updated a checklist item"
msgstr "hat ein Checklistenelement aktualisiert"
#: src/views/card/components/ActivityList.tsx:63
#: src/views/card/components/ActivityList.tsx:60
msgid "updated the description"
msgstr "hat die Beschreibung aktualisiert"
#: src/views/card/components/ActivityList.tsx:62
#: src/views/card/components/ActivityList.tsx:75
msgid "updated the due date"
msgstr "hat den Fälligkeitstermin aktualisiert"
#: src/views/card/components/ActivityList.tsx:59
msgid "updated the title"
msgstr "hat den Titel aktualisiert"
@@ -2293,7 +2419,7 @@ msgstr "hat den Titel aktualisiert"
msgid "updated the title to <0>{0}</0>"
msgstr "hat den Titel in <0>{0}</0> geändert"
#: src/components/SideNavigation.tsx:202
#: src/components/SideNavigation.tsx:237
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
@@ -2357,7 +2483,7 @@ msgstr "Verwalte deine Abrechnung und dein Abonnement."
msgid "View docs"
msgstr "Dokumentation ansehen"
#: src/views/public/board/index.tsx:129
#: src/views/public/board/index.tsx:141
msgid "View only"
msgstr "Nur ansehen"
@@ -2365,7 +2491,7 @@ msgstr "Nur ansehen"
msgid "View our roadmap."
msgstr "Sieh dir unsere roadmap an."
#: src/views/public/board/index.tsx:159
#: src/views/public/board/index.tsx:171
msgid "View workspace"
msgstr "Workspace anzeigen"
@@ -2377,6 +2503,10 @@ msgstr "Sichtbarkeit"
msgid "We are using the <0>AGPL-3.0 license</0>."
msgstr "Wir verwenden die <0>AGPL-3.0 lizenz</0>."
#: src/pages/unsubscribe/index.tsx:63
msgid "We couldn't update your preferences. Please try again."
msgstr "Wir konnten ihre einstellungen nicht aktualisieren. Bitte versuchen sie es erneut."
#: src/views/home/components/Features.tsx:152
msgid "We're just getting started. "
msgstr "Wir stehen erst am anfang. "
@@ -2406,10 +2536,10 @@ msgid "Why make an open source Trello?"
msgstr "Warum ein open source Trello entwickeln?"
#: src/components/SettingsLayout.tsx:39
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/board/index.tsx:386
#: src/views/boards/index.tsx:33
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:101
#: src/views/public/board/index.tsx:113
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Arbeitsbereich"
@@ -2515,6 +2645,10 @@ msgstr "Sie haben sich erfolgreich angemeldet."
msgid "You have been signed up successfully."
msgstr "Sie haben sich erfolgreich registriert."
#: src/pages/unsubscribe/index.tsx:98
msgid "You have been unsubscribed!"
msgstr "Sie wurden abgemeldet!"
#: src/views/members/components/InviteMemberForm.tsx:326
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!"
@@ -2559,6 +2693,10 @@ msgstr "Dein Trello-Konto wurde getrennt."
msgid "Your Trello account is connected."
msgstr "Dein Trello-Konto ist verbunden."
#: src/pages/unsubscribe/index.tsx:33
msgid "Your unsubscribe link is missing a token. Please open the latest email and try again."
msgstr "Ihrem abmeldelink fehlt ein token. Bitte öffnen sie die neueste e-mail und versuchen sie es erneut."
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:49
msgid "Your workspace description has been updated."
msgstr "Deine Workspace-Beschreibung wurde aktualisiert."

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@ msgid ", or see our"
msgstr ", or see our"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#: src/views/boards/index.tsx:27
#: src/views/boards/index.tsx:38
msgid "{0}"
msgstr "{0}"
@@ -30,18 +30,18 @@ msgstr "{0}"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:218
#: src/views/boards/index.tsx:33
#: src/views/card/index.tsx:272
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:443
#: src/views/board/components/NewCardForm.tsx:452
msgid "{0} labels"
msgstr "{0} labels"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:389
#: src/views/board/index.tsx:412
msgid "{0} not found"
msgstr "{0} not found"
@@ -91,8 +91,12 @@ msgstr "Account"
msgid "Account deleted"
msgstr "Account deleted"
#: src/views/card/index.tsx:308
#: src/views/public/board/CardModal.tsx:157
#: src/providers/keyboard-shortcuts.tsx:55
msgid "Actions"
msgstr "Actions"
#: src/views/card/index.tsx:386
#: src/views/public/board/CardModal.tsx:158
msgid "Activity"
msgstr "Activity"
@@ -143,19 +147,19 @@ msgstr "Add label"
msgid "Add member"
msgstr "Add member"
#: src/views/card/components/ActivityList.tsx:69
#: src/views/card/components/ActivityList.tsx:66
msgid "added a checklist"
msgstr "added a checklist"
#: src/views/card/components/ActivityList.tsx:72
#: src/views/card/components/ActivityList.tsx:69
msgid "added a checklist item"
msgstr "added a checklist item"
#: src/views/card/components/ActivityList.tsx:65
#: src/views/card/components/ActivityList.tsx:62
msgid "added a label to the card"
msgstr "added a label to the card"
#: src/views/card/components/ActivityList.tsx:67
#: src/views/card/components/ActivityList.tsx:64
msgid "added a member to the card"
msgstr "added a member to the card"
@@ -348,9 +352,9 @@ msgstr "Billing portal"
msgid "Blog Post"
msgstr "Blog Post"
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:218
#: src/views/public/board/index.tsx:101
#: src/views/board/index.tsx:386
#: src/views/card/index.tsx:272
#: src/views/public/board/index.tsx:113
msgid "Board"
msgstr "Board"
@@ -376,7 +380,7 @@ msgstr "Board name cannot exceed 100 characters"
msgid "Board name is required"
msgstr "Board name is required"
#: src/views/public/board/index.tsx:153
#: src/views/public/board/index.tsx:165
msgid "Board not found"
msgstr "Board not found"
@@ -392,7 +396,7 @@ msgstr "Board URL can only contain letters, numbers, and hyphens"
msgid "Board URL cannot exceed 60 characters"
msgstr "Board URL cannot exceed 60 characters"
#: src/views/public/board/index.tsx:68
#: src/views/public/board/index.tsx:79
msgid "Board URL copied to clipboard"
msgstr "Board URL copied to clipboard"
@@ -409,7 +413,7 @@ msgstr "Board visibility"
msgid "Board visibility updated"
msgstr "Board visibility updated"
#: src/components/SideNavigation.tsx:91
#: src/components/SideNavigation.tsx:97
msgid "Boards"
msgstr "Boards"
@@ -448,15 +452,16 @@ msgstr "Bug Report"
msgid "Cancel"
msgstr "Cancel"
#: src/views/card/index.tsx:218
#: src/views/card/index.tsx:272
msgid "Card"
msgstr "Card"
#: src/views/card/index.tsx:264
#: src/views/card/index.tsx:306
#: src/views/card/index.tsx:342
msgid "Card not found"
msgstr "Card not found"
#: src/views/board/components/NewCardForm.tsx:310
#: src/views/board/components/NewCardForm.tsx:319
msgid "Card title"
msgstr "Card title"
@@ -475,6 +480,11 @@ msgstr "Change Password"
msgid "Change your language preferences."
msgstr "Change your language preferences."
#: src/views/card/components/ActivityList.tsx:220
#: src/views/card/components/ActivityList.tsx:234
msgid "changed the due date to <0>{formattedDate}</0>"
msgstr "changed the due date to <0>{formattedDate}</0>"
#: src/views/oss-friends/index.tsx:61
msgid "Check out some of our favorite open source projects."
msgstr "Check out some of our favorite open source projects."
@@ -493,7 +503,7 @@ msgstr "Checklist name"
msgid "Checklists"
msgstr "Checklists"
#: src/views/board/components/Filters.tsx:180
#: src/views/board/components/Filters.tsx:227
msgid "Clear filters"
msgstr "Clear filters"
@@ -545,7 +555,7 @@ msgstr "Complete"
msgid "Complete control and ownership:"
msgstr "Complete control and ownership:"
#: src/views/card/components/ActivityList.tsx:74
#: src/views/card/components/ActivityList.tsx:71
msgid "completed a checklist item"
msgstr "completed a checklist item"
@@ -558,6 +568,10 @@ msgstr "completed checklist item <0>{0}</0>"
#~ msgid "Confirm URL change"
#~ msgstr "Confirm URL change"
#: src/pages/unsubscribe/index.tsx:81
msgid "Confirm your email preferences:"
msgstr "Confirm your email preferences:"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:150
msgid "Confirm your new password"
msgstr "Confirm your new password"
@@ -588,12 +602,12 @@ msgstr "Contact us"
msgid "Content Creation"
msgstr "Content Creation"
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Continue with "
msgstr "Continue with "
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
#: src/components/AuthForm.tsx:351
#: src/components/AuthForm.tsx:355
msgid "Continue with {0}"
msgstr "Continue with {0}"
@@ -611,7 +625,7 @@ msgid "Create {0}"
msgstr "Create {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewCardForm.tsx:516
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Create another"
@@ -628,7 +642,7 @@ msgstr "Create API key"
#~ msgid "Create board"
#~ msgstr "Create board"
#: src/views/board/components/NewCardForm.tsx:479
#: src/views/board/components/NewCardForm.tsx:526
msgid "Create card"
msgstr "Create card"
@@ -650,6 +664,7 @@ msgstr "Create list"
#. placeholder {0}: isTemplate ? "template" : "board"
#: src/views/boards/components/BoardsList.tsx:45
#: src/views/boards/index.tsx:26
msgid "Create new {0}"
msgstr "Create new {0}"
@@ -661,12 +676,13 @@ msgstr "Create new {0}"
msgid "Create new key"
msgstr "Create new key"
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/board/components/NewCardForm.tsx:414
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Create new label"
#: src/views/board/index.tsx:478
#: src/views/board/index.tsx:65
#: src/views/board/index.tsx:503
msgid "Create new list"
msgstr "Create new list"
@@ -679,11 +695,11 @@ msgid "Create template"
msgstr "Create template"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:149
#: src/components/WorkspaceMenu.tsx:153
msgid "Create workspace"
msgstr "Create workspace"
#: src/views/card/components/ActivityList.tsx:61
#: src/views/card/components/ActivityList.tsx:58
msgid "created the card"
msgstr "created the card"
@@ -737,7 +753,7 @@ msgstr "Custom workspace URL"
msgid "Customer Support"
msgstr "Customer Support"
#: src/components/UserMenu.tsx:153
#: src/components/UserMenu.tsx:155
msgid "Dark"
msgstr "Dark"
@@ -786,11 +802,11 @@ msgstr "Delete template"
msgid "Delete workspace"
msgstr "Delete workspace"
#: src/views/card/components/ActivityList.tsx:71
#: src/views/card/components/ActivityList.tsx:68
msgid "deleted a checklist"
msgstr "deleted a checklist"
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:73
msgid "deleted a checklist item"
msgstr "deleted a checklist item"
@@ -836,6 +852,10 @@ msgstr "Display name updated"
msgid "Do you offer a free plan?"
msgstr "Do you offer a free plan?"
#: src/pages/unsubscribe/index.tsx:78
msgid "Do you want to unsubscribe?"
msgstr "Do you want to unsubscribe?"
#: src/components/FeedbackModal.tsx:109
msgid "docs"
msgstr "docs"
@@ -844,7 +864,7 @@ msgstr "docs"
msgid "Docs"
msgstr "Docs"
#: src/components/UserMenu.tsx:191
#: src/components/UserMenu.tsx:206
#: src/views/boards/components/TemplateBoards.tsx:24
#: src/views/home/components/Footer.tsx:78
#: src/views/home/components/Header.tsx:36
@@ -866,6 +886,28 @@ msgstr "Done"
msgid "Download failed"
msgstr "Download failed"
#: src/views/board/components/Filters.tsx:171
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/card/index.tsx:138
msgid "Due date"
msgstr "Due date"
#: src/views/board/components/Filters.tsx:132
msgid "Due next month"
msgstr "Due next month"
#: src/views/board/components/Filters.tsx:127
msgid "Due next week"
msgstr "Due next week"
#: src/views/board/components/Filters.tsx:117
msgid "Due today"
msgstr "Due today"
#: src/views/board/components/Filters.tsx:122
msgid "Due tomorrow"
msgstr "Due tomorrow"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
@@ -887,7 +929,7 @@ msgstr "Edit workspace URL"
msgid "Editing"
msgstr "Editing"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "email"
@@ -907,11 +949,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:398
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Enter your email address"
#: src/components/AuthForm.tsx:384
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Enter your name"
@@ -919,7 +961,7 @@ msgstr "Enter your name"
msgid "Enter your new password"
msgstr "Enter your new password"
#: src/components/AuthForm.tsx:411
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Enter your password"
@@ -1068,7 +1110,7 @@ msgid "Features"
msgstr "Features"
#: src/components/FeedbackModal.tsx:59
#: src/components/UserMenu.tsx:199
#: src/components/UserMenu.tsx:214
msgid "Feedback"
msgstr "Feedback"
@@ -1076,7 +1118,7 @@ msgstr "Feedback"
msgid "Feedback sent"
msgstr "Feedback sent"
#: src/views/board/components/Filters.tsx:174
#: src/views/board/components/Filters.tsx:221
msgid "Filter"
msgstr "Filter"
@@ -1121,6 +1163,10 @@ msgstr "Full-time"
msgid "Fun"
msgstr "Fun"
#: src/providers/keyboard-shortcuts.tsx:53
msgid "General"
msgstr "General"
#: src/views/auth/signup/index.tsx:69
#: src/views/home/components/Cta.tsx:61
#: src/views/home/components/Header.tsx:106
@@ -1144,7 +1190,7 @@ msgstr "Get started by creating a new {0}"
#~ msgid "Get started by creating a new board"
#~ msgstr "Get started by creating a new board"
#: src/views/board/index.tsx:470
#: src/views/board/index.tsx:495
msgid "Get started by creating a new list"
msgstr "Get started by creating a new list"
@@ -1178,6 +1224,22 @@ msgstr "Go Home"
msgid "Go to app"
msgstr "Go to app"
#: src/components/SideNavigation.tsx:105
msgid "Go to boards"
msgstr "Go to boards"
#: src/components/SideNavigation.tsx:129
msgid "Go to members"
msgstr "Go to members"
#: src/components/SideNavigation.tsx:141
msgid "Go to settings"
msgstr "Go to settings"
#: src/components/SideNavigation.tsx:117
msgid "Go to templates"
msgstr "Go to templates"
#: src/views/boards/components/TemplateBoards.tsx:18
msgid "High Priority"
msgstr "High Priority"
@@ -1231,7 +1293,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:39
#: src/views/boards/index.tsx:50
msgid "Import"
msgstr "Import"
@@ -1366,9 +1428,13 @@ msgstr "Kanban reimagined"
msgid "Keep in mind that this action is irreversible."
msgstr "Keep in mind that this action is irreversible."
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
#: src/providers/keyboard-shortcuts.tsx:320
msgid "Keyboard Shortcuts"
msgstr "Keyboard Shortcuts"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/NewCardForm.tsx:418
#: src/views/card/index.tsx:120
msgid "Labels"
msgstr "Labels"
@@ -1414,15 +1480,15 @@ msgstr "Legal"
msgid "License"
msgstr "License"
#: src/components/UserMenu.tsx:167
#: src/components/UserMenu.tsx:169
msgid "Light"
msgstr "Light"
#: src/views/public/board/index.tsx:66
#: src/views/public/board/index.tsx:77
msgid "Link copied"
msgstr "Link copied"
#: src/views/card/index.tsx:111
#: src/views/card/index.tsx:112
msgid "List"
msgstr "List"
@@ -1430,7 +1496,7 @@ msgstr "List"
msgid "List name"
msgstr "List name"
#: src/views/board/components/Filters.tsx:123
#: src/views/board/components/Filters.tsx:163
msgid "Lists"
msgstr "Lists"
@@ -1438,7 +1504,7 @@ msgstr "Lists"
msgid "Login | kan.bn"
msgstr "Login | kan.bn"
#: src/components/UserMenu.tsx:209
#: src/components/UserMenu.tsx:224
msgid "Logout"
msgstr "Logout"
@@ -1454,7 +1520,7 @@ msgstr "Loved by teams worldwide"
msgid "Low Priority"
msgstr "Low Priority"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "magic link"
@@ -1462,7 +1528,7 @@ msgstr "magic link"
msgid "Make template"
msgstr "Make template"
#: src/views/card/components/ActivityList.tsx:75
#: src/views/card/components/ActivityList.tsx:72
msgid "marked a checklist item as incomplete"
msgstr "marked a checklist item as incomplete"
@@ -1475,10 +1541,10 @@ msgstr "marked checklist item <0>{0}</0> as incomplete"
msgid "Medium Priority"
msgstr "Medium Priority"
#: src/components/SideNavigation.tsx:101
#: src/views/board/components/Filters.tsx:107
#: src/views/board/components/NewCardForm.tsx:367
#: src/views/card/index.tsx:128
#: src/components/SideNavigation.tsx:121
#: src/views/board/components/Filters.tsx:147
#: src/views/board/components/NewCardForm.tsx:376
#: src/views/card/index.tsx:129
#: src/views/members/index.tsx:178
msgid "Members"
msgstr "Members"
@@ -1507,7 +1573,7 @@ msgstr "moved the card from <0>{0}</0> to<1>{1}</1>"
#~ msgid "moved the card from <0>{fromList}</0> to<1>{toList}</1>"
#~ msgstr "moved the card from <0>{fromList}</0> to<1>{toList}</1>"
#: src/views/card/components/ActivityList.tsx:64
#: src/views/card/components/ActivityList.tsx:61
msgid "moved the card to another list"
msgstr "moved the card to another list"
@@ -1521,11 +1587,15 @@ msgstr "moved the card to another list"
msgid "Name"
msgstr "Name"
#: src/providers/keyboard-shortcuts.tsx:54
msgid "Navigation"
msgstr "Navigation"
#: src/components/FeedbackModal.tsx:95
msgid "Need help?"
msgstr "Need help?"
#: src/views/boards/index.tsx:50
#: src/views/boards/index.tsx:62
#: src/views/home/components/Features.tsx:73
msgid "New"
msgstr "New"
@@ -1543,7 +1613,7 @@ msgstr "New API key"
#~ msgid "New board"
#~ msgstr "New board"
#: src/views/board/components/NewCardForm.tsx:293
#: src/views/board/components/NewCardForm.tsx:302
msgid "New card"
msgstr "New card"
@@ -1560,7 +1630,7 @@ msgid "New label"
msgstr "New label"
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
#: src/views/board/index.tsx:466
msgid "New list"
msgstr "New list"
@@ -1601,7 +1671,7 @@ msgstr "Next Steps"
msgid "No {0}"
msgstr "No {0}"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "No authentication methods are currently available"
@@ -1617,11 +1687,19 @@ msgstr "No boards found"
msgid "No credit card required"
msgstr "No credit card required"
#: src/views/board/components/Filters.tsx:137
msgid "No dates"
msgstr "No dates"
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "No download URL available for this attachment."
#: src/views/board/index.tsx:467
#: src/providers/keyboard-shortcuts.tsx:333
msgid "No keyboard shortcuts registered."
msgstr "No keyboard shortcuts registered."
#: src/views/board/index.tsx:492
msgid "No lists"
msgstr "No lists"
@@ -1645,6 +1723,14 @@ msgstr "Once you delete your account, there is no going back. This action cannot
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/WorkspaceMenu.tsx:31
msgid "Open command menu"
msgstr "Open command menu"
#: src/providers/keyboard-shortcuts.tsx:171
msgid "Open keyboard shortcuts"
msgstr "Open keyboard shortcuts"
#: src/views/pricing/components/FeatureComparisonTable.tsx:150
msgid "Open source"
msgstr "Open source"
@@ -1653,7 +1739,7 @@ msgstr "Open source"
msgid "Open Source Friends"
msgstr "Open Source Friends"
#: src/components/AuthForm.tsx:374
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "or"
@@ -1666,6 +1752,10 @@ msgstr "Organize and find cards quickly with powerful filtering tools."
msgid "OSS Friends"
msgstr "OSS Friends"
#: src/views/board/components/Filters.tsx:112
msgid "Overdue"
msgstr "Overdue"
#: src/views/pricing/components/Pricing.tsx:73
#: src/views/pricing/components/PricingTiers.tsx:70
msgid "Own your data"
@@ -1735,15 +1825,15 @@ msgstr "Platform"
msgid "Please confirm your new password"
msgstr "Please confirm your new password"
#: src/components/AuthForm.tsx:402
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Please enter a valid email address"
#: src/components/AuthForm.tsx:388
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Please enter a valid name"
#: src/components/AuthForm.tsx:416
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Please enter a valid password"
@@ -1754,14 +1844,14 @@ msgstr "Please select a file to upload."
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:177
#: src/views/board/components/NewCardForm.tsx:184
#: src/views/board/components/NewListForm.tsx:79
#: src/views/board/components/NewTemplateForm.tsx:61
#: src/views/board/components/NewTemplateForm.tsx:77
#: src/views/board/components/UpdateBoardSlugForm.tsx:73
#: src/views/board/components/VisibilityButton.tsx:54
#: src/views/board/index.tsx:151
#: src/views/board/index.tsx:207
#: src/views/board/index.tsx:174
#: src/views/board/index.tsx:230
#: src/views/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
@@ -1771,13 +1861,15 @@ 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/DueDateSelector.tsx:59
#: 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/NewChecklistForm.tsx:70
#: src/views/card/components/NewChecklistItemForm.tsx:89
#: src/views/card/components/NewCommentForm.tsx:31
#: src/views/card/index.tsx:177
#: src/views/card/index.tsx:187
#: src/views/card/index.tsx:200
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:241
@@ -1877,11 +1969,11 @@ msgstr "Remove"
msgid "Remove member"
msgstr "Remove member"
#: src/views/card/components/ActivityList.tsx:66
#: src/views/card/components/ActivityList.tsx:63
msgid "removed a label from the card"
msgstr "removed a label from the card"
#: src/views/card/components/ActivityList.tsx:68
#: src/views/card/components/ActivityList.tsx:65
msgid "removed a member from the card"
msgstr "removed a member from the card"
@@ -1894,7 +1986,12 @@ msgstr "removed label <0>{0}</0>"
#~ msgid "removed label <0>{label}</0>"
#~ msgstr "removed label <0>{label}</0>"
#: src/views/card/components/ActivityList.tsx:70
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:241
msgid "removed the due date"
msgstr "removed the due date"
#: src/views/card/components/ActivityList.tsx:67
msgid "renamed a checklist"
msgstr "renamed a checklist"
@@ -2005,8 +2102,16 @@ msgstr "Send feedback"
msgid "Senior"
msgstr "Senior"
#: src/views/card/components/DueDateSelector.tsx:116
msgid "Set due date"
msgstr "Set due date"
#: src/views/card/components/ActivityList.tsx:74
msgid "set the due date"
msgstr "set the due date"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:106
#: src/components/SideNavigation.tsx:133
msgid "Settings"
msgstr "Settings"
@@ -2038,6 +2143,10 @@ msgstr "Settings | Workspace"
#~ msgid "Share invite link"
#~ msgstr "Share invite link"
#: src/components/UserMenu.tsx:184
msgid "Shortcuts"
msgstr "Shortcuts"
#: src/views/home/components/Header.tsx:104
msgid "Sign in"
msgstr "Sign in"
@@ -2063,7 +2172,7 @@ msgstr "Sign up disabled"
msgid "Sign up is currently disabled. Please try again later."
msgstr "Sign up is currently disabled. Please try again later."
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Sign up with "
@@ -2106,7 +2215,7 @@ msgstr "Success"
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Supercharge your workspace for just $29/month. Here's what you'll get:"
#: src/components/UserMenu.tsx:180
#: src/components/UserMenu.tsx:195
msgid "Support"
msgstr "Support"
@@ -2115,7 +2224,7 @@ msgstr "Support"
msgid "Support the development of the project"
msgstr "Support the development of the project"
#: src/components/UserMenu.tsx:139
#: src/components/UserMenu.tsx:141
msgid "System"
msgstr "System"
@@ -2130,8 +2239,8 @@ msgstr "Team Plan"
msgid "Teams"
msgstr "Teams"
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
#: src/views/board/index.tsx:386
#: src/views/board/index.tsx:421
msgid "Template"
msgstr "Template"
@@ -2151,7 +2260,7 @@ msgstr "Template name cannot exceed 100 characters"
msgid "Template name is required"
msgstr "Template name is required"
#: src/components/SideNavigation.tsx:96
#: src/components/SideNavigation.tsx:109
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Templates"
@@ -2185,7 +2294,7 @@ msgstr "The open source <0/> alternative to Trello"
msgid "The visibility of your board has been set to {0}."
msgstr "The visibility of your board has been set to {0}."
#: src/components/UserMenu.tsx:126
#: src/components/UserMenu.tsx:128
msgid "Theme"
msgstr "Theme"
@@ -2205,7 +2314,7 @@ msgstr "This action can't be undone."
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:156
#: src/views/public/board/index.tsx:168
msgid "This board is private or does not exist"
msgstr "This board is private or does not exist"
@@ -2287,7 +2396,11 @@ msgstr "Unable to add checklist item"
msgid "Unable to add comment"
msgstr "Unable to add comment"
#: src/views/board/components/NewCardForm.tsx:174
#: src/views/card/index.tsx:199
msgid "Unable to add label"
msgstr "Unable to add label"
#: src/views/board/components/NewCardForm.tsx:181
msgid "Unable to create card"
msgstr "Unable to create card"
@@ -2344,8 +2457,8 @@ msgstr "Unable to update board URL"
msgid "Unable to update board visibility"
msgstr "Unable to update board visibility"
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
#: src/views/board/index.tsx:229
#: src/views/card/index.tsx:186
msgid "Unable to update card"
msgstr "Unable to update card"
@@ -2361,11 +2474,15 @@ msgstr "Unable to update checklist item"
msgid "Unable to update comment"
msgstr "Unable to update comment"
#: src/views/card/components/DueDateSelector.tsx:58
msgid "Unable to update due date"
msgstr "Unable to update due date"
#: src/views/card/components/LabelSelector.tsx:71
msgid "Unable to update labels"
msgstr "Unable to update labels"
#: src/views/board/index.tsx:150
#: src/views/board/index.tsx:173
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Unable to update list"
@@ -2425,6 +2542,11 @@ msgstr "Unlimited members"
msgid "Unlimited workspaces"
msgstr "Unlimited workspaces"
#: src/pages/unsubscribe/index.tsx:68
#: src/pages/unsubscribe/index.tsx:93
msgid "Unsubscribe"
msgstr "Unsubscribe"
#: src/views/board/components/UpdateBoardSlugForm.tsx:175
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
@@ -2437,15 +2559,19 @@ msgstr "Update"
msgid "Update label"
msgstr "Update label"
#: src/views/card/components/ActivityList.tsx:73
#: src/views/card/components/ActivityList.tsx:70
msgid "updated a checklist item"
msgstr "updated a checklist item"
#: src/views/card/components/ActivityList.tsx:63
#: src/views/card/components/ActivityList.tsx:60
msgid "updated the description"
msgstr "updated the description"
#: src/views/card/components/ActivityList.tsx:62
#: src/views/card/components/ActivityList.tsx:75
msgid "updated the due date"
msgstr "updated the due date"
#: src/views/card/components/ActivityList.tsx:59
msgid "updated the title"
msgstr "updated the title"
@@ -2462,7 +2588,7 @@ msgstr "updated the title to <0>{0}</0>"
#~ msgid "Upgrade"
#~ msgstr "Upgrade"
#: src/components/SideNavigation.tsx:202
#: src/components/SideNavigation.tsx:237
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
@@ -2530,7 +2656,7 @@ msgstr "View and manage your billing and subscription."
msgid "View docs"
msgstr "View docs"
#: src/views/public/board/index.tsx:129
#: src/views/public/board/index.tsx:141
msgid "View only"
msgstr "View only"
@@ -2538,7 +2664,7 @@ msgstr "View only"
msgid "View our roadmap."
msgstr "View our roadmap."
#: src/views/public/board/index.tsx:159
#: src/views/public/board/index.tsx:171
msgid "View workspace"
msgstr "View workspace"
@@ -2550,6 +2676,10 @@ msgstr "Visibility"
msgid "We are using the <0>AGPL-3.0 license</0>."
msgstr "We are using the <0>AGPL-3.0 license</0>."
#: src/pages/unsubscribe/index.tsx:63
msgid "We couldn't update your preferences. Please try again."
msgstr "We couldn't update your preferences. Please try again."
#: src/views/home/components/Features.tsx:152
msgid "We're just getting started. "
msgstr "We're just getting started. "
@@ -2587,10 +2717,10 @@ msgid "Why make an open source Trello?"
msgstr "Why make an open source Trello?"
#: src/components/SettingsLayout.tsx:39
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/board/index.tsx:386
#: src/views/boards/index.tsx:33
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:101
#: src/views/public/board/index.tsx:113
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Workspace"
@@ -2700,6 +2830,10 @@ msgstr "You have been logged in successfully."
msgid "You have been signed up successfully."
msgstr "You have been signed up successfully."
#: src/pages/unsubscribe/index.tsx:98
msgid "You have been unsubscribed!"
msgstr "You have been unsubscribed!"
#: src/views/members/components/InviteMemberForm.tsx:326
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!"
@@ -2744,6 +2878,10 @@ msgstr "Your Trello account has been disconnected."
msgid "Your Trello account is connected."
msgstr "Your Trello account is connected."
#: src/pages/unsubscribe/index.tsx:33
msgid "Your unsubscribe link is missing a token. Please open the latest email and try again."
msgstr "Your unsubscribe link is missing a token. Please open the latest email and try again."
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:49
msgid "Your workspace description has been updated."
msgstr "Your workspace description has been updated."

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@ msgid ", or see our"
msgstr ", o consulta nuestra"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#: src/views/boards/index.tsx:27
#: src/views/boards/index.tsx:38
msgid "{0}"
msgstr "{0}"
@@ -30,18 +30,18 @@ msgstr "{0}"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:218
#: src/views/boards/index.tsx:33
#: src/views/card/index.tsx:272
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:443
#: src/views/board/components/NewCardForm.tsx:452
msgid "{0} labels"
msgstr "{0} etiquetas"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:389
#: src/views/board/index.tsx:412
msgid "{0} not found"
msgstr "{0} no encontrado"
@@ -79,8 +79,12 @@ msgstr "Cuenta"
msgid "Account deleted"
msgstr "Cuenta eliminada"
#: src/views/card/index.tsx:308
#: src/views/public/board/CardModal.tsx:157
#: src/providers/keyboard-shortcuts.tsx:55
msgid "Actions"
msgstr "Acciones"
#: src/views/card/index.tsx:386
#: src/views/public/board/CardModal.tsx:158
msgid "Activity"
msgstr "Actividad"
@@ -127,19 +131,19 @@ msgstr "Añadir etiqueta"
msgid "Add member"
msgstr "Añadir miembro"
#: src/views/card/components/ActivityList.tsx:69
#: src/views/card/components/ActivityList.tsx:66
msgid "added a checklist"
msgstr "añadió una lista de verificación"
#: src/views/card/components/ActivityList.tsx:72
#: src/views/card/components/ActivityList.tsx:69
msgid "added a checklist item"
msgstr "añadió un elemento a la lista de verificación"
#: src/views/card/components/ActivityList.tsx:65
#: src/views/card/components/ActivityList.tsx:62
msgid "added a label to the card"
msgstr "añadió una etiqueta a la tarjeta"
#: src/views/card/components/ActivityList.tsx:67
#: src/views/card/components/ActivityList.tsx:64
msgid "added a member to the card"
msgstr "añadió un miembro a la tarjeta"
@@ -308,9 +312,9 @@ msgstr "Portal de facturación"
msgid "Blog Post"
msgstr "Entrada de blog"
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:218
#: src/views/public/board/index.tsx:101
#: src/views/board/index.tsx:386
#: src/views/card/index.tsx:272
#: src/views/public/board/index.tsx:113
msgid "Board"
msgstr "Tablero"
@@ -327,7 +331,7 @@ msgstr "El nombre del tablero no puede exceder los 100 caracteres"
msgid "Board name is required"
msgstr "El nombre del tablero es obligatorio"
#: src/views/public/board/index.tsx:153
#: src/views/public/board/index.tsx:165
msgid "Board not found"
msgstr "Tablero no encontrado"
@@ -343,7 +347,7 @@ msgstr "La URL del tablero solo puede contener letras, números y guiones"
msgid "Board URL cannot exceed 60 characters"
msgstr "La URL del tablero no puede exceder los 60 caracteres"
#: src/views/public/board/index.tsx:68
#: src/views/public/board/index.tsx:79
msgid "Board URL copied to clipboard"
msgstr "URL del tablero copiada al portapapeles"
@@ -360,7 +364,7 @@ msgstr "Visibilidad del tablero"
msgid "Board visibility updated"
msgstr "Visibilidad del tablero actualizada"
#: src/components/SideNavigation.tsx:91
#: src/components/SideNavigation.tsx:97
msgid "Boards"
msgstr "Tableros"
@@ -391,15 +395,16 @@ msgstr "Informe de error"
msgid "Cancel"
msgstr "Cancelar"
#: src/views/card/index.tsx:218
#: src/views/card/index.tsx:272
msgid "Card"
msgstr "Tarjeta"
#: src/views/card/index.tsx:264
#: src/views/card/index.tsx:306
#: src/views/card/index.tsx:342
msgid "Card not found"
msgstr "Tarjeta no encontrada"
#: src/views/board/components/NewCardForm.tsx:310
#: src/views/board/components/NewCardForm.tsx:319
msgid "Card title"
msgstr "Título de la tarjeta"
@@ -414,6 +419,11 @@ msgstr "Cambiar contraseña"
msgid "Change your language preferences."
msgstr "Cambia tus preferencias de idioma."
#: src/views/card/components/ActivityList.tsx:220
#: src/views/card/components/ActivityList.tsx:234
msgid "changed the due date to <0>{formattedDate}</0>"
msgstr "cambió la fecha de vencimiento a <0>{formattedDate}</0>"
#: src/views/oss-friends/index.tsx:61
msgid "Check out some of our favorite open source projects."
msgstr "Descubre algunos de nuestros proyectos de código abierto favoritos."
@@ -432,7 +442,7 @@ msgstr "Nombre de la lista de verificación"
msgid "Checklists"
msgstr "Listas de verificación"
#: src/views/board/components/Filters.tsx:180
#: src/views/board/components/Filters.tsx:227
msgid "Clear filters"
msgstr "Borrar filtros"
@@ -484,7 +494,7 @@ msgstr "Completado"
msgid "Complete control and ownership:"
msgstr "Control y propiedad completos:"
#: src/views/card/components/ActivityList.tsx:74
#: src/views/card/components/ActivityList.tsx:71
msgid "completed a checklist item"
msgstr "completó un elemento de la lista de verificación"
@@ -493,6 +503,10 @@ msgstr "completó un elemento de la lista de verificación"
msgid "completed checklist item <0>{0}</0>"
msgstr "completó el elemento <0>{0}</0> de la lista de verificación"
#: src/pages/unsubscribe/index.tsx:81
msgid "Confirm your email preferences:"
msgstr "Confirma tus preferencias de correo electrónico:"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:150
msgid "Confirm your new password"
msgstr "Confirma tu nueva contraseña"
@@ -523,12 +537,12 @@ msgstr "Contáctanos"
msgid "Content Creation"
msgstr "Creación de contenido"
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Continue with "
msgstr "Continuar con "
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
#: src/components/AuthForm.tsx:351
#: src/components/AuthForm.tsx:355
msgid "Continue with {0}"
msgstr "Continuar con {0}"
@@ -546,7 +560,7 @@ msgid "Create {0}"
msgstr "Crear {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewCardForm.tsx:516
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Crear otro"
@@ -555,7 +569,7 @@ msgstr "Crear otro"
msgid "Create API key"
msgstr "Crear clave API"
#: src/views/board/components/NewCardForm.tsx:479
#: src/views/board/components/NewCardForm.tsx:526
msgid "Create card"
msgstr "Crear tarjeta"
@@ -577,6 +591,7 @@ msgstr "Crear lista"
#. placeholder {0}: isTemplate ? "template" : "board"
#: src/views/boards/components/BoardsList.tsx:45
#: src/views/boards/index.tsx:26
msgid "Create new {0}"
msgstr "Crear nuevo {0}"
@@ -584,12 +599,13 @@ msgstr "Crear nuevo {0}"
msgid "Create new key"
msgstr "Crear nueva clave"
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/board/components/NewCardForm.tsx:414
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Crear nueva etiqueta"
#: src/views/board/index.tsx:478
#: src/views/board/index.tsx:65
#: src/views/board/index.tsx:503
msgid "Create new list"
msgstr "Crear nueva lista"
@@ -598,11 +614,11 @@ msgid "Create template"
msgstr "Crear plantilla"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:149
#: src/components/WorkspaceMenu.tsx:153
msgid "Create workspace"
msgstr "Crear espacio de trabajo"
#: src/views/card/components/ActivityList.tsx:61
#: src/views/card/components/ActivityList.tsx:58
msgid "created the card"
msgstr "creó la tarjeta"
@@ -648,7 +664,7 @@ msgstr "URL personalizada del espacio de trabajo"
msgid "Customer Support"
msgstr "Atención al cliente"
#: src/components/UserMenu.tsx:153
#: src/components/UserMenu.tsx:155
msgid "Dark"
msgstr "Oscuro"
@@ -697,11 +713,11 @@ msgstr "Eliminar plantilla"
msgid "Delete workspace"
msgstr "Eliminar espacio de trabajo"
#: src/views/card/components/ActivityList.tsx:71
#: src/views/card/components/ActivityList.tsx:68
msgid "deleted a checklist"
msgstr "eliminó una lista de verificación"
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:73
msgid "deleted a checklist item"
msgstr "eliminó un elemento de la lista de verificación"
@@ -747,6 +763,10 @@ msgstr "Nombre visible actualizado"
msgid "Do you offer a free plan?"
msgstr "¿Ofrecen un plan gratuito?"
#: src/pages/unsubscribe/index.tsx:78
msgid "Do you want to unsubscribe?"
msgstr "¿Quieres darte de baja?"
#: src/components/FeedbackModal.tsx:109
msgid "docs"
msgstr "documentos"
@@ -755,7 +775,7 @@ msgstr "documentos"
msgid "Docs"
msgstr "Documentos"
#: src/components/UserMenu.tsx:191
#: src/components/UserMenu.tsx:206
#: src/views/boards/components/TemplateBoards.tsx:24
#: src/views/home/components/Footer.tsx:78
#: src/views/home/components/Header.tsx:36
@@ -777,6 +797,28 @@ msgstr "Terminado"
msgid "Download failed"
msgstr "Error en la descarga"
#: src/views/board/components/Filters.tsx:171
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/card/index.tsx:138
msgid "Due date"
msgstr "Fecha de vencimiento"
#: src/views/board/components/Filters.tsx:132
msgid "Due next month"
msgstr "Vence el próximo mes"
#: src/views/board/components/Filters.tsx:127
msgid "Due next week"
msgstr "Vence la próxima semana"
#: src/views/board/components/Filters.tsx:117
msgid "Due today"
msgstr "Vence hoy"
#: src/views/board/components/Filters.tsx:122
msgid "Due tomorrow"
msgstr "Vence mañana"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
@@ -798,7 +840,7 @@ msgstr "Editar URL del espacio de trabajo"
msgid "Editing"
msgstr "Editando"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "correo electrónico"
@@ -818,11 +860,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:398
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Introduce tu dirección de correo electrónico"
#: src/components/AuthForm.tsx:384
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Introduce tu nombre"
@@ -830,7 +872,7 @@ msgstr "Introduce tu nombre"
msgid "Enter your new password"
msgstr "Introduce tu nueva contraseña"
#: src/components/AuthForm.tsx:411
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Introduce tu contraseña"
@@ -979,7 +1021,7 @@ msgid "Features"
msgstr "Características"
#: src/components/FeedbackModal.tsx:59
#: src/components/UserMenu.tsx:199
#: src/components/UserMenu.tsx:214
msgid "Feedback"
msgstr "Comentarios"
@@ -987,7 +1029,7 @@ msgstr "Comentarios"
msgid "Feedback sent"
msgstr "Comentarios enviados"
#: src/views/board/components/Filters.tsx:174
#: src/views/board/components/Filters.tsx:221
msgid "Filter"
msgstr "Filtrar"
@@ -1024,6 +1066,10 @@ msgstr "Tiempo completo"
msgid "Fun"
msgstr "Diversión"
#: src/providers/keyboard-shortcuts.tsx:53
msgid "General"
msgstr "General"
#: src/views/auth/signup/index.tsx:69
#: src/views/home/components/Cta.tsx:61
#: src/views/home/components/Header.tsx:106
@@ -1043,7 +1089,7 @@ msgstr "Comenzar"
msgid "Get started by creating a new {0}"
msgstr "Comienza creando un nuevo {0}"
#: src/views/board/index.tsx:470
#: src/views/board/index.tsx:495
msgid "Get started by creating a new list"
msgstr "Comienza creando una nueva lista"
@@ -1077,6 +1123,22 @@ msgstr "Ir a inicio"
msgid "Go to app"
msgstr "Ir a la aplicación"
#: src/components/SideNavigation.tsx:105
msgid "Go to boards"
msgstr "Ir a tableros"
#: src/components/SideNavigation.tsx:129
msgid "Go to members"
msgstr "Ir a miembros"
#: src/components/SideNavigation.tsx:141
msgid "Go to settings"
msgstr "Ir a configuración"
#: src/components/SideNavigation.tsx:117
msgid "Go to templates"
msgstr "Ir a plantillas"
#: src/views/boards/components/TemplateBoards.tsx:18
msgid "High Priority"
msgstr "Alta prioridad"
@@ -1126,7 +1188,7 @@ msgstr "Ideas"
msgid "Ideas to improve this page..."
msgstr "Ideas para mejorar esta página..."
#: src/views/boards/index.tsx:39
#: src/views/boards/index.tsx:50
msgid "Import"
msgstr "Importar"
@@ -1257,9 +1319,13 @@ msgstr "Kanban reinventado"
msgid "Keep in mind that this action is irreversible."
msgstr "Ten en cuenta que esta acción es irreversible."
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
#: src/providers/keyboard-shortcuts.tsx:320
msgid "Keyboard Shortcuts"
msgstr "Atajos de teclado"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/NewCardForm.tsx:418
#: src/views/card/index.tsx:120
msgid "Labels"
msgstr "Etiquetas"
@@ -1305,15 +1371,15 @@ msgstr "Legal"
msgid "License"
msgstr "Licencia"
#: src/components/UserMenu.tsx:167
#: src/components/UserMenu.tsx:169
msgid "Light"
msgstr "Claro"
#: src/views/public/board/index.tsx:66
#: src/views/public/board/index.tsx:77
msgid "Link copied"
msgstr "Enlace copiado"
#: src/views/card/index.tsx:111
#: src/views/card/index.tsx:112
msgid "List"
msgstr "Lista"
@@ -1321,7 +1387,7 @@ msgstr "Lista"
msgid "List name"
msgstr "Nombre de la lista"
#: src/views/board/components/Filters.tsx:123
#: src/views/board/components/Filters.tsx:163
msgid "Lists"
msgstr "Listas"
@@ -1329,7 +1395,7 @@ msgstr "Listas"
msgid "Login | kan.bn"
msgstr "Iniciar sesión | kan.bn"
#: src/components/UserMenu.tsx:209
#: src/components/UserMenu.tsx:224
msgid "Logout"
msgstr "Cerrar sesión"
@@ -1345,7 +1411,7 @@ msgstr "Amado por equipos en todo el mundo"
msgid "Low Priority"
msgstr "Prioridad baja"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "enlace mágico"
@@ -1353,7 +1419,7 @@ msgstr "enlace mágico"
msgid "Make template"
msgstr "Crear plantilla"
#: src/views/card/components/ActivityList.tsx:75
#: src/views/card/components/ActivityList.tsx:72
msgid "marked a checklist item as incomplete"
msgstr "marcó un elemento de la lista de verificación como incompleto"
@@ -1366,10 +1432,10 @@ msgstr "marcó el elemento <0>{0}</0> de la lista de verificación como incomple
msgid "Medium Priority"
msgstr "Prioridad media"
#: src/components/SideNavigation.tsx:101
#: src/views/board/components/Filters.tsx:107
#: src/views/board/components/NewCardForm.tsx:367
#: src/views/card/index.tsx:128
#: src/components/SideNavigation.tsx:121
#: src/views/board/components/Filters.tsx:147
#: src/views/board/components/NewCardForm.tsx:376
#: src/views/card/index.tsx:129
#: src/views/members/index.tsx:178
msgid "Members"
msgstr "Miembros"
@@ -1394,7 +1460,7 @@ msgstr "facturación mensual"
msgid "moved the card from <0>{0}</0> to<1>{1}</1>"
msgstr "movió la tarjeta de <0>{0}</0> a<1>{1}</1>"
#: src/views/card/components/ActivityList.tsx:64
#: src/views/card/components/ActivityList.tsx:61
msgid "moved the card to another list"
msgstr "movió la tarjeta a otra lista"
@@ -1404,11 +1470,15 @@ msgstr "movió la tarjeta a otra lista"
msgid "Name"
msgstr "Nombre"
#: src/providers/keyboard-shortcuts.tsx:54
msgid "Navigation"
msgstr "Navegación"
#: src/components/FeedbackModal.tsx:95
msgid "Need help?"
msgstr "¿Necesitas ayuda?"
#: src/views/boards/index.tsx:50
#: src/views/boards/index.tsx:62
#: src/views/home/components/Features.tsx:73
msgid "New"
msgstr "Nuevo"
@@ -1422,7 +1492,7 @@ msgstr "Nuevo {0}"
msgid "New API key"
msgstr "Nueva clave API"
#: src/views/board/components/NewCardForm.tsx:293
#: src/views/board/components/NewCardForm.tsx:302
msgid "New card"
msgstr "Nueva tarjeta"
@@ -1439,7 +1509,7 @@ msgid "New label"
msgstr "Nueva etiqueta"
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
#: src/views/board/index.tsx:466
msgid "New list"
msgstr "Nueva lista"
@@ -1476,7 +1546,7 @@ msgstr "Próximos pasos"
msgid "No {0}"
msgstr "Sin {0}"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "No hay métodos de autenticación disponibles actualmente"
@@ -1488,11 +1558,19 @@ msgstr "No se encontraron tableros"
msgid "No credit card required"
msgstr "No se requiere tarjeta de crédito"
#: src/views/board/components/Filters.tsx:137
msgid "No dates"
msgstr "Sin fechas"
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "No hay URL de descarga disponible para este archivo adjunto."
#: src/views/board/index.tsx:467
#: src/providers/keyboard-shortcuts.tsx:333
msgid "No keyboard shortcuts registered."
msgstr "No hay atajos de teclado registrados."
#: src/views/board/index.tsx:492
msgid "No lists"
msgstr "Sin listas"
@@ -1516,6 +1594,14 @@ msgstr "Una vez que elimines tu cuenta, no hay vuelta atrás. Esta acción no se
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/WorkspaceMenu.tsx:31
msgid "Open command menu"
msgstr "Abrir menú de comandos"
#: src/providers/keyboard-shortcuts.tsx:171
msgid "Open keyboard shortcuts"
msgstr "Abrir atajos de teclado"
#: src/views/pricing/components/FeatureComparisonTable.tsx:150
msgid "Open source"
msgstr "Código abierto"
@@ -1524,7 +1610,7 @@ msgstr "Código abierto"
msgid "Open Source Friends"
msgstr "Amigos de código abierto"
#: src/components/AuthForm.tsx:374
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "o"
@@ -1537,6 +1623,10 @@ msgstr "Organiza y encuentra tarjetas rápidamente con potentes herramientas de
msgid "OSS Friends"
msgstr "Amigos OSS"
#: src/views/board/components/Filters.tsx:112
msgid "Overdue"
msgstr "Vencido"
#: src/views/pricing/components/Pricing.tsx:73
#: src/views/pricing/components/PricingTiers.tsx:70
msgid "Own your data"
@@ -1606,15 +1696,15 @@ msgstr "Plataforma"
msgid "Please confirm your new password"
msgstr "Por favor, confirma tu nueva contraseña"
#: src/components/AuthForm.tsx:402
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Por favor, introduce una dirección de correo electrónico válida"
#: src/components/AuthForm.tsx:388
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Por favor, introduce un nombre válido"
#: src/components/AuthForm.tsx:416
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Por favor, introduce una contraseña válida"
@@ -1625,14 +1715,14 @@ msgstr "Por favor selecciona un archivo para subir."
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:177
#: src/views/board/components/NewCardForm.tsx:184
#: src/views/board/components/NewListForm.tsx:79
#: src/views/board/components/NewTemplateForm.tsx:61
#: src/views/board/components/NewTemplateForm.tsx:77
#: src/views/board/components/UpdateBoardSlugForm.tsx:73
#: src/views/board/components/VisibilityButton.tsx:54
#: src/views/board/index.tsx:151
#: src/views/board/index.tsx:207
#: src/views/board/index.tsx:174
#: src/views/board/index.tsx:230
#: src/views/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
@@ -1642,13 +1732,15 @@ 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/DueDateSelector.tsx:59
#: 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/NewChecklistForm.tsx:70
#: src/views/card/components/NewChecklistItemForm.tsx:89
#: src/views/card/components/NewCommentForm.tsx:31
#: src/views/card/index.tsx:177
#: src/views/card/index.tsx:187
#: src/views/card/index.tsx:200
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:241
@@ -1748,11 +1840,11 @@ msgstr "Eliminar"
msgid "Remove member"
msgstr "Eliminar miembro"
#: src/views/card/components/ActivityList.tsx:66
#: src/views/card/components/ActivityList.tsx:63
msgid "removed a label from the card"
msgstr "eliminó una etiqueta de la tarjeta"
#: src/views/card/components/ActivityList.tsx:68
#: src/views/card/components/ActivityList.tsx:65
msgid "removed a member from the card"
msgstr "eliminó un miembro de la tarjeta"
@@ -1761,7 +1853,12 @@ msgstr "eliminó un miembro de la tarjeta"
msgid "removed label <0>{0}</0>"
msgstr "eliminó la etiqueta <0>{0}</0>"
#: src/views/card/components/ActivityList.tsx:70
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:241
msgid "removed the due date"
msgstr "eliminó la fecha de vencimiento"
#: src/views/card/components/ActivityList.tsx:67
msgid "renamed a checklist"
msgstr "renombró una lista de verificación"
@@ -1864,8 +1961,16 @@ msgstr "Enviar comentarios"
msgid "Senior"
msgstr "Senior"
#: src/views/card/components/DueDateSelector.tsx:116
msgid "Set due date"
msgstr "Establecer fecha de vencimiento"
#: src/views/card/components/ActivityList.tsx:74
msgid "set the due date"
msgstr "estableció la fecha de vencimiento"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:106
#: src/components/SideNavigation.tsx:133
msgid "Settings"
msgstr "Configuración"
@@ -1889,6 +1994,10 @@ msgstr "Configuración | Integraciones"
msgid "Settings | Workspace"
msgstr "Configuración | Espacio de trabajo"
#: src/components/UserMenu.tsx:184
msgid "Shortcuts"
msgstr "Atajos"
#: src/views/home/components/Header.tsx:104
msgid "Sign in"
msgstr "Iniciar sesión"
@@ -1914,7 +2023,7 @@ msgstr "Registro deshabilitado"
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:434
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Registrarse con "
@@ -1957,7 +2066,7 @@ msgstr "Éxito"
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Potencia tu espacio de trabajo por solo $29/mes. Esto es lo que obtendrás:"
#: src/components/UserMenu.tsx:180
#: src/components/UserMenu.tsx:195
msgid "Support"
msgstr "Soporte"
@@ -1966,7 +2075,7 @@ msgstr "Soporte"
msgid "Support the development of the project"
msgstr "Apoya el desarrollo del proyecto"
#: src/components/UserMenu.tsx:139
#: src/components/UserMenu.tsx:141
msgid "System"
msgstr "Sistema"
@@ -1981,8 +2090,8 @@ msgstr "Plan de Equipo"
msgid "Teams"
msgstr "Equipos"
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
#: src/views/board/index.tsx:386
#: src/views/board/index.tsx:421
msgid "Template"
msgstr "Plantilla"
@@ -2002,7 +2111,7 @@ msgstr "El nombre de la plantilla no puede exceder los 100 caracteres"
msgid "Template name is required"
msgstr "El nombre de la plantilla es obligatorio"
#: src/components/SideNavigation.tsx:96
#: src/components/SideNavigation.tsx:109
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Plantillas"
@@ -2036,7 +2145,7 @@ msgstr "La alternativa de código abierto <0/> a Trello"
msgid "The visibility of your board has been set to {0}."
msgstr "La visibilidad de tu tablero ha sido establecida a {0}."
#: src/components/UserMenu.tsx:126
#: src/components/UserMenu.tsx:128
msgid "Theme"
msgstr "Tema"
@@ -2056,7 +2165,7 @@ msgstr "Esta acción no se puede deshacer."
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:156
#: src/views/public/board/index.tsx:168
msgid "This board is private or does not exist"
msgstr "Este tablero es privado o no existe"
@@ -2130,7 +2239,11 @@ msgstr "No se puede añadir el elemento a la lista de verificación"
msgid "Unable to add comment"
msgstr "No se puede añadir el comentario"
#: src/views/board/components/NewCardForm.tsx:174
#: src/views/card/index.tsx:199
msgid "Unable to add label"
msgstr ""
#: src/views/board/components/NewCardForm.tsx:181
msgid "Unable to create card"
msgstr "No se puede crear la tarjeta"
@@ -2187,8 +2300,8 @@ msgstr "No se puede actualizar la URL del tablero"
msgid "Unable to update board visibility"
msgstr "No se puede actualizar la visibilidad del tablero"
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
#: src/views/board/index.tsx:229
#: src/views/card/index.tsx:186
msgid "Unable to update card"
msgstr "No se puede actualizar la tarjeta"
@@ -2204,11 +2317,15 @@ 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/DueDateSelector.tsx:58
msgid "Unable to update due date"
msgstr "No se puede actualizar la fecha de vencimiento"
#: src/views/card/components/LabelSelector.tsx:71
msgid "Unable to update labels"
msgstr "No se pueden actualizar las etiquetas"
#: src/views/board/index.tsx:150
#: src/views/board/index.tsx:173
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "No se puede actualizar la lista"
@@ -2264,6 +2381,11 @@ msgstr "Miembros ilimitados"
msgid "Unlimited workspaces"
msgstr "Espacios de trabajo ilimitados"
#: src/pages/unsubscribe/index.tsx:68
#: src/pages/unsubscribe/index.tsx:93
msgid "Unsubscribe"
msgstr "Darse de baja"
#: src/views/board/components/UpdateBoardSlugForm.tsx:175
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
@@ -2276,15 +2398,19 @@ msgstr "Actualizar"
msgid "Update label"
msgstr "Actualizar etiqueta"
#: src/views/card/components/ActivityList.tsx:73
#: src/views/card/components/ActivityList.tsx:70
msgid "updated a checklist item"
msgstr "actualizó un elemento de la lista de verificación"
#: src/views/card/components/ActivityList.tsx:63
#: src/views/card/components/ActivityList.tsx:60
msgid "updated the description"
msgstr "actualizó la descripción"
#: src/views/card/components/ActivityList.tsx:62
#: src/views/card/components/ActivityList.tsx:75
msgid "updated the due date"
msgstr "actualizó la fecha de vencimiento"
#: src/views/card/components/ActivityList.tsx:59
msgid "updated the title"
msgstr "actualizó el título"
@@ -2293,7 +2419,7 @@ msgstr "actualizó el título"
msgid "updated the title to <0>{0}</0>"
msgstr "actualizó el título a <0>{0}</0>"
#: src/components/SideNavigation.tsx:202
#: src/components/SideNavigation.tsx:237
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
@@ -2357,7 +2483,7 @@ msgstr "Ver y gestionar tu facturación y suscripción."
msgid "View docs"
msgstr "Ver documentación"
#: src/views/public/board/index.tsx:129
#: src/views/public/board/index.tsx:141
msgid "View only"
msgstr "Solo visualización"
@@ -2365,7 +2491,7 @@ msgstr "Solo visualización"
msgid "View our roadmap."
msgstr "Ver nuestra hoja de ruta."
#: src/views/public/board/index.tsx:159
#: src/views/public/board/index.tsx:171
msgid "View workspace"
msgstr "Ver espacio de trabajo"
@@ -2377,6 +2503,10 @@ msgstr "Visibilidad"
msgid "We are using the <0>AGPL-3.0 license</0>."
msgstr "Estamos usando la <0>licencia AGPL-3.0</0>."
#: src/pages/unsubscribe/index.tsx:63
msgid "We couldn't update your preferences. Please try again."
msgstr "No pudimos actualizar tus preferencias. Por favor, inténtalo de nuevo."
#: src/views/home/components/Features.tsx:152
msgid "We're just getting started. "
msgstr "Apenas estamos comenzando. "
@@ -2406,10 +2536,10 @@ 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:363
#: src/views/boards/index.tsx:22
#: src/views/board/index.tsx:386
#: src/views/boards/index.tsx:33
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:101
#: src/views/public/board/index.tsx:113
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Espacio de trabajo"
@@ -2515,6 +2645,10 @@ msgstr "Has iniciado sesión correctamente."
msgid "You have been signed up successfully."
msgstr "Te has registrado correctamente."
#: src/pages/unsubscribe/index.tsx:98
msgid "You have been unsubscribed!"
msgstr "¡Te has dado de baja!"
#: src/views/members/components/InviteMemberForm.tsx:326
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!"
@@ -2559,6 +2693,10 @@ msgstr "Tu cuenta de Trello ha sido desconectada."
msgid "Your Trello account is connected."
msgstr "Tu cuenta de Trello está conectada."
#: src/pages/unsubscribe/index.tsx:33
msgid "Your unsubscribe link is missing a token. Please open the latest email and try again."
msgstr "A tu enlace de baja le falta un token. Por favor, abre el último correo electrónico e inténtalo de nuevo."
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:49
msgid "Your workspace description has been updated."
msgstr "La descripción de tu espacio de trabajo ha sido actualizada."

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@ msgid ", or see our"
msgstr ", ou consultez notre"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#: src/views/boards/index.tsx:27
#: src/views/boards/index.tsx:38
msgid "{0}"
msgstr "{0}"
@@ -30,18 +30,18 @@ msgstr "{0}"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:218
#: src/views/boards/index.tsx:33
#: src/views/card/index.tsx:272
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:443
#: src/views/board/components/NewCardForm.tsx:452
msgid "{0} labels"
msgstr "{0} étiquettes"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:389
#: src/views/board/index.tsx:412
msgid "{0} not found"
msgstr "{0} introuvable"
@@ -79,8 +79,12 @@ msgstr "Compte"
msgid "Account deleted"
msgstr "Compte supprimé"
#: src/views/card/index.tsx:308
#: src/views/public/board/CardModal.tsx:157
#: src/providers/keyboard-shortcuts.tsx:55
msgid "Actions"
msgstr "Actions"
#: src/views/card/index.tsx:386
#: src/views/public/board/CardModal.tsx:158
msgid "Activity"
msgstr "Activité"
@@ -127,19 +131,19 @@ msgstr "Ajouter une étiquette"
msgid "Add member"
msgstr "Ajouter un membre"
#: src/views/card/components/ActivityList.tsx:69
#: src/views/card/components/ActivityList.tsx:66
msgid "added a checklist"
msgstr "a ajouté une checklist"
#: src/views/card/components/ActivityList.tsx:72
#: src/views/card/components/ActivityList.tsx:69
msgid "added a checklist item"
msgstr "a ajouté un élément à la checklist"
#: src/views/card/components/ActivityList.tsx:65
#: src/views/card/components/ActivityList.tsx:62
msgid "added a label to the card"
msgstr "a ajouté une étiquette à la carte"
#: src/views/card/components/ActivityList.tsx:67
#: src/views/card/components/ActivityList.tsx:64
msgid "added a member to the card"
msgstr "a ajouté un membre à la carte"
@@ -308,9 +312,9 @@ msgstr "Portail de facturation"
msgid "Blog Post"
msgstr "Article de blog"
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:218
#: src/views/public/board/index.tsx:101
#: src/views/board/index.tsx:386
#: src/views/card/index.tsx:272
#: src/views/public/board/index.tsx:113
msgid "Board"
msgstr "Tableau"
@@ -327,7 +331,7 @@ msgstr "Le nom du tableau ne peut pas dépasser 100 caractères"
msgid "Board name is required"
msgstr "Le nom du tableau est requis"
#: src/views/public/board/index.tsx:153
#: src/views/public/board/index.tsx:165
msgid "Board not found"
msgstr "Tableau introuvable"
@@ -343,7 +347,7 @@ msgstr "L'URL du tableau ne peut contenir que des lettres, des chiffres et des t
msgid "Board URL cannot exceed 60 characters"
msgstr "L'URL du tableau ne peut pas dépasser 60 caractères"
#: src/views/public/board/index.tsx:68
#: src/views/public/board/index.tsx:79
msgid "Board URL copied to clipboard"
msgstr "URL du tableau copiée dans le presse-papiers"
@@ -360,7 +364,7 @@ msgstr "Visibilité du tableau"
msgid "Board visibility updated"
msgstr "Visibilité du tableau mise à jour"
#: src/components/SideNavigation.tsx:91
#: src/components/SideNavigation.tsx:97
msgid "Boards"
msgstr "Tableaux"
@@ -391,15 +395,16 @@ msgstr "Rapport de bug"
msgid "Cancel"
msgstr "Annuler"
#: src/views/card/index.tsx:218
#: src/views/card/index.tsx:272
msgid "Card"
msgstr "Carte"
#: src/views/card/index.tsx:264
#: src/views/card/index.tsx:306
#: src/views/card/index.tsx:342
msgid "Card not found"
msgstr "Carte introuvable"
#: src/views/board/components/NewCardForm.tsx:310
#: src/views/board/components/NewCardForm.tsx:319
msgid "Card title"
msgstr "Titre de la carte"
@@ -414,6 +419,11 @@ msgstr "Modifier le mot de passe"
msgid "Change your language preferences."
msgstr "Modifiez vos préférences linguistiques."
#: src/views/card/components/ActivityList.tsx:220
#: src/views/card/components/ActivityList.tsx:234
msgid "changed the due date to <0>{formattedDate}</0>"
msgstr "a modifié la date d'échéance pour <0>{formattedDate}</0>"
#: src/views/oss-friends/index.tsx:61
msgid "Check out some of our favorite open source projects."
msgstr "Découvrez certains de nos projets open source préférés."
@@ -432,7 +442,7 @@ msgstr "Nom de la checklist"
msgid "Checklists"
msgstr "Listes de contrôle"
#: src/views/board/components/Filters.tsx:180
#: src/views/board/components/Filters.tsx:227
msgid "Clear filters"
msgstr "Effacer les filtres"
@@ -484,7 +494,7 @@ msgstr "Terminé"
msgid "Complete control and ownership:"
msgstr "Contrôle et propriété complets :"
#: src/views/card/components/ActivityList.tsx:74
#: src/views/card/components/ActivityList.tsx:71
msgid "completed a checklist item"
msgstr "a terminé un élément de la checklist"
@@ -493,6 +503,10 @@ msgstr "a terminé un élément de la checklist"
msgid "completed checklist item <0>{0}</0>"
msgstr "a terminé l'élément <0>{0}</0> de la checklist"
#: src/pages/unsubscribe/index.tsx:81
msgid "Confirm your email preferences:"
msgstr "Confirmez vos préférences d'e-mail :"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:150
msgid "Confirm your new password"
msgstr "Confirmez votre nouveau mot de passe"
@@ -523,12 +537,12 @@ msgstr "Contactez-nous"
msgid "Content Creation"
msgstr "Création de contenu"
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Continue with "
msgstr "Continuer avec "
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
#: src/components/AuthForm.tsx:351
#: src/components/AuthForm.tsx:355
msgid "Continue with {0}"
msgstr "Continuer avec {0}"
@@ -546,7 +560,7 @@ msgid "Create {0}"
msgstr "Créer {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewCardForm.tsx:516
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Créer un autre"
@@ -555,7 +569,7 @@ msgstr "Créer un autre"
msgid "Create API key"
msgstr "Créer une clé API"
#: src/views/board/components/NewCardForm.tsx:479
#: src/views/board/components/NewCardForm.tsx:526
msgid "Create card"
msgstr "Créer une carte"
@@ -577,6 +591,7 @@ msgstr "Créer une liste"
#. placeholder {0}: isTemplate ? "template" : "board"
#: src/views/boards/components/BoardsList.tsx:45
#: src/views/boards/index.tsx:26
msgid "Create new {0}"
msgstr "Créer un nouveau {0}"
@@ -584,12 +599,13 @@ msgstr "Créer un nouveau {0}"
msgid "Create new key"
msgstr "Créer une nouvelle clé"
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/board/components/NewCardForm.tsx:414
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Créer une nouvelle étiquette"
#: src/views/board/index.tsx:478
#: src/views/board/index.tsx:65
#: src/views/board/index.tsx:503
msgid "Create new list"
msgstr "Créer une nouvelle liste"
@@ -598,11 +614,11 @@ msgid "Create template"
msgstr "Créer un modèle"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:149
#: src/components/WorkspaceMenu.tsx:153
msgid "Create workspace"
msgstr "Créer un espace de travail"
#: src/views/card/components/ActivityList.tsx:61
#: src/views/card/components/ActivityList.tsx:58
msgid "created the card"
msgstr "a créé la carte"
@@ -648,7 +664,7 @@ msgstr "URL d'espace de travail personnalisée"
msgid "Customer Support"
msgstr "Support client"
#: src/components/UserMenu.tsx:153
#: src/components/UserMenu.tsx:155
msgid "Dark"
msgstr "Sombre"
@@ -697,11 +713,11 @@ msgstr "Supprimer le modèle"
msgid "Delete workspace"
msgstr "Supprimer l'espace de travail"
#: src/views/card/components/ActivityList.tsx:71
#: src/views/card/components/ActivityList.tsx:68
msgid "deleted a checklist"
msgstr "a supprimé une checklist"
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:73
msgid "deleted a checklist item"
msgstr "a supprimé un élément de la checklist"
@@ -747,6 +763,10 @@ msgstr "Nom d'affichage mis à jour"
msgid "Do you offer a free plan?"
msgstr "Proposez-vous un plan gratuit ?"
#: src/pages/unsubscribe/index.tsx:78
msgid "Do you want to unsubscribe?"
msgstr "Voulez-vous vous désabonner ?"
#: src/components/FeedbackModal.tsx:109
msgid "docs"
msgstr "documentation"
@@ -755,7 +775,7 @@ msgstr "documentation"
msgid "Docs"
msgstr "Documentation"
#: src/components/UserMenu.tsx:191
#: src/components/UserMenu.tsx:206
#: src/views/boards/components/TemplateBoards.tsx:24
#: src/views/home/components/Footer.tsx:78
#: src/views/home/components/Header.tsx:36
@@ -777,6 +797,28 @@ msgstr "Fait"
msgid "Download failed"
msgstr "Échec du téléchargement"
#: src/views/board/components/Filters.tsx:171
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/card/index.tsx:138
msgid "Due date"
msgstr "Date d'échéance"
#: src/views/board/components/Filters.tsx:132
msgid "Due next month"
msgstr "Échéance le mois prochain"
#: src/views/board/components/Filters.tsx:127
msgid "Due next week"
msgstr "Échéance la semaine prochaine"
#: src/views/board/components/Filters.tsx:117
msgid "Due today"
msgstr "Échéance aujourd'hui"
#: src/views/board/components/Filters.tsx:122
msgid "Due tomorrow"
msgstr "Échéance demain"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
@@ -798,7 +840,7 @@ msgstr "Modifier l'URL de l'espace de travail"
msgid "Editing"
msgstr "Édition"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "e-mail"
@@ -818,11 +860,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:398
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Saisissez votre adresse e-mail"
#: src/components/AuthForm.tsx:384
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Saisissez votre nom"
@@ -830,7 +872,7 @@ msgstr "Saisissez votre nom"
msgid "Enter your new password"
msgstr "Saisissez votre nouveau mot de passe"
#: src/components/AuthForm.tsx:411
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Saisissez votre mot de passe"
@@ -979,7 +1021,7 @@ msgid "Features"
msgstr "Fonctionnalités"
#: src/components/FeedbackModal.tsx:59
#: src/components/UserMenu.tsx:199
#: src/components/UserMenu.tsx:214
msgid "Feedback"
msgstr "Commentaires"
@@ -987,7 +1029,7 @@ msgstr "Commentaires"
msgid "Feedback sent"
msgstr "Commentaires envoyés"
#: src/views/board/components/Filters.tsx:174
#: src/views/board/components/Filters.tsx:221
msgid "Filter"
msgstr "Filtrer"
@@ -1024,6 +1066,10 @@ msgstr "Temps plein"
msgid "Fun"
msgstr "Amusant"
#: src/providers/keyboard-shortcuts.tsx:53
msgid "General"
msgstr "Général"
#: src/views/auth/signup/index.tsx:69
#: src/views/home/components/Cta.tsx:61
#: src/views/home/components/Header.tsx:106
@@ -1043,7 +1089,7 @@ msgstr "Commencer"
msgid "Get started by creating a new {0}"
msgstr "Commencez par créer un nouveau {0}"
#: src/views/board/index.tsx:470
#: src/views/board/index.tsx:495
msgid "Get started by creating a new list"
msgstr "Commencez par créer une nouvelle liste"
@@ -1077,6 +1123,22 @@ msgstr "Aller à l'accueil"
msgid "Go to app"
msgstr "Accéder à l'application"
#: src/components/SideNavigation.tsx:105
msgid "Go to boards"
msgstr "Aller aux tableaux"
#: src/components/SideNavigation.tsx:129
msgid "Go to members"
msgstr "Aller aux membres"
#: src/components/SideNavigation.tsx:141
msgid "Go to settings"
msgstr "Aller aux paramètres"
#: src/components/SideNavigation.tsx:117
msgid "Go to templates"
msgstr "Aller aux modèles"
#: src/views/boards/components/TemplateBoards.tsx:18
msgid "High Priority"
msgstr "Haute priorité"
@@ -1126,7 +1188,7 @@ msgstr "Idées"
msgid "Ideas to improve this page..."
msgstr "Idées pour améliorer cette page..."
#: src/views/boards/index.tsx:39
#: src/views/boards/index.tsx:50
msgid "Import"
msgstr "Importer"
@@ -1257,9 +1319,13 @@ msgstr "Kanban réinventé"
msgid "Keep in mind that this action is irreversible."
msgstr "Gardez à l'esprit que cette action est irréversible."
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
#: src/providers/keyboard-shortcuts.tsx:320
msgid "Keyboard Shortcuts"
msgstr "Raccourcis clavier"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/NewCardForm.tsx:418
#: src/views/card/index.tsx:120
msgid "Labels"
msgstr "Étiquettes"
@@ -1305,15 +1371,15 @@ msgstr "Mentions légales"
msgid "License"
msgstr "Licence"
#: src/components/UserMenu.tsx:167
#: src/components/UserMenu.tsx:169
msgid "Light"
msgstr "Clair"
#: src/views/public/board/index.tsx:66
#: src/views/public/board/index.tsx:77
msgid "Link copied"
msgstr "Lien copié"
#: src/views/card/index.tsx:111
#: src/views/card/index.tsx:112
msgid "List"
msgstr "Liste"
@@ -1321,7 +1387,7 @@ msgstr "Liste"
msgid "List name"
msgstr "Nom de la liste"
#: src/views/board/components/Filters.tsx:123
#: src/views/board/components/Filters.tsx:163
msgid "Lists"
msgstr "Listes"
@@ -1329,7 +1395,7 @@ msgstr "Listes"
msgid "Login | kan.bn"
msgstr "Connexion | kan.bn"
#: src/components/UserMenu.tsx:209
#: src/components/UserMenu.tsx:224
msgid "Logout"
msgstr "Déconnexion"
@@ -1345,7 +1411,7 @@ msgstr "Adoré par les équipes du monde entier"
msgid "Low Priority"
msgstr "Priorité basse"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "lien magique"
@@ -1353,7 +1419,7 @@ msgstr "lien magique"
msgid "Make template"
msgstr "Créer un modèle"
#: src/views/card/components/ActivityList.tsx:75
#: src/views/card/components/ActivityList.tsx:72
msgid "marked a checklist item as incomplete"
msgstr "a marqué un élément de la checklist comme incomplet"
@@ -1366,10 +1432,10 @@ msgstr "a marqué l'élément <0>{0}</0> de la checklist comme incomplet"
msgid "Medium Priority"
msgstr "Priorité moyenne"
#: src/components/SideNavigation.tsx:101
#: src/views/board/components/Filters.tsx:107
#: src/views/board/components/NewCardForm.tsx:367
#: src/views/card/index.tsx:128
#: src/components/SideNavigation.tsx:121
#: src/views/board/components/Filters.tsx:147
#: src/views/board/components/NewCardForm.tsx:376
#: src/views/card/index.tsx:129
#: src/views/members/index.tsx:178
msgid "Members"
msgstr "Membres"
@@ -1394,7 +1460,7 @@ msgstr "facturation mensuelle"
msgid "moved the card from <0>{0}</0> to<1>{1}</1>"
msgstr "a déplacé la carte de <0>{0}</0> vers <1>{1}</1>"
#: src/views/card/components/ActivityList.tsx:64
#: src/views/card/components/ActivityList.tsx:61
msgid "moved the card to another list"
msgstr "a déplacé la carte vers une autre liste"
@@ -1404,11 +1470,15 @@ msgstr "a déplacé la carte vers une autre liste"
msgid "Name"
msgstr "Nom"
#: src/providers/keyboard-shortcuts.tsx:54
msgid "Navigation"
msgstr "Navigation"
#: src/components/FeedbackModal.tsx:95
msgid "Need help?"
msgstr "Besoin d'aide ?"
#: src/views/boards/index.tsx:50
#: src/views/boards/index.tsx:62
#: src/views/home/components/Features.tsx:73
msgid "New"
msgstr "Nouveau"
@@ -1422,7 +1492,7 @@ msgstr "Nouveau {0}"
msgid "New API key"
msgstr "Nouvelle clé API"
#: src/views/board/components/NewCardForm.tsx:293
#: src/views/board/components/NewCardForm.tsx:302
msgid "New card"
msgstr "Nouvelle carte"
@@ -1439,7 +1509,7 @@ msgid "New label"
msgstr "Nouvelle étiquette"
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
#: src/views/board/index.tsx:466
msgid "New list"
msgstr "Nouvelle liste"
@@ -1476,7 +1546,7 @@ msgstr "Prochaines étapes"
msgid "No {0}"
msgstr "Aucun {0}"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Aucune méthode d'authentification n'est actuellement disponible"
@@ -1488,11 +1558,19 @@ msgstr "Aucun tableau trouvé"
msgid "No credit card required"
msgstr "Aucune carte de crédit requise"
#: src/views/board/components/Filters.tsx:137
msgid "No dates"
msgstr "Aucune date"
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "Aucune URL de téléchargement disponible pour cette pièce jointe."
#: src/views/board/index.tsx:467
#: src/providers/keyboard-shortcuts.tsx:333
msgid "No keyboard shortcuts registered."
msgstr "Aucun raccourci clavier enregistré."
#: src/views/board/index.tsx:492
msgid "No lists"
msgstr "Aucune liste"
@@ -1516,6 +1594,14 @@ msgstr "Une fois que vous supprimez votre compte, il n'y a pas de retour possibl
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/WorkspaceMenu.tsx:31
msgid "Open command menu"
msgstr "Ouvrir le menu de commandes"
#: src/providers/keyboard-shortcuts.tsx:171
msgid "Open keyboard shortcuts"
msgstr "Ouvrir les raccourcis clavier"
#: src/views/pricing/components/FeatureComparisonTable.tsx:150
msgid "Open source"
msgstr "Open source"
@@ -1524,7 +1610,7 @@ msgstr "Open source"
msgid "Open Source Friends"
msgstr "Amis Open Source"
#: src/components/AuthForm.tsx:374
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "ou"
@@ -1537,6 +1623,10 @@ msgstr "Organisez et trouvez rapidement des cartes avec des outils de filtrage p
msgid "OSS Friends"
msgstr "Amis OSS"
#: src/views/board/components/Filters.tsx:112
msgid "Overdue"
msgstr "En retard"
#: src/views/pricing/components/Pricing.tsx:73
#: src/views/pricing/components/PricingTiers.tsx:70
msgid "Own your data"
@@ -1606,15 +1696,15 @@ msgstr "Plateforme"
msgid "Please confirm your new password"
msgstr "Veuillez confirmer votre nouveau mot de passe"
#: src/components/AuthForm.tsx:402
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Veuillez saisir une adresse e-mail valide"
#: src/components/AuthForm.tsx:388
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Veuillez saisir un nom valide"
#: src/components/AuthForm.tsx:416
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Veuillez saisir un mot de passe valide"
@@ -1625,14 +1715,14 @@ msgstr "Veuillez sélectionner un fichier à télécharger."
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:177
#: src/views/board/components/NewCardForm.tsx:184
#: src/views/board/components/NewListForm.tsx:79
#: src/views/board/components/NewTemplateForm.tsx:61
#: src/views/board/components/NewTemplateForm.tsx:77
#: src/views/board/components/UpdateBoardSlugForm.tsx:73
#: src/views/board/components/VisibilityButton.tsx:54
#: src/views/board/index.tsx:151
#: src/views/board/index.tsx:207
#: src/views/board/index.tsx:174
#: src/views/board/index.tsx:230
#: src/views/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
@@ -1642,13 +1732,15 @@ 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/DueDateSelector.tsx:59
#: 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/NewChecklistForm.tsx:70
#: src/views/card/components/NewChecklistItemForm.tsx:89
#: src/views/card/components/NewCommentForm.tsx:31
#: src/views/card/index.tsx:177
#: src/views/card/index.tsx:187
#: src/views/card/index.tsx:200
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:241
@@ -1748,11 +1840,11 @@ msgstr "Supprimer"
msgid "Remove member"
msgstr "Supprimer le membre"
#: src/views/card/components/ActivityList.tsx:66
#: src/views/card/components/ActivityList.tsx:63
msgid "removed a label from the card"
msgstr "a supprimé une étiquette de la carte"
#: src/views/card/components/ActivityList.tsx:68
#: src/views/card/components/ActivityList.tsx:65
msgid "removed a member from the card"
msgstr "a retiré un membre de la carte"
@@ -1761,7 +1853,12 @@ msgstr "a retiré un membre de la carte"
msgid "removed label <0>{0}</0>"
msgstr "a supprimé l'étiquette <0>{0}</0>"
#: src/views/card/components/ActivityList.tsx:70
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:241
msgid "removed the due date"
msgstr "a supprimé la date d'échéance"
#: src/views/card/components/ActivityList.tsx:67
msgid "renamed a checklist"
msgstr "a renommé une checklist"
@@ -1864,8 +1961,16 @@ msgstr "Envoyer des commentaires"
msgid "Senior"
msgstr "Senior"
#: src/views/card/components/DueDateSelector.tsx:116
msgid "Set due date"
msgstr "Définir la date d'échéance"
#: src/views/card/components/ActivityList.tsx:74
msgid "set the due date"
msgstr "a défini la date d'échéance"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:106
#: src/components/SideNavigation.tsx:133
msgid "Settings"
msgstr "Paramètres"
@@ -1889,6 +1994,10 @@ msgstr "Paramètres | Intégrations"
msgid "Settings | Workspace"
msgstr "Paramètres | Espace de travail"
#: src/components/UserMenu.tsx:184
msgid "Shortcuts"
msgstr "Raccourcis"
#: src/views/home/components/Header.tsx:104
msgid "Sign in"
msgstr "Se connecter"
@@ -1914,7 +2023,7 @@ msgstr "Inscription désactivée"
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:434
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "S'inscrire avec "
@@ -1957,7 +2066,7 @@ msgstr "Succès"
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Optimisez votre espace de travail pour seulement 29 $/mois. Voici ce que vous obtiendrez :"
#: src/components/UserMenu.tsx:180
#: src/components/UserMenu.tsx:195
msgid "Support"
msgstr "Assistance"
@@ -1966,7 +2075,7 @@ msgstr "Assistance"
msgid "Support the development of the project"
msgstr "Soutenir le développement du projet"
#: src/components/UserMenu.tsx:139
#: src/components/UserMenu.tsx:141
msgid "System"
msgstr "Système"
@@ -1981,8 +2090,8 @@ msgstr "Forfait d'équipe"
msgid "Teams"
msgstr "Équipes"
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
#: src/views/board/index.tsx:386
#: src/views/board/index.tsx:421
msgid "Template"
msgstr "Modèle"
@@ -2002,7 +2111,7 @@ msgstr "Le nom du modèle ne peut pas dépasser 100 caractères"
msgid "Template name is required"
msgstr "Le nom du modèle est requis"
#: src/components/SideNavigation.tsx:96
#: src/components/SideNavigation.tsx:109
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Modèles"
@@ -2036,7 +2145,7 @@ msgstr "L'alternative open source <0/> à Trello"
msgid "The visibility of your board has been set to {0}."
msgstr "La visibilité de votre tableau a été définie sur {0}."
#: src/components/UserMenu.tsx:126
#: src/components/UserMenu.tsx:128
msgid "Theme"
msgstr "Thème"
@@ -2056,7 +2165,7 @@ msgstr "Cette action ne peut pas être annulée."
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:156
#: src/views/public/board/index.tsx:168
msgid "This board is private or does not exist"
msgstr "Ce tableau est privé ou n'existe pas"
@@ -2130,7 +2239,11 @@ msgstr "Impossible d'ajouter l'élément à la liste de contrôle"
msgid "Unable to add comment"
msgstr "Impossible d'ajouter un commentaire"
#: src/views/board/components/NewCardForm.tsx:174
#: src/views/card/index.tsx:199
msgid "Unable to add label"
msgstr ""
#: src/views/board/components/NewCardForm.tsx:181
msgid "Unable to create card"
msgstr "Impossible de créer la carte"
@@ -2187,8 +2300,8 @@ msgstr "Impossible de mettre à jour l'URL du tableau"
msgid "Unable to update board visibility"
msgstr "Impossible de mettre à jour la visibilité du tableau"
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
#: src/views/board/index.tsx:229
#: src/views/card/index.tsx:186
msgid "Unable to update card"
msgstr "Impossible de mettre à jour la carte"
@@ -2204,11 +2317,15 @@ 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/DueDateSelector.tsx:58
msgid "Unable to update due date"
msgstr "Impossible de mettre à jour la date d'échéance"
#: src/views/card/components/LabelSelector.tsx:71
msgid "Unable to update labels"
msgstr "Impossible de mettre à jour les étiquettes"
#: src/views/board/index.tsx:150
#: src/views/board/index.tsx:173
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Impossible de mettre à jour la liste"
@@ -2264,6 +2381,11 @@ msgstr "Membres illimités"
msgid "Unlimited workspaces"
msgstr "Espaces de travail illimités"
#: src/pages/unsubscribe/index.tsx:68
#: src/pages/unsubscribe/index.tsx:93
msgid "Unsubscribe"
msgstr "Se désabonner"
#: src/views/board/components/UpdateBoardSlugForm.tsx:175
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
@@ -2276,15 +2398,19 @@ msgstr "Mettre à jour"
msgid "Update label"
msgstr "Mettre à jour l'étiquette"
#: src/views/card/components/ActivityList.tsx:73
#: src/views/card/components/ActivityList.tsx:70
msgid "updated a checklist item"
msgstr "a mis à jour un élément de la liste de contrôle"
#: src/views/card/components/ActivityList.tsx:63
#: src/views/card/components/ActivityList.tsx:60
msgid "updated the description"
msgstr "a mis à jour la description"
#: src/views/card/components/ActivityList.tsx:62
#: src/views/card/components/ActivityList.tsx:75
msgid "updated the due date"
msgstr "a mis à jour la date d'échéance"
#: src/views/card/components/ActivityList.tsx:59
msgid "updated the title"
msgstr "a mis à jour le titre"
@@ -2293,7 +2419,7 @@ msgstr "a mis à jour le titre"
msgid "updated the title to <0>{0}</0>"
msgstr "a mis à jour le titre en <0>{0}</0>"
#: src/components/SideNavigation.tsx:202
#: src/components/SideNavigation.tsx:237
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
@@ -2357,7 +2483,7 @@ msgstr "Consultez et gérez votre facturation et votre abonnement."
msgid "View docs"
msgstr "Voir la documentation"
#: src/views/public/board/index.tsx:129
#: src/views/public/board/index.tsx:141
msgid "View only"
msgstr "Affichage uniquement"
@@ -2365,7 +2491,7 @@ msgstr "Affichage uniquement"
msgid "View our roadmap."
msgstr "Consultez notre feuille de route."
#: src/views/public/board/index.tsx:159
#: src/views/public/board/index.tsx:171
msgid "View workspace"
msgstr "Voir l'espace de travail"
@@ -2377,6 +2503,10 @@ msgstr "Visibilité"
msgid "We are using the <0>AGPL-3.0 license</0>."
msgstr "Nous utilisons la <0>licence AGPL-3.0</0>."
#: src/pages/unsubscribe/index.tsx:63
msgid "We couldn't update your preferences. Please try again."
msgstr "Nous n'avons pas pu mettre à jour vos préférences. Veuillez réessayer."
#: src/views/home/components/Features.tsx:152
msgid "We're just getting started. "
msgstr "Nous ne faisons que commencer. "
@@ -2406,10 +2536,10 @@ 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:363
#: src/views/boards/index.tsx:22
#: src/views/board/index.tsx:386
#: src/views/boards/index.tsx:33
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:101
#: src/views/public/board/index.tsx:113
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Espace de travail"
@@ -2515,6 +2645,10 @@ msgstr "Vous vous êtes connecté avec succès."
msgid "You have been signed up successfully."
msgstr "Vous vous êtes inscrit avec succès."
#: src/pages/unsubscribe/index.tsx:98
msgid "You have been unsubscribed!"
msgstr "Vous avez été désabonné !"
#: src/views/members/components/InviteMemberForm.tsx:326
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 !"
@@ -2559,6 +2693,10 @@ msgstr "Votre compte Trello a été déconnecté."
msgid "Your Trello account is connected."
msgstr "Votre compte Trello est connecté."
#: src/pages/unsubscribe/index.tsx:33
msgid "Your unsubscribe link is missing a token. Please open the latest email and try again."
msgstr "Votre lien de désabonnement ne contient pas de jeton. Veuillez ouvrir le dernier e-mail reçu et réessayer."
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:49
msgid "Your workspace description has been updated."
msgstr "La description de votre espace de travail a été mise à jour."

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,7 @@ export const locales = [
"nl",
"ru",
"pl",
"ptbr"
] as const;
export type Locale = (typeof locales)[number];
@@ -22,4 +23,5 @@ export const localeNames: Record<Locale, string> = {
nl: "Nederlands",
ru: "Русский",
pl: "Polski",
ptbr: "Português",
};

View File

@@ -22,7 +22,7 @@ msgid ", or see our"
msgstr ", o consulta i nostri"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#: src/views/boards/index.tsx:27
#: src/views/boards/index.tsx:38
msgid "{0}"
msgstr "{0}"
@@ -30,18 +30,18 @@ msgstr "{0}"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:218
#: src/views/boards/index.tsx:33
#: src/views/card/index.tsx:272
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:443
#: src/views/board/components/NewCardForm.tsx:452
msgid "{0} labels"
msgstr "{0} etichette"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:389
#: src/views/board/index.tsx:412
msgid "{0} not found"
msgstr "{0} non trovato"
@@ -79,8 +79,12 @@ msgstr "Account"
msgid "Account deleted"
msgstr "Account eliminato"
#: src/views/card/index.tsx:308
#: src/views/public/board/CardModal.tsx:157
#: src/providers/keyboard-shortcuts.tsx:55
msgid "Actions"
msgstr "Azioni"
#: src/views/card/index.tsx:386
#: src/views/public/board/CardModal.tsx:158
msgid "Activity"
msgstr "Attività"
@@ -127,19 +131,19 @@ msgstr "Aggiungi etichetta"
msgid "Add member"
msgstr "Aggiungi membro"
#: src/views/card/components/ActivityList.tsx:69
#: src/views/card/components/ActivityList.tsx:66
msgid "added a checklist"
msgstr "ha aggiunto una checklist"
#: src/views/card/components/ActivityList.tsx:72
#: src/views/card/components/ActivityList.tsx:69
msgid "added a checklist item"
msgstr "ha aggiunto un elemento alla checklist"
#: src/views/card/components/ActivityList.tsx:65
#: src/views/card/components/ActivityList.tsx:62
msgid "added a label to the card"
msgstr "ha aggiunto un'etichetta alla carta"
#: src/views/card/components/ActivityList.tsx:67
#: src/views/card/components/ActivityList.tsx:64
msgid "added a member to the card"
msgstr "ha aggiunto un membro alla carta"
@@ -308,9 +312,9 @@ msgstr "Portale di fatturazione"
msgid "Blog Post"
msgstr "Post del blog"
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:218
#: src/views/public/board/index.tsx:101
#: src/views/board/index.tsx:386
#: src/views/card/index.tsx:272
#: src/views/public/board/index.tsx:113
msgid "Board"
msgstr "Bacheca"
@@ -327,7 +331,7 @@ msgstr "Il nome della bacheca non può superare i 100 caratteri"
msgid "Board name is required"
msgstr "Il nome della bacheca è obbligatorio"
#: src/views/public/board/index.tsx:153
#: src/views/public/board/index.tsx:165
msgid "Board not found"
msgstr "Bacheca non trovata"
@@ -343,7 +347,7 @@ msgstr "L'URL della bacheca può contenere solo lettere, numeri e trattini"
msgid "Board URL cannot exceed 60 characters"
msgstr "L'URL della bacheca non può superare i 60 caratteri"
#: src/views/public/board/index.tsx:68
#: src/views/public/board/index.tsx:79
msgid "Board URL copied to clipboard"
msgstr "URL della bacheca copiato negli appunti"
@@ -360,7 +364,7 @@ msgstr "Visibilità bacheca"
msgid "Board visibility updated"
msgstr "Visibilità della bacheca aggiornata"
#: src/components/SideNavigation.tsx:91
#: src/components/SideNavigation.tsx:97
msgid "Boards"
msgstr "Bacheche"
@@ -391,15 +395,16 @@ msgstr "Segnalazione bug"
msgid "Cancel"
msgstr "Annulla"
#: src/views/card/index.tsx:218
#: src/views/card/index.tsx:272
msgid "Card"
msgstr "Carta"
#: src/views/card/index.tsx:264
#: src/views/card/index.tsx:306
#: src/views/card/index.tsx:342
msgid "Card not found"
msgstr "Carta non trovata"
#: src/views/board/components/NewCardForm.tsx:310
#: src/views/board/components/NewCardForm.tsx:319
msgid "Card title"
msgstr "Titolo della carta"
@@ -414,6 +419,11 @@ msgstr "Cambia password"
msgid "Change your language preferences."
msgstr "Modifica le tue preferenze di lingua."
#: src/views/card/components/ActivityList.tsx:220
#: src/views/card/components/ActivityList.tsx:234
msgid "changed the due date to <0>{formattedDate}</0>"
msgstr "ha cambiato la data di scadenza a <0>{formattedDate}</0>"
#: src/views/oss-friends/index.tsx:61
msgid "Check out some of our favorite open source projects."
msgstr "Dai un'occhiata ad alcuni dei nostri progetti open source preferiti."
@@ -432,7 +442,7 @@ msgstr "Nome della checklist"
msgid "Checklists"
msgstr "Checklist"
#: src/views/board/components/Filters.tsx:180
#: src/views/board/components/Filters.tsx:227
msgid "Clear filters"
msgstr "Cancella filtri"
@@ -484,7 +494,7 @@ msgstr "Completato"
msgid "Complete control and ownership:"
msgstr "Controllo e proprietà completi:"
#: src/views/card/components/ActivityList.tsx:74
#: src/views/card/components/ActivityList.tsx:71
msgid "completed a checklist item"
msgstr "ha completato un elemento della checklist"
@@ -493,6 +503,10 @@ msgstr "ha completato un elemento della checklist"
msgid "completed checklist item <0>{0}</0>"
msgstr "ha completato l'elemento <0>{0}</0> della checklist"
#: src/pages/unsubscribe/index.tsx:81
msgid "Confirm your email preferences:"
msgstr "Conferma le tue preferenze email:"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:150
msgid "Confirm your new password"
msgstr "Conferma la tua nuova password"
@@ -523,12 +537,12 @@ msgstr "Contattaci"
msgid "Content Creation"
msgstr "Creazione contenuti"
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Continue with "
msgstr "Continua con "
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
#: src/components/AuthForm.tsx:351
#: src/components/AuthForm.tsx:355
msgid "Continue with {0}"
msgstr "Continua con {0}"
@@ -546,7 +560,7 @@ msgid "Create {0}"
msgstr "Crea {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewCardForm.tsx:516
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Crea un altro"
@@ -555,7 +569,7 @@ msgstr "Crea un altro"
msgid "Create API key"
msgstr "Crea chiave API"
#: src/views/board/components/NewCardForm.tsx:479
#: src/views/board/components/NewCardForm.tsx:526
msgid "Create card"
msgstr "Crea carta"
@@ -577,6 +591,7 @@ msgstr "Crea lista"
#. placeholder {0}: isTemplate ? "template" : "board"
#: src/views/boards/components/BoardsList.tsx:45
#: src/views/boards/index.tsx:26
msgid "Create new {0}"
msgstr "Crea nuovo {0}"
@@ -584,12 +599,13 @@ msgstr "Crea nuovo {0}"
msgid "Create new key"
msgstr "Crea nuova chiave"
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/board/components/NewCardForm.tsx:414
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Crea nuova etichetta"
#: src/views/board/index.tsx:478
#: src/views/board/index.tsx:65
#: src/views/board/index.tsx:503
msgid "Create new list"
msgstr "Crea nuova lista"
@@ -598,11 +614,11 @@ msgid "Create template"
msgstr "Crea template"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:149
#: src/components/WorkspaceMenu.tsx:153
msgid "Create workspace"
msgstr "Crea spazio di lavoro"
#: src/views/card/components/ActivityList.tsx:61
#: src/views/card/components/ActivityList.tsx:58
msgid "created the card"
msgstr "ha creato la carta"
@@ -648,7 +664,7 @@ msgstr "URL personalizzato dell'area di lavoro"
msgid "Customer Support"
msgstr "Assistenza clienti"
#: src/components/UserMenu.tsx:153
#: src/components/UserMenu.tsx:155
msgid "Dark"
msgstr "Scuro"
@@ -697,11 +713,11 @@ msgstr "Elimina template"
msgid "Delete workspace"
msgstr "Elimina spazio di lavoro"
#: src/views/card/components/ActivityList.tsx:71
#: src/views/card/components/ActivityList.tsx:68
msgid "deleted a checklist"
msgstr "ha eliminato una checklist"
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:73
msgid "deleted a checklist item"
msgstr "ha eliminato un elemento della checklist"
@@ -747,6 +763,10 @@ msgstr "Nome visualizzato aggiornato"
msgid "Do you offer a free plan?"
msgstr "Offrite un piano gratuito?"
#: src/pages/unsubscribe/index.tsx:78
msgid "Do you want to unsubscribe?"
msgstr "Vuoi annullare l'iscrizione?"
#: src/components/FeedbackModal.tsx:109
msgid "docs"
msgstr "documenti"
@@ -755,7 +775,7 @@ msgstr "documenti"
msgid "Docs"
msgstr "Documenti"
#: src/components/UserMenu.tsx:191
#: src/components/UserMenu.tsx:206
#: src/views/boards/components/TemplateBoards.tsx:24
#: src/views/home/components/Footer.tsx:78
#: src/views/home/components/Header.tsx:36
@@ -777,6 +797,28 @@ msgstr "Fatto"
msgid "Download failed"
msgstr "Download fallito"
#: src/views/board/components/Filters.tsx:171
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/card/index.tsx:138
msgid "Due date"
msgstr "Data di scadenza"
#: src/views/board/components/Filters.tsx:132
msgid "Due next month"
msgstr "Scade il mese prossimo"
#: src/views/board/components/Filters.tsx:127
msgid "Due next week"
msgstr "Scade la prossima settimana"
#: src/views/board/components/Filters.tsx:117
msgid "Due today"
msgstr "Scade oggi"
#: src/views/board/components/Filters.tsx:122
msgid "Due tomorrow"
msgstr "Scade domani"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
@@ -798,7 +840,7 @@ msgstr "Modifica URL dell'area di lavoro"
msgid "Editing"
msgstr "Modifica"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "email"
@@ -818,11 +860,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:398
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Inserisci il tuo indirizzo email"
#: src/components/AuthForm.tsx:384
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Inserisci il tuo nome"
@@ -830,7 +872,7 @@ msgstr "Inserisci il tuo nome"
msgid "Enter your new password"
msgstr "Inserisci la tua nuova password"
#: src/components/AuthForm.tsx:411
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Inserisci la tua password"
@@ -979,7 +1021,7 @@ msgid "Features"
msgstr "Funzionalità"
#: src/components/FeedbackModal.tsx:59
#: src/components/UserMenu.tsx:199
#: src/components/UserMenu.tsx:214
msgid "Feedback"
msgstr "Feedback"
@@ -987,7 +1029,7 @@ msgstr "Feedback"
msgid "Feedback sent"
msgstr "Feedback inviato"
#: src/views/board/components/Filters.tsx:174
#: src/views/board/components/Filters.tsx:221
msgid "Filter"
msgstr "Filtra"
@@ -1024,6 +1066,10 @@ msgstr "Tempo pieno"
msgid "Fun"
msgstr "Divertimento"
#: src/providers/keyboard-shortcuts.tsx:53
msgid "General"
msgstr "Generale"
#: src/views/auth/signup/index.tsx:69
#: src/views/home/components/Cta.tsx:61
#: src/views/home/components/Header.tsx:106
@@ -1043,7 +1089,7 @@ msgstr "Inizia ora"
msgid "Get started by creating a new {0}"
msgstr "Inizia creando un nuovo {0}"
#: src/views/board/index.tsx:470
#: src/views/board/index.tsx:495
msgid "Get started by creating a new list"
msgstr "Inizia creando una nuova lista"
@@ -1077,6 +1123,22 @@ msgstr "Vai alla home"
msgid "Go to app"
msgstr "Vai all'app"
#: src/components/SideNavigation.tsx:105
msgid "Go to boards"
msgstr "Vai alle bacheche"
#: src/components/SideNavigation.tsx:129
msgid "Go to members"
msgstr "Vai ai membri"
#: src/components/SideNavigation.tsx:141
msgid "Go to settings"
msgstr "Vai alle impostazioni"
#: src/components/SideNavigation.tsx:117
msgid "Go to templates"
msgstr "Vai ai modelli"
#: src/views/boards/components/TemplateBoards.tsx:18
msgid "High Priority"
msgstr "Alta priorità"
@@ -1126,7 +1188,7 @@ msgstr "Idee"
msgid "Ideas to improve this page..."
msgstr "Idee per migliorare questa pagina..."
#: src/views/boards/index.tsx:39
#: src/views/boards/index.tsx:50
msgid "Import"
msgstr "Importa"
@@ -1257,9 +1319,13 @@ msgstr "Kanban reinventato"
msgid "Keep in mind that this action is irreversible."
msgstr "Tieni presente che questa azione è irreversibile."
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
#: src/providers/keyboard-shortcuts.tsx:320
msgid "Keyboard Shortcuts"
msgstr "Scorciatoie da tastiera"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/NewCardForm.tsx:418
#: src/views/card/index.tsx:120
msgid "Labels"
msgstr "Etichette"
@@ -1305,15 +1371,15 @@ msgstr "Legale"
msgid "License"
msgstr "Licenza"
#: src/components/UserMenu.tsx:167
#: src/components/UserMenu.tsx:169
msgid "Light"
msgstr "Chiaro"
#: src/views/public/board/index.tsx:66
#: src/views/public/board/index.tsx:77
msgid "Link copied"
msgstr "Link copiato"
#: src/views/card/index.tsx:111
#: src/views/card/index.tsx:112
msgid "List"
msgstr "Lista"
@@ -1321,7 +1387,7 @@ msgstr "Lista"
msgid "List name"
msgstr "Nome lista"
#: src/views/board/components/Filters.tsx:123
#: src/views/board/components/Filters.tsx:163
msgid "Lists"
msgstr "Liste"
@@ -1329,7 +1395,7 @@ msgstr "Liste"
msgid "Login | kan.bn"
msgstr "Login | kan.bn"
#: src/components/UserMenu.tsx:209
#: src/components/UserMenu.tsx:224
msgid "Logout"
msgstr "Esci"
@@ -1345,7 +1411,7 @@ msgstr "Amato dai team di tutto il mondo"
msgid "Low Priority"
msgstr "Bassa priorità"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "link magico"
@@ -1353,7 +1419,7 @@ msgstr "link magico"
msgid "Make template"
msgstr "Crea template"
#: src/views/card/components/ActivityList.tsx:75
#: src/views/card/components/ActivityList.tsx:72
msgid "marked a checklist item as incomplete"
msgstr "ha segnato un elemento della checklist come incompleto"
@@ -1366,10 +1432,10 @@ msgstr "ha segnato l'elemento <0>{0}</0> della checklist come incompleto"
msgid "Medium Priority"
msgstr "Media priorità"
#: src/components/SideNavigation.tsx:101
#: src/views/board/components/Filters.tsx:107
#: src/views/board/components/NewCardForm.tsx:367
#: src/views/card/index.tsx:128
#: src/components/SideNavigation.tsx:121
#: src/views/board/components/Filters.tsx:147
#: src/views/board/components/NewCardForm.tsx:376
#: src/views/card/index.tsx:129
#: src/views/members/index.tsx:178
msgid "Members"
msgstr "Membri"
@@ -1394,7 +1460,7 @@ msgstr "fatturazione mensile"
msgid "moved the card from <0>{0}</0> to<1>{1}</1>"
msgstr "ha spostato la scheda da <0>{0}</0> a<1>{1}</1>"
#: src/views/card/components/ActivityList.tsx:64
#: src/views/card/components/ActivityList.tsx:61
msgid "moved the card to another list"
msgstr "ha spostato la scheda in un'altra lista"
@@ -1404,11 +1470,15 @@ msgstr "ha spostato la scheda in un'altra lista"
msgid "Name"
msgstr "Nome"
#: src/providers/keyboard-shortcuts.tsx:54
msgid "Navigation"
msgstr "Navigazione"
#: src/components/FeedbackModal.tsx:95
msgid "Need help?"
msgstr "Hai bisogno di aiuto?"
#: src/views/boards/index.tsx:50
#: src/views/boards/index.tsx:62
#: src/views/home/components/Features.tsx:73
msgid "New"
msgstr "Nuovo"
@@ -1422,7 +1492,7 @@ msgstr "Nuovo {0}"
msgid "New API key"
msgstr "Nuova chiave API"
#: src/views/board/components/NewCardForm.tsx:293
#: src/views/board/components/NewCardForm.tsx:302
msgid "New card"
msgstr "Nuova carta"
@@ -1439,7 +1509,7 @@ msgid "New label"
msgstr "Nuova etichetta"
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
#: src/views/board/index.tsx:466
msgid "New list"
msgstr "Nuova lista"
@@ -1476,7 +1546,7 @@ msgstr "Prossimi passi"
msgid "No {0}"
msgstr "Nessun {0}"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Nessun metodo di autenticazione è attualmente disponibile"
@@ -1488,11 +1558,19 @@ msgstr "Nessuna bacheca trovata"
msgid "No credit card required"
msgstr "Nessuna carta di credito richiesta"
#: src/views/board/components/Filters.tsx:137
msgid "No dates"
msgstr "Nessuna data"
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "Nessun URL di download disponibile per questo allegato."
#: src/views/board/index.tsx:467
#: src/providers/keyboard-shortcuts.tsx:333
msgid "No keyboard shortcuts registered."
msgstr "Nessuna scorciatoia da tastiera registrata."
#: src/views/board/index.tsx:492
msgid "No lists"
msgstr "Nessuna lista"
@@ -1516,6 +1594,14 @@ msgstr "Una volta eliminato il tuo account, non si può tornare indietro. Questa
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/WorkspaceMenu.tsx:31
msgid "Open command menu"
msgstr "Apri menu dei comandi"
#: src/providers/keyboard-shortcuts.tsx:171
msgid "Open keyboard shortcuts"
msgstr "Apri scorciatoie da tastiera"
#: src/views/pricing/components/FeatureComparisonTable.tsx:150
msgid "Open source"
msgstr "Open source"
@@ -1524,7 +1610,7 @@ msgstr "Open source"
msgid "Open Source Friends"
msgstr "Amici Open Source"
#: src/components/AuthForm.tsx:374
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "o"
@@ -1537,6 +1623,10 @@ msgstr "Organizza e trova le schede rapidamente con potenti strumenti di filtrag
msgid "OSS Friends"
msgstr "Amici OSS"
#: src/views/board/components/Filters.tsx:112
msgid "Overdue"
msgstr "In ritardo"
#: src/views/pricing/components/Pricing.tsx:73
#: src/views/pricing/components/PricingTiers.tsx:70
msgid "Own your data"
@@ -1606,15 +1696,15 @@ msgstr "Piattaforma"
msgid "Please confirm your new password"
msgstr "Conferma la tua nuova password"
#: src/components/AuthForm.tsx:402
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Inserisci un indirizzo email valido"
#: src/components/AuthForm.tsx:388
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Inserisci un nome valido"
#: src/components/AuthForm.tsx:416
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Inserisci una password valida"
@@ -1625,14 +1715,14 @@ msgstr "Seleziona un file da caricare."
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:177
#: src/views/board/components/NewCardForm.tsx:184
#: src/views/board/components/NewListForm.tsx:79
#: src/views/board/components/NewTemplateForm.tsx:61
#: src/views/board/components/NewTemplateForm.tsx:77
#: src/views/board/components/UpdateBoardSlugForm.tsx:73
#: src/views/board/components/VisibilityButton.tsx:54
#: src/views/board/index.tsx:151
#: src/views/board/index.tsx:207
#: src/views/board/index.tsx:174
#: src/views/board/index.tsx:230
#: src/views/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
@@ -1642,13 +1732,15 @@ 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/DueDateSelector.tsx:59
#: 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/NewChecklistForm.tsx:70
#: src/views/card/components/NewChecklistItemForm.tsx:89
#: src/views/card/components/NewCommentForm.tsx:31
#: src/views/card/index.tsx:177
#: src/views/card/index.tsx:187
#: src/views/card/index.tsx:200
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:241
@@ -1748,11 +1840,11 @@ msgstr "Rimuovi"
msgid "Remove member"
msgstr "Rimuovi membro"
#: src/views/card/components/ActivityList.tsx:66
#: src/views/card/components/ActivityList.tsx:63
msgid "removed a label from the card"
msgstr "ha rimosso un'etichetta dalla scheda"
#: src/views/card/components/ActivityList.tsx:68
#: src/views/card/components/ActivityList.tsx:65
msgid "removed a member from the card"
msgstr "ha rimosso un membro dalla scheda"
@@ -1761,7 +1853,12 @@ msgstr "ha rimosso un membro dalla scheda"
msgid "removed label <0>{0}</0>"
msgstr "ha rimosso l'etichetta <0>{0}</0>"
#: src/views/card/components/ActivityList.tsx:70
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:241
msgid "removed the due date"
msgstr "ha rimosso la data di scadenza"
#: src/views/card/components/ActivityList.tsx:67
msgid "renamed a checklist"
msgstr "ha rinominato una checklist"
@@ -1864,8 +1961,16 @@ msgstr "Invia feedback"
msgid "Senior"
msgstr "Senior"
#: src/views/card/components/DueDateSelector.tsx:116
msgid "Set due date"
msgstr "Imposta data di scadenza"
#: src/views/card/components/ActivityList.tsx:74
msgid "set the due date"
msgstr "ha impostato la data di scadenza"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:106
#: src/components/SideNavigation.tsx:133
msgid "Settings"
msgstr "Impostazioni"
@@ -1889,6 +1994,10 @@ msgstr "Impostazioni | Integrazioni"
msgid "Settings | Workspace"
msgstr "Impostazioni | Area di lavoro"
#: src/components/UserMenu.tsx:184
msgid "Shortcuts"
msgstr "Scorciatoie"
#: src/views/home/components/Header.tsx:104
msgid "Sign in"
msgstr "Accedi"
@@ -1914,7 +2023,7 @@ msgstr "Registrazione disabilitata"
msgid "Sign up is currently disabled. Please try again later."
msgstr "La registrazione è attualmente disabilitata. Riprova più tardi."
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Registrati con "
@@ -1957,7 +2066,7 @@ msgstr "Operazione riuscita"
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Potenzia la tua area di lavoro per soli $29/mese. Ecco cosa otterrai:"
#: src/components/UserMenu.tsx:180
#: src/components/UserMenu.tsx:195
msgid "Support"
msgstr "Supporto"
@@ -1966,7 +2075,7 @@ msgstr "Supporto"
msgid "Support the development of the project"
msgstr "Sostieni lo sviluppo del progetto"
#: src/components/UserMenu.tsx:139
#: src/components/UserMenu.tsx:141
msgid "System"
msgstr "Sistema"
@@ -1981,8 +2090,8 @@ msgstr "Piano Team"
msgid "Teams"
msgstr "Team"
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
#: src/views/board/index.tsx:386
#: src/views/board/index.tsx:421
msgid "Template"
msgstr "Template"
@@ -2002,7 +2111,7 @@ msgstr "Il nome del template non può superare i 100 caratteri"
msgid "Template name is required"
msgstr "Il nome del template è obbligatorio"
#: src/components/SideNavigation.tsx:96
#: src/components/SideNavigation.tsx:109
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Template"
@@ -2036,7 +2145,7 @@ msgstr "L'alternativa open source <0/> a Trello"
msgid "The visibility of your board has been set to {0}."
msgstr "La visibilità della tua bacheca è stata impostata su {0}."
#: src/components/UserMenu.tsx:126
#: src/components/UserMenu.tsx:128
msgid "Theme"
msgstr "Tema"
@@ -2056,7 +2165,7 @@ msgstr "Questa azione non può essere annullata."
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:156
#: src/views/public/board/index.tsx:168
msgid "This board is private or does not exist"
msgstr "Questa bacheca è privata o non esiste"
@@ -2130,7 +2239,11 @@ msgstr "Impossibile aggiungere l'elemento della checklist"
msgid "Unable to add comment"
msgstr "Impossibile aggiungere il commento"
#: src/views/board/components/NewCardForm.tsx:174
#: src/views/card/index.tsx:199
msgid "Unable to add label"
msgstr ""
#: src/views/board/components/NewCardForm.tsx:181
msgid "Unable to create card"
msgstr "Impossibile creare la scheda"
@@ -2187,8 +2300,8 @@ msgstr "Impossibile aggiornare l'URL della bacheca"
msgid "Unable to update board visibility"
msgstr "Impossibile aggiornare la visibilità della bacheca"
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
#: src/views/board/index.tsx:229
#: src/views/card/index.tsx:186
msgid "Unable to update card"
msgstr "Impossibile aggiornare la scheda"
@@ -2204,11 +2317,15 @@ msgstr "Impossibile aggiornare l'elemento della checklist"
msgid "Unable to update comment"
msgstr "Impossibile aggiornare il commento"
#: src/views/card/components/DueDateSelector.tsx:58
msgid "Unable to update due date"
msgstr "Impossibile aggiornare la data di scadenza"
#: src/views/card/components/LabelSelector.tsx:71
msgid "Unable to update labels"
msgstr "Impossibile aggiornare le etichette"
#: src/views/board/index.tsx:150
#: src/views/board/index.tsx:173
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Impossibile aggiornare la lista"
@@ -2264,6 +2381,11 @@ msgstr "Membri illimitati"
msgid "Unlimited workspaces"
msgstr "Aree di lavoro illimitate"
#: src/pages/unsubscribe/index.tsx:68
#: src/pages/unsubscribe/index.tsx:93
msgid "Unsubscribe"
msgstr "Annulla iscrizione"
#: src/views/board/components/UpdateBoardSlugForm.tsx:175
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
@@ -2276,15 +2398,19 @@ msgstr "Aggiorna"
msgid "Update label"
msgstr "Aggiorna etichetta"
#: src/views/card/components/ActivityList.tsx:73
#: src/views/card/components/ActivityList.tsx:70
msgid "updated a checklist item"
msgstr "ha aggiornato un elemento della checklist"
#: src/views/card/components/ActivityList.tsx:63
#: src/views/card/components/ActivityList.tsx:60
msgid "updated the description"
msgstr "ha aggiornato la descrizione"
#: src/views/card/components/ActivityList.tsx:62
#: src/views/card/components/ActivityList.tsx:75
msgid "updated the due date"
msgstr "ha aggiornato la data di scadenza"
#: src/views/card/components/ActivityList.tsx:59
msgid "updated the title"
msgstr "ha aggiornato il titolo"
@@ -2293,7 +2419,7 @@ msgstr "ha aggiornato il titolo"
msgid "updated the title to <0>{0}</0>"
msgstr "ha aggiornato il titolo in <0>{0}</0>"
#: src/components/SideNavigation.tsx:202
#: src/components/SideNavigation.tsx:237
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
@@ -2357,7 +2483,7 @@ msgstr "Visualizza e gestisci la tua fatturazione e abbonamento."
msgid "View docs"
msgstr "Visualizza documenti"
#: src/views/public/board/index.tsx:129
#: src/views/public/board/index.tsx:141
msgid "View only"
msgstr "Solo visualizzazione"
@@ -2365,7 +2491,7 @@ msgstr "Solo visualizzazione"
msgid "View our roadmap."
msgstr "Visualizza la nostra roadmap."
#: src/views/public/board/index.tsx:159
#: src/views/public/board/index.tsx:171
msgid "View workspace"
msgstr "Visualizza spazio di lavoro"
@@ -2377,6 +2503,10 @@ msgstr "Visibilità"
msgid "We are using the <0>AGPL-3.0 license</0>."
msgstr "Utilizziamo la <0>licenza AGPL-3.0</0>."
#: src/pages/unsubscribe/index.tsx:63
msgid "We couldn't update your preferences. Please try again."
msgstr "Non siamo riusciti ad aggiornare le tue preferenze. Riprova."
#: src/views/home/components/Features.tsx:152
msgid "We're just getting started. "
msgstr "Siamo solo all'inizio. "
@@ -2406,10 +2536,10 @@ msgid "Why make an open source Trello?"
msgstr "Perché creare un Trello open source?"
#: src/components/SettingsLayout.tsx:39
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/board/index.tsx:386
#: src/views/boards/index.tsx:33
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:101
#: src/views/public/board/index.tsx:113
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Spazio di lavoro"
@@ -2515,6 +2645,10 @@ msgstr "Hai effettuato l'accesso con successo."
msgid "You have been signed up successfully."
msgstr "Ti sei registrato con successo."
#: src/pages/unsubscribe/index.tsx:98
msgid "You have been unsubscribed!"
msgstr "La tua iscrizione è stata annullata!"
#: src/views/members/components/InviteMemberForm.tsx:326
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!"
@@ -2559,6 +2693,10 @@ msgstr "Il tuo account Trello è stato disconnesso."
msgid "Your Trello account is connected."
msgstr "Il tuo account Trello è connesso."
#: src/pages/unsubscribe/index.tsx:33
msgid "Your unsubscribe link is missing a token. Please open the latest email and try again."
msgstr "Il tuo link per annullare l'iscrizione non contiene un token. Apri l'email più recente e riprova."
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:49
msgid "Your workspace description has been updated."
msgstr "La descrizione della tua area di lavoro è stata aggiornata."

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@ msgid ", or see our"
msgstr ", of bekijk onze"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#: src/views/boards/index.tsx:27
#: src/views/boards/index.tsx:38
msgid "{0}"
msgstr "{0}"
@@ -30,18 +30,18 @@ msgstr "{0}"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:218
#: src/views/boards/index.tsx:33
#: src/views/card/index.tsx:272
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:443
#: src/views/board/components/NewCardForm.tsx:452
msgid "{0} labels"
msgstr "{0} labels"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:389
#: src/views/board/index.tsx:412
msgid "{0} not found"
msgstr "{0} niet gevonden"
@@ -79,8 +79,12 @@ msgstr "Account"
msgid "Account deleted"
msgstr "Account verwijderd"
#: src/views/card/index.tsx:308
#: src/views/public/board/CardModal.tsx:157
#: src/providers/keyboard-shortcuts.tsx:55
msgid "Actions"
msgstr "Acties"
#: src/views/card/index.tsx:386
#: src/views/public/board/CardModal.tsx:158
msgid "Activity"
msgstr "Activiteit"
@@ -127,19 +131,19 @@ msgstr "Label toevoegen"
msgid "Add member"
msgstr "Lid toevoegen"
#: src/views/card/components/ActivityList.tsx:69
#: src/views/card/components/ActivityList.tsx:66
msgid "added a checklist"
msgstr "heeft een checklist toegevoegd"
#: src/views/card/components/ActivityList.tsx:72
#: src/views/card/components/ActivityList.tsx:69
msgid "added a checklist item"
msgstr "heeft een checklistitem toegevoegd"
#: src/views/card/components/ActivityList.tsx:65
#: src/views/card/components/ActivityList.tsx:62
msgid "added a label to the card"
msgstr "heeft een label aan de kaart toegevoegd"
#: src/views/card/components/ActivityList.tsx:67
#: src/views/card/components/ActivityList.tsx:64
msgid "added a member to the card"
msgstr "heeft een lid aan de kaart toegevoegd"
@@ -308,9 +312,9 @@ msgstr "Factureringsportaal"
msgid "Blog Post"
msgstr "Blogbericht"
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:218
#: src/views/public/board/index.tsx:101
#: src/views/board/index.tsx:386
#: src/views/card/index.tsx:272
#: src/views/public/board/index.tsx:113
msgid "Board"
msgstr "Bord"
@@ -327,7 +331,7 @@ msgstr "Bordnaam mag niet langer zijn dan 100 tekens"
msgid "Board name is required"
msgstr "Bordnaam is verplicht"
#: src/views/public/board/index.tsx:153
#: src/views/public/board/index.tsx:165
msgid "Board not found"
msgstr "Bord niet gevonden"
@@ -343,7 +347,7 @@ msgstr "Board URL kan alleen letters, cijfers en koppeltekens bevatten"
msgid "Board URL cannot exceed 60 characters"
msgstr "Board URL mag niet langer zijn dan 60 tekens"
#: src/views/public/board/index.tsx:68
#: src/views/public/board/index.tsx:79
msgid "Board URL copied to clipboard"
msgstr "Board URL gekopieerd naar klembord"
@@ -360,7 +364,7 @@ msgstr "Bordzichtbaarheid"
msgid "Board visibility updated"
msgstr "Zichtbaarheid van bord bijgewerkt"
#: src/components/SideNavigation.tsx:91
#: src/components/SideNavigation.tsx:97
msgid "Boards"
msgstr "Borden"
@@ -391,15 +395,16 @@ msgstr "Bugrapport"
msgid "Cancel"
msgstr "Annuleren"
#: src/views/card/index.tsx:218
#: src/views/card/index.tsx:272
msgid "Card"
msgstr "Kaart"
#: src/views/card/index.tsx:264
#: src/views/card/index.tsx:306
#: src/views/card/index.tsx:342
msgid "Card not found"
msgstr "Kaart niet gevonden"
#: src/views/board/components/NewCardForm.tsx:310
#: src/views/board/components/NewCardForm.tsx:319
msgid "Card title"
msgstr "Kaarttitel"
@@ -414,6 +419,11 @@ msgstr "Wachtwoord wijzigen"
msgid "Change your language preferences."
msgstr "Wijzig je taalvoorkeuren."
#: src/views/card/components/ActivityList.tsx:220
#: src/views/card/components/ActivityList.tsx:234
msgid "changed the due date to <0>{formattedDate}</0>"
msgstr "heeft de vervaldatum gewijzigd naar <0>{formattedDate}</0>"
#: src/views/oss-friends/index.tsx:61
msgid "Check out some of our favorite open source projects."
msgstr "Bekijk enkele van onze favoriete open source projecten."
@@ -432,7 +442,7 @@ msgstr "Naam checklist"
msgid "Checklists"
msgstr "Checklists"
#: src/views/board/components/Filters.tsx:180
#: src/views/board/components/Filters.tsx:227
msgid "Clear filters"
msgstr "Filters wissen"
@@ -484,7 +494,7 @@ msgstr "Voltooid"
msgid "Complete control and ownership:"
msgstr "Volledige controle en eigendom:"
#: src/views/card/components/ActivityList.tsx:74
#: src/views/card/components/ActivityList.tsx:71
msgid "completed a checklist item"
msgstr "heeft een checklistitem voltooid"
@@ -493,6 +503,10 @@ msgstr "heeft een checklistitem voltooid"
msgid "completed checklist item <0>{0}</0>"
msgstr "heeft checklistitem <0>{0}</0> voltooid"
#: src/pages/unsubscribe/index.tsx:81
msgid "Confirm your email preferences:"
msgstr "Bevestig je e-mailvoorkeuren:"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:150
msgid "Confirm your new password"
msgstr "Bevestig je nieuwe wachtwoord"
@@ -523,12 +537,12 @@ msgstr "Neem contact op"
msgid "Content Creation"
msgstr "Content creatie"
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Continue with "
msgstr "Doorgaan met "
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
#: src/components/AuthForm.tsx:351
#: src/components/AuthForm.tsx:355
msgid "Continue with {0}"
msgstr "Doorgaan met {0}"
@@ -546,7 +560,7 @@ msgid "Create {0}"
msgstr "{0} maken"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewCardForm.tsx:516
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Maak nog een"
@@ -555,7 +569,7 @@ msgstr "Maak nog een"
msgid "Create API key"
msgstr "API-sleutel aanmaken"
#: src/views/board/components/NewCardForm.tsx:479
#: src/views/board/components/NewCardForm.tsx:526
msgid "Create card"
msgstr "Maak kaart"
@@ -577,6 +591,7 @@ msgstr "Lijst maken"
#. placeholder {0}: isTemplate ? "template" : "board"
#: src/views/boards/components/BoardsList.tsx:45
#: src/views/boards/index.tsx:26
msgid "Create new {0}"
msgstr "Nieuwe {0} maken"
@@ -584,12 +599,13 @@ msgstr "Nieuwe {0} maken"
msgid "Create new key"
msgstr "Nieuwe sleutel aanmaken"
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/board/components/NewCardForm.tsx:414
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Maak nieuw label"
#: src/views/board/index.tsx:478
#: src/views/board/index.tsx:65
#: src/views/board/index.tsx:503
msgid "Create new list"
msgstr "Maak nieuwe lijst"
@@ -598,11 +614,11 @@ msgid "Create template"
msgstr "Sjabloon maken"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:149
#: src/components/WorkspaceMenu.tsx:153
msgid "Create workspace"
msgstr "Werkruimte maken"
#: src/views/card/components/ActivityList.tsx:61
#: src/views/card/components/ActivityList.tsx:58
msgid "created the card"
msgstr "heeft de kaart aangemaakt"
@@ -648,7 +664,7 @@ msgstr "Aangepaste werkruimte-URL"
msgid "Customer Support"
msgstr "Klantenservice"
#: src/components/UserMenu.tsx:153
#: src/components/UserMenu.tsx:155
msgid "Dark"
msgstr "Donker"
@@ -697,11 +713,11 @@ msgstr "Sjabloon verwijderen"
msgid "Delete workspace"
msgstr "Werkruimte verwijderen"
#: src/views/card/components/ActivityList.tsx:71
#: src/views/card/components/ActivityList.tsx:68
msgid "deleted a checklist"
msgstr "heeft een checklist verwijderd"
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:73
msgid "deleted a checklist item"
msgstr "heeft een checklistitem verwijderd"
@@ -747,6 +763,10 @@ msgstr "Weergavenaam bijgewerkt"
msgid "Do you offer a free plan?"
msgstr "Bieden jullie een gratis abonnement aan?"
#: src/pages/unsubscribe/index.tsx:78
msgid "Do you want to unsubscribe?"
msgstr "Wil je je uitschrijven?"
#: src/components/FeedbackModal.tsx:109
msgid "docs"
msgstr "docs"
@@ -755,7 +775,7 @@ msgstr "docs"
msgid "Docs"
msgstr "Docs"
#: src/components/UserMenu.tsx:191
#: src/components/UserMenu.tsx:206
#: src/views/boards/components/TemplateBoards.tsx:24
#: src/views/home/components/Footer.tsx:78
#: src/views/home/components/Header.tsx:36
@@ -777,6 +797,28 @@ msgstr "Klaar"
msgid "Download failed"
msgstr "Download mislukt"
#: src/views/board/components/Filters.tsx:171
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/card/index.tsx:138
msgid "Due date"
msgstr "Vervaldatum"
#: src/views/board/components/Filters.tsx:132
msgid "Due next month"
msgstr "Vervalt volgende maand"
#: src/views/board/components/Filters.tsx:127
msgid "Due next week"
msgstr "Vervalt volgende week"
#: src/views/board/components/Filters.tsx:117
msgid "Due today"
msgstr "Vervalt vandaag"
#: src/views/board/components/Filters.tsx:122
msgid "Due tomorrow"
msgstr "Vervalt morgen"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
@@ -798,7 +840,7 @@ msgstr "Werkruimte-URL bewerken"
msgid "Editing"
msgstr "Bewerken"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "e-mail"
@@ -818,11 +860,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:398
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Voer je e-mailadres in"
#: src/components/AuthForm.tsx:384
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Voer je naam in"
@@ -830,7 +872,7 @@ msgstr "Voer je naam in"
msgid "Enter your new password"
msgstr "Voer je nieuwe wachtwoord in"
#: src/components/AuthForm.tsx:411
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Voer je wachtwoord in"
@@ -979,7 +1021,7 @@ msgid "Features"
msgstr "Functies"
#: src/components/FeedbackModal.tsx:59
#: src/components/UserMenu.tsx:199
#: src/components/UserMenu.tsx:214
msgid "Feedback"
msgstr "Feedback"
@@ -987,7 +1029,7 @@ msgstr "Feedback"
msgid "Feedback sent"
msgstr "Feedback verzonden"
#: src/views/board/components/Filters.tsx:174
#: src/views/board/components/Filters.tsx:221
msgid "Filter"
msgstr "Filter"
@@ -1024,6 +1066,10 @@ msgstr "Fulltime"
msgid "Fun"
msgstr "Leuk"
#: src/providers/keyboard-shortcuts.tsx:53
msgid "General"
msgstr "Algemeen"
#: src/views/auth/signup/index.tsx:69
#: src/views/home/components/Cta.tsx:61
#: src/views/home/components/Header.tsx:106
@@ -1043,7 +1089,7 @@ msgstr "Aan de slag"
msgid "Get started by creating a new {0}"
msgstr "Begin door een nieuwe {0} te maken"
#: src/views/board/index.tsx:470
#: src/views/board/index.tsx:495
msgid "Get started by creating a new list"
msgstr "Begin door een nieuwe lijst te maken"
@@ -1077,6 +1123,22 @@ msgstr "Naar startpagina"
msgid "Go to app"
msgstr "Naar de app"
#: src/components/SideNavigation.tsx:105
msgid "Go to boards"
msgstr "Ga naar borden"
#: src/components/SideNavigation.tsx:129
msgid "Go to members"
msgstr "Ga naar leden"
#: src/components/SideNavigation.tsx:141
msgid "Go to settings"
msgstr "Ga naar instellingen"
#: src/components/SideNavigation.tsx:117
msgid "Go to templates"
msgstr "Ga naar sjablonen"
#: src/views/boards/components/TemplateBoards.tsx:18
msgid "High Priority"
msgstr "Hoge prioriteit"
@@ -1126,7 +1188,7 @@ msgstr "Ideeën"
msgid "Ideas to improve this page..."
msgstr "Ideeën om deze pagina te verbeteren..."
#: src/views/boards/index.tsx:39
#: src/views/boards/index.tsx:50
msgid "Import"
msgstr "Importeren"
@@ -1257,9 +1319,13 @@ msgstr "Kanban opnieuw uitgevonden"
msgid "Keep in mind that this action is irreversible."
msgstr "Houd er rekening mee dat deze actie onomkeerbaar is."
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
#: src/providers/keyboard-shortcuts.tsx:320
msgid "Keyboard Shortcuts"
msgstr "Sneltoetsen"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/NewCardForm.tsx:418
#: src/views/card/index.tsx:120
msgid "Labels"
msgstr "Labels"
@@ -1305,15 +1371,15 @@ msgstr "Juridisch"
msgid "License"
msgstr "Licentie"
#: src/components/UserMenu.tsx:167
#: src/components/UserMenu.tsx:169
msgid "Light"
msgstr "Licht"
#: src/views/public/board/index.tsx:66
#: src/views/public/board/index.tsx:77
msgid "Link copied"
msgstr "Link gekopieerd"
#: src/views/card/index.tsx:111
#: src/views/card/index.tsx:112
msgid "List"
msgstr "Lijst"
@@ -1321,7 +1387,7 @@ msgstr "Lijst"
msgid "List name"
msgstr "Lijstnaam"
#: src/views/board/components/Filters.tsx:123
#: src/views/board/components/Filters.tsx:163
msgid "Lists"
msgstr "Lijsten"
@@ -1329,7 +1395,7 @@ msgstr "Lijsten"
msgid "Login | kan.bn"
msgstr "Login | kan.bn"
#: src/components/UserMenu.tsx:209
#: src/components/UserMenu.tsx:224
msgid "Logout"
msgstr "Uitloggen"
@@ -1345,7 +1411,7 @@ msgstr "Geliefd door teams wereldwijd"
msgid "Low Priority"
msgstr "Lage prioriteit"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "magische link"
@@ -1353,7 +1419,7 @@ msgstr "magische link"
msgid "Make template"
msgstr "Sjabloon maken"
#: src/views/card/components/ActivityList.tsx:75
#: src/views/card/components/ActivityList.tsx:72
msgid "marked a checklist item as incomplete"
msgstr "heeft een checklistitem als onvoltooid gemarkeerd"
@@ -1366,10 +1432,10 @@ msgstr "heeft checklistitem <0>{0}</0> als onvoltooid gemarkeerd"
msgid "Medium Priority"
msgstr "Gemiddelde prioriteit"
#: src/components/SideNavigation.tsx:101
#: src/views/board/components/Filters.tsx:107
#: src/views/board/components/NewCardForm.tsx:367
#: src/views/card/index.tsx:128
#: src/components/SideNavigation.tsx:121
#: src/views/board/components/Filters.tsx:147
#: src/views/board/components/NewCardForm.tsx:376
#: src/views/card/index.tsx:129
#: src/views/members/index.tsx:178
msgid "Members"
msgstr "Leden"
@@ -1394,7 +1460,7 @@ msgstr "maandelijkse facturering"
msgid "moved the card from <0>{0}</0> to<1>{1}</1>"
msgstr "heeft de kaart verplaatst van <0>{0}</0> naar<1>{1}</1>"
#: src/views/card/components/ActivityList.tsx:64
#: src/views/card/components/ActivityList.tsx:61
msgid "moved the card to another list"
msgstr "heeft de kaart naar een andere lijst verplaatst"
@@ -1404,11 +1470,15 @@ msgstr "heeft de kaart naar een andere lijst verplaatst"
msgid "Name"
msgstr "Naam"
#: src/providers/keyboard-shortcuts.tsx:54
msgid "Navigation"
msgstr "Navigatie"
#: src/components/FeedbackModal.tsx:95
msgid "Need help?"
msgstr "Hulp nodig?"
#: src/views/boards/index.tsx:50
#: src/views/boards/index.tsx:62
#: src/views/home/components/Features.tsx:73
msgid "New"
msgstr "Nieuw"
@@ -1422,7 +1492,7 @@ msgstr "Nieuwe {0}"
msgid "New API key"
msgstr "Nieuwe API-sleutel"
#: src/views/board/components/NewCardForm.tsx:293
#: src/views/board/components/NewCardForm.tsx:302
msgid "New card"
msgstr "Nieuwe kaart"
@@ -1439,7 +1509,7 @@ msgid "New label"
msgstr "Nieuw label"
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
#: src/views/board/index.tsx:466
msgid "New list"
msgstr "Nieuwe lijst"
@@ -1476,7 +1546,7 @@ msgstr "Volgende stappen"
msgid "No {0}"
msgstr "Geen {0}"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Er zijn momenteel geen authenticatiemethoden beschikbaar"
@@ -1488,11 +1558,19 @@ msgstr "Geen borden gevonden"
msgid "No credit card required"
msgstr "Geen creditcard vereist"
#: src/views/board/components/Filters.tsx:137
msgid "No dates"
msgstr "Geen datums"
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "Geen download-URL beschikbaar voor deze bijlage."
#: src/views/board/index.tsx:467
#: src/providers/keyboard-shortcuts.tsx:333
msgid "No keyboard shortcuts registered."
msgstr "Geen sneltoetsen geregistreerd."
#: src/views/board/index.tsx:492
msgid "No lists"
msgstr "Geen lijsten"
@@ -1516,6 +1594,14 @@ msgstr "Zodra je je account verwijdert, is er geen weg terug. Deze actie kan nie
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/WorkspaceMenu.tsx:31
msgid "Open command menu"
msgstr "Opdrachtmenu openen"
#: src/providers/keyboard-shortcuts.tsx:171
msgid "Open keyboard shortcuts"
msgstr "Sneltoetsen openen"
#: src/views/pricing/components/FeatureComparisonTable.tsx:150
msgid "Open source"
msgstr "Open source"
@@ -1524,7 +1610,7 @@ msgstr "Open source"
msgid "Open Source Friends"
msgstr "Open Source Vrienden"
#: src/components/AuthForm.tsx:374
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "of"
@@ -1537,6 +1623,10 @@ msgstr "Organiseer en vind kaarten snel met krachtige filtertools."
msgid "OSS Friends"
msgstr "OSS Vrienden"
#: src/views/board/components/Filters.tsx:112
msgid "Overdue"
msgstr "Achterstallig"
#: src/views/pricing/components/Pricing.tsx:73
#: src/views/pricing/components/PricingTiers.tsx:70
msgid "Own your data"
@@ -1606,15 +1696,15 @@ msgstr "Platform"
msgid "Please confirm your new password"
msgstr "Bevestig je nieuwe wachtwoord"
#: src/components/AuthForm.tsx:402
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Voer een geldig e-mailadres in"
#: src/components/AuthForm.tsx:388
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Voer een geldige naam in"
#: src/components/AuthForm.tsx:416
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Voer een geldig wachtwoord in"
@@ -1625,14 +1715,14 @@ msgstr "Selecteer een bestand om te uploaden."
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:177
#: src/views/board/components/NewCardForm.tsx:184
#: src/views/board/components/NewListForm.tsx:79
#: src/views/board/components/NewTemplateForm.tsx:61
#: src/views/board/components/NewTemplateForm.tsx:77
#: src/views/board/components/UpdateBoardSlugForm.tsx:73
#: src/views/board/components/VisibilityButton.tsx:54
#: src/views/board/index.tsx:151
#: src/views/board/index.tsx:207
#: src/views/board/index.tsx:174
#: src/views/board/index.tsx:230
#: src/views/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
@@ -1642,13 +1732,15 @@ 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/DueDateSelector.tsx:59
#: 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/NewChecklistForm.tsx:70
#: src/views/card/components/NewChecklistItemForm.tsx:89
#: src/views/card/components/NewCommentForm.tsx:31
#: src/views/card/index.tsx:177
#: src/views/card/index.tsx:187
#: src/views/card/index.tsx:200
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:241
@@ -1748,11 +1840,11 @@ msgstr "Verwijderen"
msgid "Remove member"
msgstr "Lid verwijderen"
#: src/views/card/components/ActivityList.tsx:66
#: src/views/card/components/ActivityList.tsx:63
msgid "removed a label from the card"
msgstr "heeft een label van de kaart verwijderd"
#: src/views/card/components/ActivityList.tsx:68
#: src/views/card/components/ActivityList.tsx:65
msgid "removed a member from the card"
msgstr "heeft een lid van de kaart verwijderd"
@@ -1761,7 +1853,12 @@ msgstr "heeft een lid van de kaart verwijderd"
msgid "removed label <0>{0}</0>"
msgstr "heeft label <0>{0}</0> verwijderd"
#: src/views/card/components/ActivityList.tsx:70
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:241
msgid "removed the due date"
msgstr "heeft de vervaldatum verwijderd"
#: src/views/card/components/ActivityList.tsx:67
msgid "renamed a checklist"
msgstr "heeft een checklist hernoemd"
@@ -1864,8 +1961,16 @@ msgstr "Feedback versturen"
msgid "Senior"
msgstr "Senior"
#: src/views/card/components/DueDateSelector.tsx:116
msgid "Set due date"
msgstr "Vervaldatum instellen"
#: src/views/card/components/ActivityList.tsx:74
msgid "set the due date"
msgstr "heeft de vervaldatum ingesteld"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:106
#: src/components/SideNavigation.tsx:133
msgid "Settings"
msgstr "Instellingen"
@@ -1889,6 +1994,10 @@ msgstr "Instellingen | Integraties"
msgid "Settings | Workspace"
msgstr "Instellingen | Werkruimte"
#: src/components/UserMenu.tsx:184
msgid "Shortcuts"
msgstr "Snelkoppelingen"
#: src/views/home/components/Header.tsx:104
msgid "Sign in"
msgstr "Inloggen"
@@ -1914,7 +2023,7 @@ msgstr "Registreren uitgeschakeld"
msgid "Sign up is currently disabled. Please try again later."
msgstr "Registreren is momenteel uitgeschakeld. Probeer het later opnieuw."
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Registreren met "
@@ -1957,7 +2066,7 @@ msgstr "Geslaagd"
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Geef je werkruimte een boost voor slechts $29/maand. Dit krijg je:"
#: src/components/UserMenu.tsx:180
#: src/components/UserMenu.tsx:195
msgid "Support"
msgstr "Ondersteuning"
@@ -1966,7 +2075,7 @@ msgstr "Ondersteuning"
msgid "Support the development of the project"
msgstr "Ondersteun de ontwikkeling van het project"
#: src/components/UserMenu.tsx:139
#: src/components/UserMenu.tsx:141
msgid "System"
msgstr "Systeem"
@@ -1981,8 +2090,8 @@ msgstr "Teamplan"
msgid "Teams"
msgstr "Teams"
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
#: src/views/board/index.tsx:386
#: src/views/board/index.tsx:421
msgid "Template"
msgstr "Sjabloon"
@@ -2002,7 +2111,7 @@ msgstr "Sjabloonnaam mag niet langer zijn dan 100 tekens"
msgid "Template name is required"
msgstr "Sjabloonnaam is verplicht"
#: src/components/SideNavigation.tsx:96
#: src/components/SideNavigation.tsx:109
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Sjablonen"
@@ -2036,7 +2145,7 @@ msgstr "Het open source <0/> alternatief voor Trello"
msgid "The visibility of your board has been set to {0}."
msgstr "De zichtbaarheid van je bord is ingesteld op {0}."
#: src/components/UserMenu.tsx:126
#: src/components/UserMenu.tsx:128
msgid "Theme"
msgstr "Thema"
@@ -2056,7 +2165,7 @@ msgstr "Deze actie kan niet ongedaan worden gemaakt."
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:156
#: src/views/public/board/index.tsx:168
msgid "This board is private or does not exist"
msgstr "Dit bord is privé of bestaat niet"
@@ -2130,7 +2239,11 @@ msgstr "Kan checklistitem niet toevoegen"
msgid "Unable to add comment"
msgstr "Kan geen reactie toevoegen"
#: src/views/board/components/NewCardForm.tsx:174
#: src/views/card/index.tsx:199
msgid "Unable to add label"
msgstr ""
#: src/views/board/components/NewCardForm.tsx:181
msgid "Unable to create card"
msgstr "Kan kaart niet aanmaken"
@@ -2187,8 +2300,8 @@ msgstr "Kan board URL niet bijwerken"
msgid "Unable to update board visibility"
msgstr "Kan bordzichtbaarheid niet bijwerken"
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
#: src/views/board/index.tsx:229
#: src/views/card/index.tsx:186
msgid "Unable to update card"
msgstr "Kan kaart niet bijwerken"
@@ -2204,11 +2317,15 @@ msgstr "Kan checklistitem niet bijwerken"
msgid "Unable to update comment"
msgstr "Kan reactie niet bijwerken"
#: src/views/card/components/DueDateSelector.tsx:58
msgid "Unable to update due date"
msgstr "Kan vervaldatum niet bijwerken"
#: src/views/card/components/LabelSelector.tsx:71
msgid "Unable to update labels"
msgstr "Kan labels niet bijwerken"
#: src/views/board/index.tsx:150
#: src/views/board/index.tsx:173
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Kan lijst niet bijwerken"
@@ -2264,6 +2381,11 @@ msgstr "Onbeperkt aantal leden"
msgid "Unlimited workspaces"
msgstr "Onbeperkte werkruimtes"
#: src/pages/unsubscribe/index.tsx:68
#: src/pages/unsubscribe/index.tsx:93
msgid "Unsubscribe"
msgstr "Uitschrijven"
#: src/views/board/components/UpdateBoardSlugForm.tsx:175
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
@@ -2276,15 +2398,19 @@ msgstr "Bijwerken"
msgid "Update label"
msgstr "Label bijwerken"
#: src/views/card/components/ActivityList.tsx:73
#: src/views/card/components/ActivityList.tsx:70
msgid "updated a checklist item"
msgstr "een checklistitem bijgewerkt"
#: src/views/card/components/ActivityList.tsx:63
#: src/views/card/components/ActivityList.tsx:60
msgid "updated the description"
msgstr "beschrijving bijgewerkt"
#: src/views/card/components/ActivityList.tsx:62
#: src/views/card/components/ActivityList.tsx:75
msgid "updated the due date"
msgstr "heeft de vervaldatum bijgewerkt"
#: src/views/card/components/ActivityList.tsx:59
msgid "updated the title"
msgstr "titel bijgewerkt"
@@ -2293,7 +2419,7 @@ msgstr "titel bijgewerkt"
msgid "updated the title to <0>{0}</0>"
msgstr "titel bijgewerkt naar <0>{0}</0>"
#: src/components/SideNavigation.tsx:202
#: src/components/SideNavigation.tsx:237
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
@@ -2357,7 +2483,7 @@ msgstr "Bekijk en beheer je facturering en abonnement."
msgid "View docs"
msgstr "Bekijk documentatie"
#: src/views/public/board/index.tsx:129
#: src/views/public/board/index.tsx:141
msgid "View only"
msgstr "Alleen bekijken"
@@ -2365,7 +2491,7 @@ msgstr "Alleen bekijken"
msgid "View our roadmap."
msgstr "Bekijk onze roadmap."
#: src/views/public/board/index.tsx:159
#: src/views/public/board/index.tsx:171
msgid "View workspace"
msgstr "Werkruimte bekijken"
@@ -2377,6 +2503,10 @@ msgstr "Zichtbaarheid"
msgid "We are using the <0>AGPL-3.0 license</0>."
msgstr "We gebruiken de <0>AGPL-3.0 licentie</0>."
#: src/pages/unsubscribe/index.tsx:63
msgid "We couldn't update your preferences. Please try again."
msgstr "We konden je voorkeuren niet bijwerken. Probeer het opnieuw."
#: src/views/home/components/Features.tsx:152
msgid "We're just getting started. "
msgstr "We zijn nog maar net begonnen. "
@@ -2406,10 +2536,10 @@ msgid "Why make an open source Trello?"
msgstr "Waarom een open source Trello maken?"
#: src/components/SettingsLayout.tsx:39
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/board/index.tsx:386
#: src/views/boards/index.tsx:33
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:101
#: src/views/public/board/index.tsx:113
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Werkruimte"
@@ -2515,6 +2645,10 @@ msgstr "Je bent succesvol ingelogd."
msgid "You have been signed up successfully."
msgstr "Je bent succesvol geregistreerd."
#: src/pages/unsubscribe/index.tsx:98
msgid "You have been unsubscribed!"
msgstr "Je bent uitgeschreven!"
#: src/views/members/components/InviteMemberForm.tsx:326
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!"
@@ -2559,6 +2693,10 @@ msgstr "Je Trello-account is ontkoppeld."
msgid "Your Trello account is connected."
msgstr "Je Trello-account is verbonden."
#: src/pages/unsubscribe/index.tsx:33
msgid "Your unsubscribe link is missing a token. Please open the latest email and try again."
msgstr "Je uitschrijflink mist een token. Open de meest recente e-mail en probeer het opnieuw."
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:49
msgid "Your workspace description has been updated."
msgstr "Je werkruimtebeschrijving is bijgewerkt."

File diff suppressed because one or more lines are too long

View File

@@ -27,7 +27,7 @@ msgid ", or see our"
msgstr ", lub zobacz nasze"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#: src/views/boards/index.tsx:27
#: src/views/boards/index.tsx:38
msgid "{0}"
msgstr "{0}"
@@ -35,18 +35,18 @@ msgstr "{0}"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:218
#: src/views/boards/index.tsx:33
#: src/views/card/index.tsx:272
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:443
#: src/views/board/components/NewCardForm.tsx:452
msgid "{0} labels"
msgstr "{0} etykiety"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:389
#: src/views/board/index.tsx:412
msgid "{0} not found"
msgstr "{0} nie znaleziono"
@@ -84,8 +84,12 @@ msgstr "Konto"
msgid "Account deleted"
msgstr "Konto zostało usunięte"
#: src/views/card/index.tsx:308
#: src/views/public/board/CardModal.tsx:157
#: src/providers/keyboard-shortcuts.tsx:55
msgid "Actions"
msgstr "Działania"
#: src/views/card/index.tsx:386
#: src/views/public/board/CardModal.tsx:158
msgid "Activity"
msgstr "Aktywność"
@@ -132,19 +136,19 @@ msgstr "Dodaj etykietę"
msgid "Add member"
msgstr "Dodaj członka"
#: src/views/card/components/ActivityList.tsx:69
#: src/views/card/components/ActivityList.tsx:66
msgid "added a checklist"
msgstr "dodał listę kontrolną"
#: src/views/card/components/ActivityList.tsx:72
#: src/views/card/components/ActivityList.tsx:69
msgid "added a checklist item"
msgstr "dodał(a) element listy kontrolnej"
#: src/views/card/components/ActivityList.tsx:65
#: src/views/card/components/ActivityList.tsx:62
msgid "added a label to the card"
msgstr "dodał(a) etykietę do karty"
#: src/views/card/components/ActivityList.tsx:67
#: src/views/card/components/ActivityList.tsx:64
msgid "added a member to the card"
msgstr "dodał(a) członka do karty"
@@ -313,9 +317,9 @@ msgstr "Portal rozliczeniowy"
msgid "Blog Post"
msgstr "Post na blogu"
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:218
#: src/views/public/board/index.tsx:101
#: src/views/board/index.tsx:386
#: src/views/card/index.tsx:272
#: src/views/public/board/index.tsx:113
msgid "Board"
msgstr "Tablica"
@@ -332,7 +336,7 @@ msgstr "Nazwa tablicy nie może przekraczać 100 znaków"
msgid "Board name is required"
msgstr "Nazwa tablicy jest wymagana"
#: src/views/public/board/index.tsx:153
#: src/views/public/board/index.tsx:165
msgid "Board not found"
msgstr "Tablica nie została znaleziona"
@@ -348,7 +352,7 @@ msgstr "Adres URL tablicy może zawierać tylko litery, cyfry i myślniki"
msgid "Board URL cannot exceed 60 characters"
msgstr "Adres URL tablicy nie może przekraczać 60 znaków"
#: src/views/public/board/index.tsx:68
#: src/views/public/board/index.tsx:79
msgid "Board URL copied to clipboard"
msgstr "Adres URL tablicy został skopiowany do schowka"
@@ -365,7 +369,7 @@ msgstr "Widoczność tablicy"
msgid "Board visibility updated"
msgstr "Widoczność tablicy została zaktualizowana"
#: src/components/SideNavigation.tsx:91
#: src/components/SideNavigation.tsx:97
msgid "Boards"
msgstr "Tablice"
@@ -396,15 +400,16 @@ msgstr "Zgłoszenie błędu"
msgid "Cancel"
msgstr "Anuluj"
#: src/views/card/index.tsx:218
#: src/views/card/index.tsx:272
msgid "Card"
msgstr "Karta"
#: src/views/card/index.tsx:264
#: src/views/card/index.tsx:306
#: src/views/card/index.tsx:342
msgid "Card not found"
msgstr "Nie można było znaleźć karty"
#: src/views/board/components/NewCardForm.tsx:310
#: src/views/board/components/NewCardForm.tsx:319
msgid "Card title"
msgstr "Tytuł karty"
@@ -419,6 +424,11 @@ msgstr "Zmień hasło"
msgid "Change your language preferences."
msgstr "Zmień swoje preferencje językowe."
#: src/views/card/components/ActivityList.tsx:220
#: src/views/card/components/ActivityList.tsx:234
msgid "changed the due date to <0>{formattedDate}</0>"
msgstr "zmieniono termin na <0>{formattedDate}</0>"
#: src/views/oss-friends/index.tsx:61
msgid "Check out some of our favorite open source projects."
msgstr "Sprawdź niektóre z naszych ulubionych projektów otwartoźródłowych."
@@ -437,7 +447,7 @@ msgstr "Nazwa listy kontrolnej"
msgid "Checklists"
msgstr "Listy kontrolne"
#: src/views/board/components/Filters.tsx:180
#: src/views/board/components/Filters.tsx:227
msgid "Clear filters"
msgstr "Wyczyść filtry"
@@ -489,7 +499,7 @@ msgstr "Zakończ"
msgid "Complete control and ownership:"
msgstr "Pełna kontrola i własność:"
#: src/views/card/components/ActivityList.tsx:74
#: src/views/card/components/ActivityList.tsx:71
msgid "completed a checklist item"
msgstr "ukończono element listy kontrolnej"
@@ -498,6 +508,10 @@ msgstr "ukończono element listy kontrolnej"
msgid "completed checklist item <0>{0}</0>"
msgstr "ukończono element listy kontrolnej <0>{0}</0>"
#: src/pages/unsubscribe/index.tsx:81
msgid "Confirm your email preferences:"
msgstr "Potwierdź swoje preferencje dotyczące e-maili:"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:150
msgid "Confirm your new password"
msgstr "Potwierdź swoje nowe hasło"
@@ -528,12 +542,12 @@ msgstr "Skontaktuj się z nami"
msgid "Content Creation"
msgstr "Tworzenie treści"
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Continue with "
msgstr "Kontynuuj z "
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
#: src/components/AuthForm.tsx:351
#: src/components/AuthForm.tsx:355
msgid "Continue with {0}"
msgstr "Kontynuuj z {0}"
@@ -551,7 +565,7 @@ msgid "Create {0}"
msgstr "Utwórz {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewCardForm.tsx:516
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Utwórz kolejny"
@@ -560,7 +574,7 @@ msgstr "Utwórz kolejny"
msgid "Create API key"
msgstr "Utwórz klucz API"
#: src/views/board/components/NewCardForm.tsx:479
#: src/views/board/components/NewCardForm.tsx:526
msgid "Create card"
msgstr "Utwórz kartę"
@@ -582,6 +596,7 @@ msgstr "Utwórz listę"
#. placeholder {0}: isTemplate ? "template" : "board"
#: src/views/boards/components/BoardsList.tsx:45
#: src/views/boards/index.tsx:26
msgid "Create new {0}"
msgstr "Utwórz nowy {0}"
@@ -589,12 +604,13 @@ msgstr "Utwórz nowy {0}"
msgid "Create new key"
msgstr "Utwórz nowy klucz"
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/board/components/NewCardForm.tsx:414
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Utwórz nową etykietę"
#: src/views/board/index.tsx:478
#: src/views/board/index.tsx:65
#: src/views/board/index.tsx:503
msgid "Create new list"
msgstr "Utwórz nową listę"
@@ -603,11 +619,11 @@ msgid "Create template"
msgstr "Utwórz szablon"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:149
#: src/components/WorkspaceMenu.tsx:153
msgid "Create workspace"
msgstr "Utwórz przestrzeń roboczą"
#: src/views/card/components/ActivityList.tsx:61
#: src/views/card/components/ActivityList.tsx:58
msgid "created the card"
msgstr "utworzył(a) kartę"
@@ -653,7 +669,7 @@ msgstr "Niestandardowy adres URL przestrzeni roboczej"
msgid "Customer Support"
msgstr "Obsługa klienta"
#: src/components/UserMenu.tsx:153
#: src/components/UserMenu.tsx:155
msgid "Dark"
msgstr "Ciemny"
@@ -702,11 +718,11 @@ msgstr "Usuń szablon"
msgid "Delete workspace"
msgstr "Usuń przestrzeń roboczą"
#: src/views/card/components/ActivityList.tsx:71
#: src/views/card/components/ActivityList.tsx:68
msgid "deleted a checklist"
msgstr "usunięto listę kontrolną"
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:73
msgid "deleted a checklist item"
msgstr "usunięto element listy kontrolnej"
@@ -752,6 +768,10 @@ msgstr "Zaktualizowano wyświetlaną nazwę"
msgid "Do you offer a free plan?"
msgstr "Czy oferujecie darmowy plan?"
#: src/pages/unsubscribe/index.tsx:78
msgid "Do you want to unsubscribe?"
msgstr "Czy chcesz się wypisać?"
#: src/components/FeedbackModal.tsx:109
msgid "docs"
msgstr "dokumentacja"
@@ -760,7 +780,7 @@ msgstr "dokumentacja"
msgid "Docs"
msgstr "Dokumentacja"
#: src/components/UserMenu.tsx:191
#: src/components/UserMenu.tsx:206
#: src/views/boards/components/TemplateBoards.tsx:24
#: src/views/home/components/Footer.tsx:78
#: src/views/home/components/Header.tsx:36
@@ -782,6 +802,28 @@ msgstr "Gotowe"
msgid "Download failed"
msgstr "Pobieranie nie powiodło się"
#: src/views/board/components/Filters.tsx:171
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/card/index.tsx:138
msgid "Due date"
msgstr "Termin"
#: src/views/board/components/Filters.tsx:132
msgid "Due next month"
msgstr "Termin w przyszłym miesiącu"
#: src/views/board/components/Filters.tsx:127
msgid "Due next week"
msgstr "Termin w przyszłym tygodniu"
#: src/views/board/components/Filters.tsx:117
msgid "Due today"
msgstr "Termin na dziś"
#: src/views/board/components/Filters.tsx:122
msgid "Due tomorrow"
msgstr "Termin na jutro"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
@@ -803,7 +845,7 @@ msgstr "Edytuj URL przestrzeni roboczej"
msgid "Editing"
msgstr "Edycja"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "email"
@@ -823,11 +865,11 @@ msgstr "Wprowadź swoje obecne hasło"
msgid "Enter your current password and choose a new secure password."
msgstr "Wprowadź swoje obecne hasło i wybierz nowe, bezpieczne hasło."
#: src/components/AuthForm.tsx:398
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Wprowadź swój adres e-mail"
#: src/components/AuthForm.tsx:384
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Wprowadź swoje imię"
@@ -835,7 +877,7 @@ msgstr "Wprowadź swoje imię"
msgid "Enter your new password"
msgstr "Wprowadź swoje nowe hasło"
#: src/components/AuthForm.tsx:411
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Wprowadź swoje hasło"
@@ -984,7 +1026,7 @@ msgid "Features"
msgstr "Funkcje"
#: src/components/FeedbackModal.tsx:59
#: src/components/UserMenu.tsx:199
#: src/components/UserMenu.tsx:214
msgid "Feedback"
msgstr "Opinie"
@@ -992,7 +1034,7 @@ msgstr "Opinie"
msgid "Feedback sent"
msgstr "Opinia wysłana"
#: src/views/board/components/Filters.tsx:174
#: src/views/board/components/Filters.tsx:221
msgid "Filter"
msgstr "Filtr"
@@ -1029,6 +1071,10 @@ msgstr "Pełny etat"
msgid "Fun"
msgstr "Zabawa"
#: src/providers/keyboard-shortcuts.tsx:53
msgid "General"
msgstr "Ogólne"
#: src/views/auth/signup/index.tsx:69
#: src/views/home/components/Cta.tsx:61
#: src/views/home/components/Header.tsx:106
@@ -1048,7 +1094,7 @@ msgstr "Rozpocznij"
msgid "Get started by creating a new {0}"
msgstr "Rozpocznij, tworząc nowy {0}"
#: src/views/board/index.tsx:470
#: src/views/board/index.tsx:495
msgid "Get started by creating a new list"
msgstr "Rozpocznij, tworząc nową listę"
@@ -1082,6 +1128,22 @@ msgstr "Wróć do strony głównej"
msgid "Go to app"
msgstr "Przejdź do aplikacji"
#: src/components/SideNavigation.tsx:105
msgid "Go to boards"
msgstr "Przejdź do tablic"
#: src/components/SideNavigation.tsx:129
msgid "Go to members"
msgstr "Przejdź do członków"
#: src/components/SideNavigation.tsx:141
msgid "Go to settings"
msgstr "Przejdź do ustawień"
#: src/components/SideNavigation.tsx:117
msgid "Go to templates"
msgstr "Przejdź do szablonów"
#: src/views/boards/components/TemplateBoards.tsx:18
msgid "High Priority"
msgstr "Wysoki priorytet"
@@ -1131,7 +1193,7 @@ msgstr "Pomysły"
msgid "Ideas to improve this page..."
msgstr "Pomysły na ulepszenie tej strony..."
#: src/views/boards/index.tsx:39
#: src/views/boards/index.tsx:50
msgid "Import"
msgstr "Importuj"
@@ -1262,9 +1324,13 @@ msgstr "Kanban wymyślony na nowo"
msgid "Keep in mind that this action is irreversible."
msgstr "Pamiętaj, że ta akcja jest nieodwracalna."
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
#: src/providers/keyboard-shortcuts.tsx:320
msgid "Keyboard Shortcuts"
msgstr "Skróty klawiaturowe"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/NewCardForm.tsx:418
#: src/views/card/index.tsx:120
msgid "Labels"
msgstr "Etykiety"
@@ -1310,15 +1376,15 @@ msgstr "Prawne"
msgid "License"
msgstr "Licencja"
#: src/components/UserMenu.tsx:167
#: src/components/UserMenu.tsx:169
msgid "Light"
msgstr "Jasny"
#: src/views/public/board/index.tsx:66
#: src/views/public/board/index.tsx:77
msgid "Link copied"
msgstr "Link skopiowany"
#: src/views/card/index.tsx:111
#: src/views/card/index.tsx:112
msgid "List"
msgstr "Lista"
@@ -1326,7 +1392,7 @@ msgstr "Lista"
msgid "List name"
msgstr "Nazwa listy"
#: src/views/board/components/Filters.tsx:123
#: src/views/board/components/Filters.tsx:163
msgid "Lists"
msgstr "Listy"
@@ -1334,7 +1400,7 @@ msgstr "Listy"
msgid "Login | kan.bn"
msgstr "Zaloguj się | kan.bn"
#: src/components/UserMenu.tsx:209
#: src/components/UserMenu.tsx:224
msgid "Logout"
msgstr "Wyloguj się"
@@ -1350,7 +1416,7 @@ msgstr "Uwielbiane przez zespoły na całym świecie"
msgid "Low Priority"
msgstr "Niski priorytet"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "magiczny link"
@@ -1358,7 +1424,7 @@ msgstr "magiczny link"
msgid "Make template"
msgstr "Utwórz szablon"
#: src/views/card/components/ActivityList.tsx:75
#: src/views/card/components/ActivityList.tsx:72
msgid "marked a checklist item as incomplete"
msgstr "oznaczył element listy kontrolnej jako nieukończony"
@@ -1371,10 +1437,10 @@ msgstr "oznaczył element listy kontrolnej <0>{0}</0> jako nieukończony"
msgid "Medium Priority"
msgstr "Średni priorytet"
#: src/components/SideNavigation.tsx:101
#: src/views/board/components/Filters.tsx:107
#: src/views/board/components/NewCardForm.tsx:367
#: src/views/card/index.tsx:128
#: src/components/SideNavigation.tsx:121
#: src/views/board/components/Filters.tsx:147
#: src/views/board/components/NewCardForm.tsx:376
#: src/views/card/index.tsx:129
#: src/views/members/index.tsx:178
msgid "Members"
msgstr "Członkowie"
@@ -1399,7 +1465,7 @@ msgstr "rozliczenie miesięczne"
msgid "moved the card from <0>{0}</0> to<1>{1}</1>"
msgstr "przeniesiono kartę z <0>{0}</0> do<1>{1}</1>"
#: src/views/card/components/ActivityList.tsx:64
#: src/views/card/components/ActivityList.tsx:61
msgid "moved the card to another list"
msgstr "przeniesiono kartę do innej listy"
@@ -1409,11 +1475,15 @@ msgstr "przeniesiono kartę do innej listy"
msgid "Name"
msgstr "Nazwa"
#: src/providers/keyboard-shortcuts.tsx:54
msgid "Navigation"
msgstr "Nawigacja"
#: src/components/FeedbackModal.tsx:95
msgid "Need help?"
msgstr "Potrzebujesz pomocy?"
#: src/views/boards/index.tsx:50
#: src/views/boards/index.tsx:62
#: src/views/home/components/Features.tsx:73
msgid "New"
msgstr "Nowy"
@@ -1427,7 +1497,7 @@ msgstr "Nowy {0}"
msgid "New API key"
msgstr "Nowy klucz API"
#: src/views/board/components/NewCardForm.tsx:293
#: src/views/board/components/NewCardForm.tsx:302
msgid "New card"
msgstr "Nowa karta"
@@ -1444,7 +1514,7 @@ msgid "New label"
msgstr "Nowa etykieta"
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
#: src/views/board/index.tsx:466
msgid "New list"
msgstr "Nowa lista"
@@ -1481,7 +1551,7 @@ msgstr "Następne kroki"
msgid "No {0}"
msgstr "Brak {0}"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Obecnie brak dostępnych metod uwierzytelniania"
@@ -1493,11 +1563,19 @@ msgstr "Nie znaleziono tablic"
msgid "No credit card required"
msgstr "Nie wymagana karta kredytowa"
#: src/views/board/components/Filters.tsx:137
msgid "No dates"
msgstr "Brak terminów"
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "Brak dostępnego URL do pobrania tego załącznika."
#: src/views/board/index.tsx:467
#: src/providers/keyboard-shortcuts.tsx:333
msgid "No keyboard shortcuts registered."
msgstr "Brak zarejestrowanych skrótów klawiaturowych."
#: src/views/board/index.tsx:492
msgid "No lists"
msgstr "Brak list"
@@ -1521,6 +1599,14 @@ msgstr "Po usunięciu konta nie będzie możliwości jego przywrócenia. Tej ope
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
msgstr "Po usunięciu przestrzeni roboczej nie będzie możliwości jej przywrócenia. Tej operacji nie można cofnąć."
#: src/components/WorkspaceMenu.tsx:31
msgid "Open command menu"
msgstr "Otwórz menu poleceń"
#: src/providers/keyboard-shortcuts.tsx:171
msgid "Open keyboard shortcuts"
msgstr "Otwórz skróty klawiaturowe"
#: src/views/pricing/components/FeatureComparisonTable.tsx:150
msgid "Open source"
msgstr "Otwartoźródłowe"
@@ -1529,7 +1615,7 @@ msgstr "Otwartoźródłowe"
msgid "Open Source Friends"
msgstr "Przyjaciele Open Source"
#: src/components/AuthForm.tsx:374
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "lub"
@@ -1542,6 +1628,10 @@ msgstr "Organizuj i znajduj karty szybko dzięki zaawansowanym narzędziom filtr
msgid "OSS Friends"
msgstr "Przyjaciele OSS"
#: src/views/board/components/Filters.tsx:112
msgid "Overdue"
msgstr "Zaległe"
#: src/views/pricing/components/Pricing.tsx:73
#: src/views/pricing/components/PricingTiers.tsx:70
msgid "Own your data"
@@ -1611,15 +1701,15 @@ msgstr "Platforma"
msgid "Please confirm your new password"
msgstr "Proszę potwierdzić nowe hasło"
#: src/components/AuthForm.tsx:402
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Proszę wprowadzić prawidłowy adres e-mail"
#: src/components/AuthForm.tsx:388
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Proszę wprowadzić poprawne imię"
#: src/components/AuthForm.tsx:416
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Proszę wprowadzić poprawne hasło"
@@ -1630,14 +1720,14 @@ msgstr "Proszę wybrać plik do przesłania."
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:177
#: src/views/board/components/NewCardForm.tsx:184
#: src/views/board/components/NewListForm.tsx:79
#: src/views/board/components/NewTemplateForm.tsx:61
#: src/views/board/components/NewTemplateForm.tsx:77
#: src/views/board/components/UpdateBoardSlugForm.tsx:73
#: src/views/board/components/VisibilityButton.tsx:54
#: src/views/board/index.tsx:151
#: src/views/board/index.tsx:207
#: src/views/board/index.tsx:174
#: src/views/board/index.tsx:230
#: src/views/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
@@ -1647,13 +1737,15 @@ msgstr "Proszę wybrać plik do przesłania."
#: 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/DueDateSelector.tsx:59
#: 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/NewChecklistForm.tsx:70
#: src/views/card/components/NewChecklistItemForm.tsx:89
#: src/views/card/components/NewCommentForm.tsx:31
#: src/views/card/index.tsx:177
#: src/views/card/index.tsx:187
#: src/views/card/index.tsx:200
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:241
@@ -1753,11 +1845,11 @@ msgstr "Usuń"
msgid "Remove member"
msgstr "Usuń członka"
#: src/views/card/components/ActivityList.tsx:66
#: src/views/card/components/ActivityList.tsx:63
msgid "removed a label from the card"
msgstr "usunął etykietę z karty"
#: src/views/card/components/ActivityList.tsx:68
#: src/views/card/components/ActivityList.tsx:65
msgid "removed a member from the card"
msgstr "usunął członka z karty"
@@ -1766,7 +1858,12 @@ msgstr "usunął członka z karty"
msgid "removed label <0>{0}</0>"
msgstr "usunięto etykietę <0>{0}</0>"
#: src/views/card/components/ActivityList.tsx:70
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:241
msgid "removed the due date"
msgstr "usunięto termin"
#: src/views/card/components/ActivityList.tsx:67
msgid "renamed a checklist"
msgstr "zmieniono nazwę listy kontrolnej"
@@ -1869,8 +1966,16 @@ msgstr "Prześlij opinię"
msgid "Senior"
msgstr "Starszy"
#: src/views/card/components/DueDateSelector.tsx:116
msgid "Set due date"
msgstr "Ustaw termin"
#: src/views/card/components/ActivityList.tsx:74
msgid "set the due date"
msgstr "ustawiono termin"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:106
#: src/components/SideNavigation.tsx:133
msgid "Settings"
msgstr "Ustawienia"
@@ -1894,6 +1999,10 @@ msgstr "Ustawienia | Integracje"
msgid "Settings | Workspace"
msgstr "Ustawienia | Obszar roboczy"
#: src/components/UserMenu.tsx:184
msgid "Shortcuts"
msgstr "Skróty"
#: src/views/home/components/Header.tsx:104
msgid "Sign in"
msgstr "Zaloguj się"
@@ -1919,7 +2028,7 @@ msgstr "Rejestracja wyłączona"
msgid "Sign up is currently disabled. Please try again later."
msgstr "Rejestracja jest obecnie wyłączona. Spróbuj ponownie później."
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Zarejestruj się za pomocą "
@@ -1962,7 +2071,7 @@ msgstr "Sukces"
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Zwiększ możliwości swojego obszaru roboczego za jedyne 29 USD/miesiąc. Oto, co otrzymasz:"
#: src/components/UserMenu.tsx:180
#: src/components/UserMenu.tsx:195
msgid "Support"
msgstr "Wsparcie"
@@ -1971,7 +2080,7 @@ msgstr "Wsparcie"
msgid "Support the development of the project"
msgstr "Wspieraj rozwój projektu"
#: src/components/UserMenu.tsx:139
#: src/components/UserMenu.tsx:141
msgid "System"
msgstr "System"
@@ -1986,8 +2095,8 @@ msgstr "Plan zespołowy"
msgid "Teams"
msgstr "Zespoły"
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
#: src/views/board/index.tsx:386
#: src/views/board/index.tsx:421
msgid "Template"
msgstr "Szablon"
@@ -2007,7 +2116,7 @@ msgstr "Nazwa szablonu nie może przekraczać 100 znaków"
msgid "Template name is required"
msgstr "Nazwa szablonu jest wymagana"
#: src/components/SideNavigation.tsx:96
#: src/components/SideNavigation.tsx:109
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Szablony"
@@ -2041,7 +2150,7 @@ msgstr "Otwarta alternatywa <0/> dla Trello"
msgid "The visibility of your board has been set to {0}."
msgstr "Widoczność Twojej tablicy została ustawiona na {0}."
#: src/components/UserMenu.tsx:126
#: src/components/UserMenu.tsx:128
msgid "Theme"
msgstr "Motyw"
@@ -2061,7 +2170,7 @@ msgstr "Tej akcji nie można cofnąć."
msgid "This API key will only be shown once. Please save it in a secure location."
msgstr "Ten klucz API zostanie wyświetlony tylko raz. Proszę zapisać go w bezpiecznym miejscu."
#: src/views/public/board/index.tsx:156
#: src/views/public/board/index.tsx:168
msgid "This board is private or does not exist"
msgstr "Ta tablica jest prywatna lub nie istnieje"
@@ -2135,7 +2244,11 @@ msgstr "Nie można dodać elementu listy kontrolnej"
msgid "Unable to add comment"
msgstr "Nie można dodać komentarza"
#: src/views/board/components/NewCardForm.tsx:174
#: src/views/card/index.tsx:199
msgid "Unable to add label"
msgstr ""
#: src/views/board/components/NewCardForm.tsx:181
msgid "Unable to create card"
msgstr "Nie można utworzyć karty"
@@ -2192,8 +2305,8 @@ msgstr "Nie można zaktualizować adresu URL tablicy"
msgid "Unable to update board visibility"
msgstr "Nie można zaktualizować widoczności tablicy"
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
#: src/views/board/index.tsx:229
#: src/views/card/index.tsx:186
msgid "Unable to update card"
msgstr "Nie można zaktualizować karty"
@@ -2209,11 +2322,15 @@ msgstr "Nie można zaktualizować elementu listy kontrolnej"
msgid "Unable to update comment"
msgstr "Nie można zaktualizować komentarza"
#: src/views/card/components/DueDateSelector.tsx:58
msgid "Unable to update due date"
msgstr "Nie można zaktualizować terminu"
#: src/views/card/components/LabelSelector.tsx:71
msgid "Unable to update labels"
msgstr "Nie można zaktualizować etykiet"
#: src/views/board/index.tsx:150
#: src/views/board/index.tsx:173
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Nie można zaktualizować listy"
@@ -2269,6 +2386,11 @@ msgstr "Nieograniczona liczba członków"
msgid "Unlimited workspaces"
msgstr "Nieograniczona liczba przestrzeni roboczych"
#: src/pages/unsubscribe/index.tsx:68
#: src/pages/unsubscribe/index.tsx:93
msgid "Unsubscribe"
msgstr "Wypisz się"
#: src/views/board/components/UpdateBoardSlugForm.tsx:175
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
@@ -2281,15 +2403,19 @@ msgstr "Aktualizuj"
msgid "Update label"
msgstr "Zaktualizuj etykietę"
#: src/views/card/components/ActivityList.tsx:73
#: src/views/card/components/ActivityList.tsx:70
msgid "updated a checklist item"
msgstr "zaktualizowano element listy kontrolnej"
#: src/views/card/components/ActivityList.tsx:63
#: src/views/card/components/ActivityList.tsx:60
msgid "updated the description"
msgstr "zaktualizowano opis"
#: src/views/card/components/ActivityList.tsx:62
#: src/views/card/components/ActivityList.tsx:75
msgid "updated the due date"
msgstr "zaktualizowano termin"
#: src/views/card/components/ActivityList.tsx:59
msgid "updated the title"
msgstr "zaktualizowano tytuł"
@@ -2298,7 +2424,7 @@ msgstr "zaktualizowano tytuł"
msgid "updated the title to <0>{0}</0>"
msgstr "zaktualizowano tytuł na <0>{0}</0>"
#: src/components/SideNavigation.tsx:202
#: src/components/SideNavigation.tsx:237
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
@@ -2362,7 +2488,7 @@ msgstr "Zobacz i zarządzaj swoimi płatnościami i subskrypcją."
msgid "View docs"
msgstr "Zobacz dokumentację"
#: src/views/public/board/index.tsx:129
#: src/views/public/board/index.tsx:141
msgid "View only"
msgstr "Tylko podgląd"
@@ -2370,7 +2496,7 @@ msgstr "Tylko podgląd"
msgid "View our roadmap."
msgstr "Zobacz nasz plan ulepszeń."
#: src/views/public/board/index.tsx:159
#: src/views/public/board/index.tsx:171
msgid "View workspace"
msgstr "Zobacz obszar roboczy"
@@ -2382,6 +2508,10 @@ msgstr "Widoczność"
msgid "We are using the <0>AGPL-3.0 license</0>."
msgstr "Używamy licencji <0>AGPL-3.0</0>."
#: src/pages/unsubscribe/index.tsx:63
msgid "We couldn't update your preferences. Please try again."
msgstr "Nie udało się zaktualizować twoich preferencji. Spróbuj ponownie."
#: src/views/home/components/Features.tsx:152
msgid "We're just getting started. "
msgstr "Dopiero zaczynamy. "
@@ -2411,10 +2541,10 @@ msgid "Why make an open source Trello?"
msgstr "Dlaczego próbujecie stworzyć Trello w wersji otwartoźródłowej?"
#: src/components/SettingsLayout.tsx:39
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/board/index.tsx:386
#: src/views/boards/index.tsx:33
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:101
#: src/views/public/board/index.tsx:113
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Obszar roboczy"
@@ -2520,6 +2650,10 @@ msgstr "Zostałeś pomyślnie zalogowany."
msgid "You have been signed up successfully."
msgstr "Zostałeś pomyślnie zarejestrowany."
#: src/pages/unsubscribe/index.tsx:98
msgid "You have been unsubscribed!"
msgstr "Zostałeś wypisany!"
#: src/views/members/components/InviteMemberForm.tsx:326
msgid "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
msgstr "Masz nielimitowaną liczbę miejsc w swoim planie Pro. Nie ma dodatkowych opłat za nowych członków!"
@@ -2564,6 +2698,10 @@ msgstr "Twoje konto Trello zostało odłączone."
msgid "Your Trello account is connected."
msgstr "Twoje konto Trello jest połączone."
#: src/pages/unsubscribe/index.tsx:33
msgid "Your unsubscribe link is missing a token. Please open the latest email and try again."
msgstr "Twój link do wypisania się nie zawiera tokena. Otwórz najnowszy e-mail i spróbuj ponownie."
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:49
msgid "Your workspace description has been updated."
msgstr "Opis twojej przestrzeni roboczej został zaktualizowany."

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

View File

@@ -22,7 +22,7 @@ msgid ", or see our"
msgstr ", или посмотрите наши"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#: src/views/boards/index.tsx:27
#: src/views/boards/index.tsx:38
msgid "{0}"
msgstr "{0}"
@@ -30,18 +30,18 @@ msgstr "{0}"
#. placeholder {0}: isTemplate ? "Templates" : "Boards"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:218
#: src/views/boards/index.tsx:33
#: src/views/card/index.tsx:272
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:443
#: src/views/board/components/NewCardForm.tsx:452
msgid "{0} labels"
msgstr "{0} метки"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:389
#: src/views/board/index.tsx:412
msgid "{0} not found"
msgstr "{0} не найден"
@@ -79,8 +79,12 @@ msgstr "Аккаунт"
msgid "Account deleted"
msgstr "Аккаунт удалён"
#: src/views/card/index.tsx:308
#: src/views/public/board/CardModal.tsx:157
#: src/providers/keyboard-shortcuts.tsx:55
msgid "Actions"
msgstr "Действия"
#: src/views/card/index.tsx:386
#: src/views/public/board/CardModal.tsx:158
msgid "Activity"
msgstr "Активность"
@@ -127,19 +131,19 @@ msgstr "Добавить метку"
msgid "Add member"
msgstr "Добавить участника"
#: src/views/card/components/ActivityList.tsx:69
#: src/views/card/components/ActivityList.tsx:66
msgid "added a checklist"
msgstr "добавил(а) чек-лист"
#: src/views/card/components/ActivityList.tsx:72
#: src/views/card/components/ActivityList.tsx:69
msgid "added a checklist item"
msgstr "добавил(а) элемент чек-листа"
#: src/views/card/components/ActivityList.tsx:65
#: src/views/card/components/ActivityList.tsx:62
msgid "added a label to the card"
msgstr "добавил(а) метку к карточке"
#: src/views/card/components/ActivityList.tsx:67
#: src/views/card/components/ActivityList.tsx:64
msgid "added a member to the card"
msgstr "добавил(а) участника к карточке"
@@ -308,9 +312,9 @@ msgstr "Портал выставления счетов"
msgid "Blog Post"
msgstr "Пост в блоге"
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:218
#: src/views/public/board/index.tsx:101
#: src/views/board/index.tsx:386
#: src/views/card/index.tsx:272
#: src/views/public/board/index.tsx:113
msgid "Board"
msgstr "Доска"
@@ -327,7 +331,7 @@ msgstr "Название доски не может превышать 100 си
msgid "Board name is required"
msgstr "Название доски обязательно"
#: src/views/public/board/index.tsx:153
#: src/views/public/board/index.tsx:165
msgid "Board not found"
msgstr "Доска не найдена"
@@ -343,7 +347,7 @@ msgstr "URL доски может содержать только буквы, ц
msgid "Board URL cannot exceed 60 characters"
msgstr "URL доски не может превышать 60 символов"
#: src/views/public/board/index.tsx:68
#: src/views/public/board/index.tsx:79
msgid "Board URL copied to clipboard"
msgstr "URL доски скопирован в буфер обмена"
@@ -360,7 +364,7 @@ msgstr "Видимость доски"
msgid "Board visibility updated"
msgstr "Видимость доски обновлена"
#: src/components/SideNavigation.tsx:91
#: src/components/SideNavigation.tsx:97
msgid "Boards"
msgstr "Доски"
@@ -391,15 +395,16 @@ msgstr "Отчет об ошибке"
msgid "Cancel"
msgstr "Отмена"
#: src/views/card/index.tsx:218
#: src/views/card/index.tsx:272
msgid "Card"
msgstr "Карточка"
#: src/views/card/index.tsx:264
#: src/views/card/index.tsx:306
#: src/views/card/index.tsx:342
msgid "Card not found"
msgstr "Карточка не найдена"
#: src/views/board/components/NewCardForm.tsx:310
#: src/views/board/components/NewCardForm.tsx:319
msgid "Card title"
msgstr "Название карточки"
@@ -414,6 +419,11 @@ msgstr "Изменить пароль"
msgid "Change your language preferences."
msgstr "Измените настройки языка."
#: src/views/card/components/ActivityList.tsx:220
#: src/views/card/components/ActivityList.tsx:234
msgid "changed the due date to <0>{formattedDate}</0>"
msgstr "изменил(а) дату выполнения на <0>{formattedDate}</0>"
#: src/views/oss-friends/index.tsx:61
msgid "Check out some of our favorite open source projects."
msgstr "Ознакомьтесь с некоторыми из наших любимых проектов с открытым исходным кодом."
@@ -432,7 +442,7 @@ msgstr "Название контрольного списка"
msgid "Checklists"
msgstr "Контрольные списки"
#: src/views/board/components/Filters.tsx:180
#: src/views/board/components/Filters.tsx:227
msgid "Clear filters"
msgstr "Очистить фильтры"
@@ -484,7 +494,7 @@ msgstr "Завершить"
msgid "Complete control and ownership:"
msgstr "Полный контроль и владение:"
#: src/views/card/components/ActivityList.tsx:74
#: src/views/card/components/ActivityList.tsx:71
msgid "completed a checklist item"
msgstr "завершил элемент контрольного списка"
@@ -493,6 +503,10 @@ msgstr "завершил элемент контрольного списка"
msgid "completed checklist item <0>{0}</0>"
msgstr "завершил элемент контрольного списка <0>{0}</0>"
#: src/pages/unsubscribe/index.tsx:81
msgid "Confirm your email preferences:"
msgstr "Подтвердите ваши настройки электронной почты:"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:150
msgid "Confirm your new password"
msgstr "Подтвердите ваш новый пароль"
@@ -523,12 +537,12 @@ msgstr "Свяжитесь с нами"
msgid "Content Creation"
msgstr "Создание контента"
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Continue with "
msgstr "Продолжить с "
#. placeholder {0}: key === "oidc" ? oidcProviderName : provider.name
#: src/components/AuthForm.tsx:351
#: src/components/AuthForm.tsx:355
msgid "Continue with {0}"
msgstr "Продолжить с {0}"
@@ -546,7 +560,7 @@ msgid "Create {0}"
msgstr "Создать {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewCardForm.tsx:516
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Создать ещё одну"
@@ -555,7 +569,7 @@ msgstr "Создать ещё одну"
msgid "Create API key"
msgstr "Создать API-ключ"
#: src/views/board/components/NewCardForm.tsx:479
#: src/views/board/components/NewCardForm.tsx:526
msgid "Create card"
msgstr "Создать карточку"
@@ -577,6 +591,7 @@ msgstr "Создать список"
#. placeholder {0}: isTemplate ? "template" : "board"
#: src/views/boards/components/BoardsList.tsx:45
#: src/views/boards/index.tsx:26
msgid "Create new {0}"
msgstr "Создать новый {0}"
@@ -584,12 +599,13 @@ msgstr "Создать новый {0}"
msgid "Create new key"
msgstr "Создать новый ключ"
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/board/components/NewCardForm.tsx:414
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Создать новую метку"
#: src/views/board/index.tsx:478
#: src/views/board/index.tsx:65
#: src/views/board/index.tsx:503
msgid "Create new list"
msgstr "Создать новый список"
@@ -598,11 +614,11 @@ msgid "Create template"
msgstr "Создать шаблон"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:149
#: src/components/WorkspaceMenu.tsx:153
msgid "Create workspace"
msgstr "Создать рабочее пространство"
#: src/views/card/components/ActivityList.tsx:61
#: src/views/card/components/ActivityList.tsx:58
msgid "created the card"
msgstr "создал(а) карточку"
@@ -648,7 +664,7 @@ msgstr "Пользовательский URL рабочего пространс
msgid "Customer Support"
msgstr "Служба поддержки"
#: src/components/UserMenu.tsx:153
#: src/components/UserMenu.tsx:155
msgid "Dark"
msgstr "Тёмный"
@@ -697,11 +713,11 @@ msgstr "Удалить шаблон"
msgid "Delete workspace"
msgstr "Удалить рабочее пространство"
#: src/views/card/components/ActivityList.tsx:71
#: src/views/card/components/ActivityList.tsx:68
msgid "deleted a checklist"
msgstr "удалил контрольный список"
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:73
msgid "deleted a checklist item"
msgstr "удалил элемент контрольного списка"
@@ -747,6 +763,10 @@ msgstr "Отображаемое имя обновлено"
msgid "Do you offer a free plan?"
msgstr "Вы предлагаете бесплатный план?"
#: src/pages/unsubscribe/index.tsx:78
msgid "Do you want to unsubscribe?"
msgstr "Вы хотите отписаться?"
#: src/components/FeedbackModal.tsx:109
msgid "docs"
msgstr "документы"
@@ -755,7 +775,7 @@ msgstr "документы"
msgid "Docs"
msgstr "Документы"
#: src/components/UserMenu.tsx:191
#: src/components/UserMenu.tsx:206
#: src/views/boards/components/TemplateBoards.tsx:24
#: src/views/home/components/Footer.tsx:78
#: src/views/home/components/Header.tsx:36
@@ -777,6 +797,28 @@ msgstr "Готово"
msgid "Download failed"
msgstr "Не удалось загрузить"
#: src/views/board/components/Filters.tsx:171
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/card/index.tsx:138
msgid "Due date"
msgstr "Дата выполнения"
#: src/views/board/components/Filters.tsx:132
msgid "Due next month"
msgstr "Срок в следующем месяце"
#: src/views/board/components/Filters.tsx:127
msgid "Due next week"
msgstr "Срок на следующей неделе"
#: src/views/board/components/Filters.tsx:117
msgid "Due today"
msgstr "Срок сегодня"
#: src/views/board/components/Filters.tsx:122
msgid "Due tomorrow"
msgstr "Срок завтра"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
@@ -798,7 +840,7 @@ msgstr "Редактировать URL рабочей области"
msgid "Editing"
msgstr "Редактирование"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "email"
@@ -818,11 +860,11 @@ msgstr "Введите ваш текущий пароль"
msgid "Enter your current password and choose a new secure password."
msgstr "Введите ваш текущий пароль и выберите новый безопасный пароль."
#: src/components/AuthForm.tsx:398
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Введите ваш адрес электронной почты"
#: src/components/AuthForm.tsx:384
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Введите ваше имя"
@@ -830,7 +872,7 @@ msgstr "Введите ваше имя"
msgid "Enter your new password"
msgstr "Введите ваш новый пароль"
#: src/components/AuthForm.tsx:411
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Введите ваш пароль"
@@ -979,7 +1021,7 @@ msgid "Features"
msgstr "Функции"
#: src/components/FeedbackModal.tsx:59
#: src/components/UserMenu.tsx:199
#: src/components/UserMenu.tsx:214
msgid "Feedback"
msgstr "Обратная связь"
@@ -987,7 +1029,7 @@ msgstr "Обратная связь"
msgid "Feedback sent"
msgstr "Обратная связь отправлена"
#: src/views/board/components/Filters.tsx:174
#: src/views/board/components/Filters.tsx:221
msgid "Filter"
msgstr "Фильтр"
@@ -1024,6 +1066,10 @@ msgstr "Полный рабочий день"
msgid "Fun"
msgstr "Веселье"
#: src/providers/keyboard-shortcuts.tsx:53
msgid "General"
msgstr "Общее"
#: src/views/auth/signup/index.tsx:69
#: src/views/home/components/Cta.tsx:61
#: src/views/home/components/Header.tsx:106
@@ -1043,7 +1089,7 @@ msgstr "Начать"
msgid "Get started by creating a new {0}"
msgstr "Начните с создания нового {0}"
#: src/views/board/index.tsx:470
#: src/views/board/index.tsx:495
msgid "Get started by creating a new list"
msgstr "Начните с создания нового списка"
@@ -1077,6 +1123,22 @@ msgstr "На главную"
msgid "Go to app"
msgstr "Перейти в приложение"
#: src/components/SideNavigation.tsx:105
msgid "Go to boards"
msgstr "Перейти к доскам"
#: src/components/SideNavigation.tsx:129
msgid "Go to members"
msgstr "Перейти к участникам"
#: src/components/SideNavigation.tsx:141
msgid "Go to settings"
msgstr "Перейти к настройкам"
#: src/components/SideNavigation.tsx:117
msgid "Go to templates"
msgstr "Перейти к шаблонам"
#: src/views/boards/components/TemplateBoards.tsx:18
msgid "High Priority"
msgstr "Высокий приоритет"
@@ -1126,7 +1188,7 @@ msgstr "Идеи"
msgid "Ideas to improve this page..."
msgstr "Идеи для улучшения этой страницы..."
#: src/views/boards/index.tsx:39
#: src/views/boards/index.tsx:50
msgid "Import"
msgstr "Импорт"
@@ -1257,9 +1319,13 @@ msgstr "Канбан переосмыслен"
msgid "Keep in mind that this action is irreversible."
msgstr "Имейте в виду, что это действие необратимо."
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
#: src/providers/keyboard-shortcuts.tsx:320
msgid "Keyboard Shortcuts"
msgstr "Горячие клавиши"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/NewCardForm.tsx:418
#: src/views/card/index.tsx:120
msgid "Labels"
msgstr "Метки"
@@ -1305,15 +1371,15 @@ msgstr "Юридическое"
msgid "License"
msgstr "Лицензия"
#: src/components/UserMenu.tsx:167
#: src/components/UserMenu.tsx:169
msgid "Light"
msgstr "Светлый"
#: src/views/public/board/index.tsx:66
#: src/views/public/board/index.tsx:77
msgid "Link copied"
msgstr "Ссылка скопирована"
#: src/views/card/index.tsx:111
#: src/views/card/index.tsx:112
msgid "List"
msgstr "Список"
@@ -1321,7 +1387,7 @@ msgstr "Список"
msgid "List name"
msgstr "Название списка"
#: src/views/board/components/Filters.tsx:123
#: src/views/board/components/Filters.tsx:163
msgid "Lists"
msgstr "Списки"
@@ -1329,7 +1395,7 @@ msgstr "Списки"
msgid "Login | kan.bn"
msgstr "Вход | kan.bn"
#: src/components/UserMenu.tsx:209
#: src/components/UserMenu.tsx:224
msgid "Logout"
msgstr "Выход"
@@ -1345,7 +1411,7 @@ msgstr "Любим командами по всему миру"
msgid "Low Priority"
msgstr "Низкий приоритет"
#: src/components/AuthForm.tsx:435
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "магическая ссылка"
@@ -1353,7 +1419,7 @@ msgstr "магическая ссылка"
msgid "Make template"
msgstr "Создать шаблон"
#: src/views/card/components/ActivityList.tsx:75
#: src/views/card/components/ActivityList.tsx:72
msgid "marked a checklist item as incomplete"
msgstr "отметил элемент контрольного списка как незавершённый"
@@ -1366,10 +1432,10 @@ msgstr "отметил элемент контрольного списка <0>{
msgid "Medium Priority"
msgstr "Средний приоритет"
#: src/components/SideNavigation.tsx:101
#: src/views/board/components/Filters.tsx:107
#: src/views/board/components/NewCardForm.tsx:367
#: src/views/card/index.tsx:128
#: src/components/SideNavigation.tsx:121
#: src/views/board/components/Filters.tsx:147
#: src/views/board/components/NewCardForm.tsx:376
#: src/views/card/index.tsx:129
#: src/views/members/index.tsx:178
msgid "Members"
msgstr "Участники"
@@ -1394,7 +1460,7 @@ msgstr "ежемесячная оплата"
msgid "moved the card from <0>{0}</0> to<1>{1}</1>"
msgstr "переместил карточку из <0>{0}</0> в <1>{1}</1>"
#: src/views/card/components/ActivityList.tsx:64
#: src/views/card/components/ActivityList.tsx:61
msgid "moved the card to another list"
msgstr "переместил карточку в другой список"
@@ -1404,11 +1470,15 @@ msgstr "переместил карточку в другой список"
msgid "Name"
msgstr "Имя"
#: src/providers/keyboard-shortcuts.tsx:54
msgid "Navigation"
msgstr "Навигация"
#: src/components/FeedbackModal.tsx:95
msgid "Need help?"
msgstr "Нужна помощь?"
#: src/views/boards/index.tsx:50
#: src/views/boards/index.tsx:62
#: src/views/home/components/Features.tsx:73
msgid "New"
msgstr "Новый"
@@ -1422,7 +1492,7 @@ msgstr "Новый {0}"
msgid "New API key"
msgstr "Новый API-ключ"
#: src/views/board/components/NewCardForm.tsx:293
#: src/views/board/components/NewCardForm.tsx:302
msgid "New card"
msgstr "Новая карточка"
@@ -1439,7 +1509,7 @@ msgid "New label"
msgstr "Новая метка"
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
#: src/views/board/index.tsx:466
msgid "New list"
msgstr "Новый список"
@@ -1476,7 +1546,7 @@ msgstr "Следующие шаги"
msgid "No {0}"
msgstr "Нет {0}"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "В настоящее время нет доступных методов аутентификации"
@@ -1488,11 +1558,19 @@ msgstr "Доски не найдены"
msgid "No credit card required"
msgstr "Кредитная карта не требуется"
#: src/views/board/components/Filters.tsx:137
msgid "No dates"
msgstr "Нет дат"
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "URL для загрузки этого вложения недоступен."
#: src/views/board/index.tsx:467
#: src/providers/keyboard-shortcuts.tsx:333
msgid "No keyboard shortcuts registered."
msgstr "Горячие клавиши не зарегистрированы."
#: src/views/board/index.tsx:492
msgid "No lists"
msgstr "Нет списков"
@@ -1516,6 +1594,14 @@ msgstr "После удаления аккаунта возврат будет
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
msgstr "После удаления рабочего пространства возврат будет невозможен. Это действие необратимо."
#: src/components/WorkspaceMenu.tsx:31
msgid "Open command menu"
msgstr "Открыть меню команд"
#: src/providers/keyboard-shortcuts.tsx:171
msgid "Open keyboard shortcuts"
msgstr "Открыть горячие клавиши"
#: src/views/pricing/components/FeatureComparisonTable.tsx:150
msgid "Open source"
msgstr "С открытым исходным кодом"
@@ -1524,7 +1610,7 @@ msgstr "С открытым исходным кодом"
msgid "Open Source Friends"
msgstr "Друзья с открытым исходным кодом"
#: src/components/AuthForm.tsx:374
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "или"
@@ -1537,6 +1623,10 @@ msgstr "Организуйте и находите карточки быстро
msgid "OSS Friends"
msgstr "Друзья OSS"
#: src/views/board/components/Filters.tsx:112
msgid "Overdue"
msgstr "Просрочено"
#: src/views/pricing/components/Pricing.tsx:73
#: src/views/pricing/components/PricingTiers.tsx:70
msgid "Own your data"
@@ -1606,15 +1696,15 @@ msgstr "Платформа"
msgid "Please confirm your new password"
msgstr "Пожалуйста, подтвердите новый пароль"
#: src/components/AuthForm.tsx:402
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Пожалуйста, введите действительный адрес электронной почты"
#: src/components/AuthForm.tsx:388
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Пожалуйста, введите действительное имя"
#: src/components/AuthForm.tsx:416
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Пожалуйста, введите действительный пароль"
@@ -1625,14 +1715,14 @@ msgstr "Пожалуйста, выберите файл для загрузки.
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:177
#: src/views/board/components/NewCardForm.tsx:184
#: src/views/board/components/NewListForm.tsx:79
#: src/views/board/components/NewTemplateForm.tsx:61
#: src/views/board/components/NewTemplateForm.tsx:77
#: src/views/board/components/UpdateBoardSlugForm.tsx:73
#: src/views/board/components/VisibilityButton.tsx:54
#: src/views/board/index.tsx:151
#: src/views/board/index.tsx:207
#: src/views/board/index.tsx:174
#: src/views/board/index.tsx:230
#: src/views/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
@@ -1642,13 +1732,15 @@ msgstr "Пожалуйста, выберите файл для загрузки.
#: 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/DueDateSelector.tsx:59
#: 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/NewChecklistForm.tsx:70
#: src/views/card/components/NewChecklistItemForm.tsx:89
#: src/views/card/components/NewCommentForm.tsx:31
#: src/views/card/index.tsx:177
#: src/views/card/index.tsx:187
#: src/views/card/index.tsx:200
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:241
@@ -1748,11 +1840,11 @@ msgstr "Удалить"
msgid "Remove member"
msgstr "Удалить участника"
#: src/views/card/components/ActivityList.tsx:66
#: src/views/card/components/ActivityList.tsx:63
msgid "removed a label from the card"
msgstr "удалил метку с карточки"
#: src/views/card/components/ActivityList.tsx:68
#: src/views/card/components/ActivityList.tsx:65
msgid "removed a member from the card"
msgstr "удалил участника из карточки"
@@ -1761,7 +1853,12 @@ msgstr "удалил участника из карточки"
msgid "removed label <0>{0}</0>"
msgstr "удалил метку <0>{0}</0>"
#: src/views/card/components/ActivityList.tsx:70
#: src/views/card/components/ActivityList.tsx:76
#: src/views/card/components/ActivityList.tsx:241
msgid "removed the due date"
msgstr "удалил(а) дату выполнения"
#: src/views/card/components/ActivityList.tsx:67
msgid "renamed a checklist"
msgstr "переименовал контрольный список"
@@ -1864,8 +1961,16 @@ msgstr "Отправить отзыв"
msgid "Senior"
msgstr "Старший"
#: src/views/card/components/DueDateSelector.tsx:116
msgid "Set due date"
msgstr "Установить дату выполнения"
#: src/views/card/components/ActivityList.tsx:74
msgid "set the due date"
msgstr "установил(а) дату выполнения"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:106
#: src/components/SideNavigation.tsx:133
msgid "Settings"
msgstr "Настройки"
@@ -1889,6 +1994,10 @@ msgstr "Настройки | Интеграции"
msgid "Settings | Workspace"
msgstr "Настройки | Рабочая область"
#: src/components/UserMenu.tsx:184
msgid "Shortcuts"
msgstr "Сочетания клавиш"
#: src/views/home/components/Header.tsx:104
msgid "Sign in"
msgstr "Войти"
@@ -1914,7 +2023,7 @@ msgstr "Регистрация отключена"
msgid "Sign up is currently disabled. Please try again later."
msgstr "Регистрация в настоящее время отключена. Пожалуйста, попробуйте позже."
#: src/components/AuthForm.tsx:434
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Зарегистрироваться с "
@@ -1957,7 +2066,7 @@ msgstr "Успех"
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Улучшите свою рабочую область всего за $29/месяц. Вот что вы получите:"
#: src/components/UserMenu.tsx:180
#: src/components/UserMenu.tsx:195
msgid "Support"
msgstr "Поддержка"
@@ -1966,7 +2075,7 @@ msgstr "Поддержка"
msgid "Support the development of the project"
msgstr "Поддержите развитие проекта"
#: src/components/UserMenu.tsx:139
#: src/components/UserMenu.tsx:141
msgid "System"
msgstr "Система"
@@ -1981,8 +2090,8 @@ msgstr "Командный план"
msgid "Teams"
msgstr "Команды"
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
#: src/views/board/index.tsx:386
#: src/views/board/index.tsx:421
msgid "Template"
msgstr "Шаблон"
@@ -2002,7 +2111,7 @@ msgstr "Имя шаблона не может превышать 100 симво
msgid "Template name is required"
msgstr "Имя шаблона обязательно"
#: src/components/SideNavigation.tsx:96
#: src/components/SideNavigation.tsx:109
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Шаблоны"
@@ -2036,7 +2145,7 @@ msgstr "Альтернатива Trello с открытым исходным к
msgid "The visibility of your board has been set to {0}."
msgstr "Видимость вашей доски установлена на {0}."
#: src/components/UserMenu.tsx:126
#: src/components/UserMenu.tsx:128
msgid "Theme"
msgstr "Тема"
@@ -2056,7 +2165,7 @@ msgstr "Это действие нельзя будет отменить."
msgid "This API key will only be shown once. Please save it in a secure location."
msgstr "Этот API-ключ будет показан только один раз. Пожалуйста, сохраните его в безопасном месте."
#: src/views/public/board/index.tsx:156
#: src/views/public/board/index.tsx:168
msgid "This board is private or does not exist"
msgstr "Эта доска является приватной или не существует."
@@ -2130,7 +2239,11 @@ msgstr "Не удалось добавить элемент контрольно
msgid "Unable to add comment"
msgstr "Не удалось добавить комментарий."
#: src/views/board/components/NewCardForm.tsx:174
#: src/views/card/index.tsx:199
msgid "Unable to add label"
msgstr ""
#: src/views/board/components/NewCardForm.tsx:181
msgid "Unable to create card"
msgstr "Не удалось создать карточку."
@@ -2187,8 +2300,8 @@ msgstr "Не удалось обновить URL доски"
msgid "Unable to update board visibility"
msgstr "Не удалось обновить видимость доски"
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
#: src/views/board/index.tsx:229
#: src/views/card/index.tsx:186
msgid "Unable to update card"
msgstr "Не удалось обновить карточку"
@@ -2204,11 +2317,15 @@ msgstr "Не удалось обновить элемент контрольно
msgid "Unable to update comment"
msgstr "Не удалось обновить комментарий"
#: src/views/card/components/DueDateSelector.tsx:58
msgid "Unable to update due date"
msgstr "Не удалось обновить дату выполнения"
#: src/views/card/components/LabelSelector.tsx:71
msgid "Unable to update labels"
msgstr "Не удалось обновить метки"
#: src/views/board/index.tsx:150
#: src/views/board/index.tsx:173
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Не удалось обновить список"
@@ -2264,6 +2381,11 @@ msgstr "Неограниченное количество участников"
msgid "Unlimited workspaces"
msgstr "Неограниченное количество рабочих пространств"
#: src/pages/unsubscribe/index.tsx:68
#: src/pages/unsubscribe/index.tsx:93
msgid "Unsubscribe"
msgstr "Отписаться"
#: src/views/board/components/UpdateBoardSlugForm.tsx:175
#: src/views/settings/components/UpdateDisplayNameForm.tsx:80
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:91
@@ -2276,15 +2398,19 @@ msgstr "Обновить"
msgid "Update label"
msgstr "Обновить метку"
#: src/views/card/components/ActivityList.tsx:73
#: src/views/card/components/ActivityList.tsx:70
msgid "updated a checklist item"
msgstr "обновлен элемент контрольного списка"
#: src/views/card/components/ActivityList.tsx:63
#: src/views/card/components/ActivityList.tsx:60
msgid "updated the description"
msgstr "обновлено описание"
#: src/views/card/components/ActivityList.tsx:62
#: src/views/card/components/ActivityList.tsx:75
msgid "updated the due date"
msgstr "обновил(а) дату выполнения"
#: src/views/card/components/ActivityList.tsx:59
msgid "updated the title"
msgstr "обновлен заголовок"
@@ -2293,7 +2419,7 @@ msgstr "обновлен заголовок"
msgid "updated the title to <0>{0}</0>"
msgstr "обновлен заголовок на <0>{0}</0>"
#: src/components/SideNavigation.tsx:202
#: src/components/SideNavigation.tsx:237
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
@@ -2357,7 +2483,7 @@ msgstr "Просмотр и управление вашими счетами и
msgid "View docs"
msgstr "Просмотреть документацию"
#: src/views/public/board/index.tsx:129
#: src/views/public/board/index.tsx:141
msgid "View only"
msgstr "Только просмотр"
@@ -2365,7 +2491,7 @@ msgstr "Только просмотр"
msgid "View our roadmap."
msgstr "Просмотреть нашу дорожную карту."
#: src/views/public/board/index.tsx:159
#: src/views/public/board/index.tsx:171
msgid "View workspace"
msgstr "Просмотреть рабочее пространство"
@@ -2377,6 +2503,10 @@ msgstr "Видимость"
msgid "We are using the <0>AGPL-3.0 license</0>."
msgstr "Мы используем лицензию <0>AGPL-3.0</0>."
#: src/pages/unsubscribe/index.tsx:63
msgid "We couldn't update your preferences. Please try again."
msgstr "Мы не смогли обновить ваши настройки. Пожалуйста, попробуйте снова."
#: src/views/home/components/Features.tsx:152
msgid "We're just getting started. "
msgstr "Мы только начинаем. "
@@ -2406,10 +2536,10 @@ msgid "Why make an open source Trello?"
msgstr "Зачем создавать Trello с открытым исходным кодом?"
#: src/components/SettingsLayout.tsx:39
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/board/index.tsx:386
#: src/views/boards/index.tsx:33
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:101
#: src/views/public/board/index.tsx:113
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Рабочее пространство"
@@ -2515,6 +2645,10 @@ msgstr "Вы успешно вошли в систему."
msgid "You have been signed up successfully."
msgstr "Вы успешно зарегистрировались."
#: src/pages/unsubscribe/index.tsx:98
msgid "You have been unsubscribed!"
msgstr "Вы успешно отписались!"
#: src/views/members/components/InviteMemberForm.tsx:326
msgid "You have unlimited seats with your Pro Plan. There is no additional charge for new members!"
msgstr "В вашем профессиональном плане неограниченное количество мест. За новых участников дополнительная плата не взимается!"
@@ -2559,6 +2693,10 @@ msgstr "Ваш аккаунт Trello был отключён."
msgid "Your Trello account is connected."
msgstr "Ваш аккаунт Trello подключён."
#: src/pages/unsubscribe/index.tsx:33
msgid "Your unsubscribe link is missing a token. Please open the latest email and try again."
msgstr "В вашей ссылке для отписки отсутствует токен. Пожалуйста, откройте последнее письмо и попробуйте снова."
#: src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx:49
msgid "Your workspace description has been updated."
msgstr "Описание вашего рабочего пространства было обновлено."

File diff suppressed because one or more lines are too long

View File

@@ -12,6 +12,7 @@ import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
import { useEffect } from "react";
import { KeyboardShortcutProvider } from "~/providers/keyboard-shortcuts";
import { LinguiProviderWrapper } from "~/providers/lingui";
import { ModalProvider } from "~/providers/modal";
import { PopupProvider } from "~/providers/popup";
@@ -80,21 +81,23 @@ const MyApp: AppType = ({ Component, pageProps }: AppPropsWithLayout) => {
)}
<script src="/__ENV.js" />
<main className="font-sans">
<LinguiProviderWrapper>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<ModalProvider>
<PopupProvider>
{posthogKey ? (
<PostHogProvider client={posthog}>
{getLayout(<Component {...pageProps} />)}
</PostHogProvider>
) : (
getLayout(<Component {...pageProps} />)
)}
</PopupProvider>
</ModalProvider>
</ThemeProvider>
</LinguiProviderWrapper>
<KeyboardShortcutProvider>
<LinguiProviderWrapper>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<ModalProvider>
<PopupProvider>
{posthogKey ? (
<PostHogProvider client={posthog}>
{getLayout(<Component {...pageProps} />)}
</PostHogProvider>
) : (
getLayout(<Component {...pageProps} />)
)}
</PopupProvider>
</ModalProvider>
</ThemeProvider>
</LinguiProviderWrapper>
</KeyboardShortcutProvider>
</main>
</>
);

View File

@@ -17,12 +17,11 @@ export default async function handler(
}
try {
const downloadUrl = decodeURIComponent(url);
const downloadFilename =
(typeof filename === "string" ? decodeURIComponent(filename) : null) ??
"attachment";
const downloadFilename = typeof filename === "string"
? encodeURIComponent(filename)
: "attachment";
const upstream = await fetch(downloadUrl);
const upstream = await fetch(url);
if (!upstream.ok) {
return res.status(upstream.status).json({
@@ -36,7 +35,7 @@ export default async function handler(
res.setHeader("Content-Type", contentType);
res.setHeader(
"Content-Disposition",
`attachment; filename="${downloadFilename}"`,
`attachment; filename="${downloadFilename}"; filename*=UTF-8''${downloadFilename}`,
);
const buffer = await upstream.arrayBuffer();

View File

@@ -0,0 +1,104 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { Novu } from "@novu/api";
import { jwtVerify } from "jose";
import { z } from "zod";
import { env } from "~/env";
const requestSchema = z.object({
token: z.string().min(1),
});
const tokenPayloadSchema = z.object({
subscriberId: z.string(),
});
type ResponseData =
| { success: true }
| { success: false; error: string; code?: string };
const textEncoder = new TextEncoder();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>,
) {
if (process.env.NEXT_PUBLIC_KAN_ENV !== "cloud") {
return res.status(404).json({
success: false,
error: "Unsubscribe endpoint is not available.",
code: "UNAVAILABLE",
});
}
if (req.method !== "POST") {
res.setHeader("Allow", "POST");
return res.status(405).json({
success: false,
error: "Method not allowed.",
code: "METHOD_NOT_ALLOWED",
});
}
const parsedBody = requestSchema.safeParse(req.body);
if (!parsedBody.success) {
return res.status(400).json({
success: false,
error: "Invalid request payload.",
code: "BAD_REQUEST",
});
}
if (!env.EMAIL_UNSUBSCRIBE_SECRET || !env.NOVU_API_KEY) {
return res.status(500).json({
success: false,
error: "Unsubscribe service is not configured.",
code: "NOT_CONFIGURED",
});
}
let payload: z.infer<typeof tokenPayloadSchema>;
try {
const verified = await jwtVerify(
parsedBody.data.token,
textEncoder.encode(env.EMAIL_UNSUBSCRIBE_SECRET),
{
// We intentionally do not use exp/iat claims
// tokens are long-lived and validated only by signature + payload.
clockTolerance: "0s",
},
);
payload = tokenPayloadSchema.parse(verified.payload);
} catch {
return res.status(401).json({
success: false,
error: "Your unsubscribe link is invalid or has expired.",
code: "INVALID_TOKEN",
});
}
const novu = new Novu({ secretKey: env.NOVU_API_KEY });
try {
await novu.subscribers.preferences.update(
{
channels: {
email: false,
},
},
payload.subscriberId,
);
} catch (error) {
console.error("Failed to update Novu preferences", error);
return res.status(502).json({
success: false,
error:
"We could not update your email preferences right now. Please try again later.",
code: "NOVU_ERROR",
});
}
return res.status(200).json({ success: true });
}

View File

@@ -43,18 +43,22 @@ export default async function handler(
return res.status(400).json({ error: "Invalid content type" });
}
const credentials =
env.S3_ACCESS_KEY_ID && env.S3_SECRET_ACCESS_KEY
? {
accessKeyId: env.S3_ACCESS_KEY_ID,
secretAccessKey: env.S3_SECRET_ACCESS_KEY,
}
: undefined;
const client = new S3Client({
region: env.S3_REGION ?? "",
endpoint: env.S3_ENDPOINT ?? "",
forcePathStyle: env.S3_FORCE_PATH_STYLE === "true",
credentials: {
accessKeyId: env.S3_ACCESS_KEY_ID ?? "",
secretAccessKey: env.S3_SECRET_ACCESS_KEY ?? "",
},
credentials,
});
const signedUrl = await getSignedUrl(
// @ts-ignore
client,
new PutObjectCommand({
Bucket: nextRuntimeEnv("NEXT_PUBLIC_AVATAR_BUCKET_NAME") ?? "",

View File

@@ -0,0 +1,110 @@
import { useRouter } from "next/router";
import { t } from "@lingui/core/macro";
import { useEffect, useState } from "react";
import Button from "~/components/Button";
import { PageHead } from "~/components/PageHead";
import PatternedBackground from "~/components/PatternedBackground";
type UnsubscribeStatus = "idle" | "processing" | "success" | "error";
export default function UnsubscribePage() {
const router = useRouter();
const [token, setToken] = useState("");
const [status, setStatus] = useState<UnsubscribeStatus>("idle");
const [errorMessage, setErrorMessage] = useState<string | null>(null);
useEffect(() => {
if (!router.isReady) return;
const value = router.query.token;
if (typeof value === "string") {
setToken(value);
} else if (Array.isArray(value)) {
setToken(value[0] ?? "");
} else {
setToken("");
}
}, [router.isReady, router.query.token]);
const handleUnsubscribe = async () => {
if (!token) {
setStatus("error");
setErrorMessage(
t`Your unsubscribe link is missing a token. Please open the latest email and try again.`,
);
return;
}
setStatus("processing");
setErrorMessage(null);
try {
const response = await fetch("/api/unsubscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
if (!response.ok) {
const payload = (await response.json().catch(() => null)) as {
error?: string;
} | null;
throw new Error(
payload?.error ??
"We couldn't update your preferences. Please try again.",
);
}
setStatus("success");
} catch (error) {
setStatus("error");
setErrorMessage(
t`We couldn't update your preferences. Please try again.`,
);
}
};
const title = t`Unsubscribe`;
return (
<>
<PageHead title={`${title} | kan.bn`} />
<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-6">
<div>
<h1 className="mt-6 text-center text-3xl font-bold tracking-tight text-light-1000 dark:text-dark-1000">
{t`Do you want to unsubscribe?`}
</h1>
<p className="mt-4 text-center text-sm text-light-900 dark:text-dark-800">
{t`Confirm your email preferences:`}
</p>
</div>
<div className="flex justify-center">
<Button
onClick={handleUnsubscribe}
disabled={status === "success"}
isLoading={status === "processing"}
variant="primary"
size="md"
>
{t`Unsubscribe`}
</Button>
</div>
{status === "success" && (
<p className="text-center text-sm text-light-900 dark:text-dark-800">
{t`You have been unsubscribed!`}
</p>
)}
{status === "error" && (
<p className="mx-auto max-w-[300px] text-center text-sm font-medium text-red-600 dark:text-red-400">
{errorMessage}
</p>
)}
</div>
</div>
</>
);
}

View File

@@ -0,0 +1,592 @@
import type { ReactNode } from "react";
import {
Dialog,
DialogBackdrop,
DialogPanel,
DialogTitle,
} from "@headlessui/react";
import { t } from "@lingui/core/macro";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { HiXMark } from "react-icons/hi2";
import { env } from "~/env";
import { useEventListener } from "~/hooks/useEventListener";
const ModifierKey = {
CONTROL: "CONTROL",
META: "META",
ALT: "ALT",
SHIFT: "SHIFT",
} as const;
type ModifierKey = (typeof ModifierKey)[keyof typeof ModifierKey];
const ModifierKeyInfo: Record<
ModifierKey,
{
macSymbol: string;
winName: string;
linuxName: string;
}
> = {
CONTROL: { macSymbol: "⌃", winName: "Ctrl", linuxName: "Ctrl" },
META: { macSymbol: "⌘", winName: "Win", linuxName: "Super" },
ALT: { macSymbol: "⌥", winName: "Alt", linuxName: "Alt" },
SHIFT: { macSymbol: "⇧", winName: "Shift", linuxName: "Shift" },
};
const ShortcutGroup = {
GENERAL: "GENERAL",
NAVIGATION: "NAVIGATION",
ACTIONS: "ACTIONS",
} as const;
type ShortcutGroup = (typeof ShortcutGroup)[keyof typeof ShortcutGroup];
const getShortcutGroupInfo = (): Record<ShortcutGroup, { label: string }> => ({
GENERAL: { label: t`General` },
NAVIGATION: { label: t`Navigation` },
ACTIONS: { label: t`Actions` },
});
interface KeyStroke {
key: string;
modifiers?: ModifierKey[];
}
interface Press {
type: "PRESS";
stroke: KeyStroke;
}
interface Sequence {
type: "SEQUENCE";
strokes: KeyStroke[];
}
export type KeyboardShortcut = {
action: () => void;
description: string;
group: ShortcutGroup;
} & (Press | Sequence);
interface ShortcutTreeStepNode {
type: "STEP";
children: ShortcutTreeLevel;
}
interface ShortcutTreeActionNode {
type: "ACTION";
shortcut: KeyboardShortcut;
}
type ShortcutTreeNode = ShortcutTreeStepNode | ShortcutTreeActionNode;
type ShortcutTreeLevel = Record<string, ShortcutTreeNode>;
const SEQUENCE_TIMEOUT_MS = 1000;
const ShortcutConflictCode = {
ACTION_ON_SEQUENCE: "ACTION_ON_SEQUENCE",
DUPLICATE_ACTION: "DUPLICATE_ACTION",
SEQUENCE_ON_ACTION: "SEQUENCE_ON_ACTION",
} as const;
type ShortcutConflictCode = keyof typeof ShortcutConflictCode;
interface ShortcutConflictErrorOptions {
code: ShortcutConflictCode;
shortcut: KeyboardShortcut;
conflictPath: string;
existingShortcut?: KeyboardShortcut;
}
class ShortcutConflictError extends Error {
public readonly code: ShortcutConflictCode;
public readonly shortcut: KeyboardShortcut;
public readonly conflictPath: string;
public readonly existingShortcut?: KeyboardShortcut;
constructor(options: ShortcutConflictErrorOptions) {
super(ShortcutConflictError.formatMessage(options));
this.name = "ShortcutConflictError";
this.code = options.code;
this.shortcut = options.shortcut;
this.conflictPath = options.conflictPath;
this.existingShortcut = options.existingShortcut;
}
private static stringifyShortcut(shortcut: KeyboardShortcut): string {
if (shortcut.type === "SEQUENCE") {
return shortcut.strokes.map(serializeKeyStroke).join(" → ");
}
return serializeKeyStroke(shortcut.stroke);
}
private static formatMessage(options: ShortcutConflictErrorOptions): string {
const formatted = ShortcutConflictError.stringifyShortcut(options.shortcut);
switch (options.code) {
case ShortcutConflictCode.ACTION_ON_SEQUENCE:
return `Cannot register "${formatted}": desired action conflicts with existing sequence at "${options.conflictPath}"`;
case ShortcutConflictCode.DUPLICATE_ACTION:
return `Cannot register "${formatted}": action already registered`;
case ShortcutConflictCode.SEQUENCE_ON_ACTION:
return `Cannot register "${formatted}": desired sequence conflicts with existing action at "${options.conflictPath}"`;
}
}
}
interface KeyboardShortcutContextType {
registerShortcut: (shortcut: KeyboardShortcut) => () => void;
openLegend: () => void;
openLegendKeys: ReactNode;
}
const KeyboardShortcutContext = createContext<
KeyboardShortcutContextType | undefined
>(undefined);
export function KeyboardShortcutProvider({
children,
}: {
children: ReactNode;
}) {
const treeRootRef = useRef<ShortcutTreeLevel>({});
const currentNodeRef = useRef<ShortcutTreeLevel>(treeRootRef.current);
const shortcutsRef = useRef<Map<string, KeyboardShortcut>>(new Map());
const sequenceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const [isLegendOpen, setIsLegendOpen] = useState(false);
const openLegendShortcut: KeyboardShortcut = useMemo(
() => ({
type: "PRESS",
stroke: {
key: "/",
modifiers: ["META"],
},
action: () => setIsLegendOpen(true),
description: t`Open keyboard shortcuts`,
group: ShortcutGroup.GENERAL,
}),
[setIsLegendOpen],
);
const handleKeyDown = useCallback((event: KeyboardEvent) => {
if (isTypingInInput(event)) {
return;
}
if (sequenceTimeoutRef.current) {
clearTimeout(sequenceTimeoutRef.current);
sequenceTimeoutRef.current = null;
}
const serializedKey = serializeEvent(event);
const node = currentNodeRef.current[serializedKey];
if (!node) {
currentNodeRef.current = treeRootRef.current;
return;
}
switch (node.type) {
case "STEP":
event.preventDefault();
currentNodeRef.current = node.children;
sequenceTimeoutRef.current = setTimeout(() => {
currentNodeRef.current = treeRootRef.current;
}, SEQUENCE_TIMEOUT_MS);
return;
case "ACTION":
event.preventDefault();
node.shortcut.action();
currentNodeRef.current = treeRootRef.current;
return;
default:
return;
}
}, []);
const registerShortcut = useCallback(
(shortcut: KeyboardShortcut): (() => void) => {
const strokes =
shortcut.type === "SEQUENCE" ? shortcut.strokes : [shortcut.stroke];
const path = strokes.map(serializeKeyStroke);
const pathKey = path.join(" → ");
path.reduce((currentLevel, key, i) => {
const isLast = i === path.length - 1;
const existingNode = currentLevel[key];
if (env.NODE_ENV === "development" && existingNode) {
const conflictPath = path.slice(0, i + 1).join(" → ");
validateNoConflict(existingNode, isLast, shortcut, conflictPath);
}
if (isLast) {
currentLevel[key] = {
type: "ACTION",
shortcut,
};
return currentLevel;
}
if (existingNode?.type === "STEP") {
return existingNode.children;
}
const newNode: ShortcutTreeStepNode = {
type: "STEP",
children: {},
};
currentLevel[key] = newNode;
return newNode.children;
}, treeRootRef.current);
shortcutsRef.current.set(pathKey, shortcut);
return () => {
removePathAndPrune(treeRootRef.current, path);
shortcutsRef.current.delete(pathKey);
};
},
[],
);
useEventListener("keydown", handleKeyDown);
// Register built-in shortcut to open legend
useEffect(() => {
const cleanup = registerShortcut(openLegendShortcut);
return cleanup;
}, [registerShortcut, openLegendShortcut]);
// Cleanup timeout on unmount
useEffect(() => {
return () => {
if (sequenceTimeoutRef.current) {
clearTimeout(sequenceTimeoutRef.current);
}
};
}, []);
const shortcutsArray = Array.from(shortcutsRef.current.values());
const groupedShortcuts = shortcutsArray.reduce<
Partial<Record<ShortcutGroup, KeyboardShortcut[]>>
>((acc, shortcut) => {
const group = shortcut.group;
acc[group] ??= [];
acc[group].push(shortcut);
return acc;
}, {});
const openLegend = useCallback(() => {
setIsLegendOpen(true);
}, []);
const openLegendKeys = useMemo(
() => <FormattedShortcut shortcut={openLegendShortcut} />,
[openLegendShortcut],
);
return (
<KeyboardShortcutContext.Provider
value={{ registerShortcut, openLegend, openLegendKeys }}
>
{children}
{/* Shortcut Legend */}
<Dialog
className="relative z-50"
open={isLegendOpen}
onClose={() => setIsLegendOpen(false)}
>
<DialogBackdrop
transition
className="data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in fixed inset-0 bg-light-50 bg-opacity-40 transition-opacity dark:bg-dark-50 dark:bg-opacity-40"
/>
<div className="fixed inset-0 flex min-h-full w-screen items-center justify-center overflow-y-auto p-4">
<DialogPanel
transition
className="relative w-full max-w-sm transform overflow-hidden rounded-lg border border-light-600 bg-white shadow-3xl-light dark:border-dark-600 dark:bg-dark-100 dark:shadow-3xl-dark"
>
<div className="flex items-center justify-between border-b border-light-300 px-6 py-4 dark:border-dark-300">
<DialogTitle className="text-[14px] font-semibold text-neutral-900 dark:text-dark-1000">
{t`Keyboard Shortcuts`}
</DialogTitle>
<button
onClick={() => setIsLegendOpen(false)}
className="rounded p-1 hover:bg-light-200 dark:hover:bg-dark-200"
>
<HiXMark className="h-5 w-5 text-neutral-700 dark:text-dark-700" />
</button>
</div>
<div className="max-h-[60vh] overflow-y-auto p-6">
{shortcutsArray.length === 0 ? (
<p className="text-center text-sm text-neutral-600 dark:text-dark-600">
{t`No keyboard shortcuts registered.`}
</p>
) : (
<div className="space-y-6">
{Object.values(ShortcutGroup).map((group, idx) => {
const shortcuts = groupedShortcuts[group];
if (!shortcuts?.length) return null;
const groupInfo = getShortcutGroupInfo();
return (
<div key={`${group}-${idx}`}>
<h3 className="mb-3 text-xs font-semibold uppercase tracking-wide text-light-1000 dark:text-dark-1000">
{groupInfo[group].label}
</h3>
<div className="flex flex-col gap-y-2">
{shortcuts.map((shortcut) => (
<ShortcutListItem
key={shortcut.description}
shortcut={shortcut}
/>
))}
</div>
</div>
);
})}
</div>
)}
</div>
</DialogPanel>
</div>
</Dialog>
</KeyboardShortcutContext.Provider>
);
}
/**
* Hook to register a keyboard shortcut. Cleanup is handled automatically.
* Returns both formatted keys and pre-built tooltip content.
*
* IMPORTANT: The shortcut object reference must be stable. If it changes
* on every render, the shortcut will be continuously registered and unregistered.
*
* @example
* ```tsx
* const { keys, tooltipContent } = useKeyboardShortcut({
* type: "PRESS",
* stroke: { key: "k", modifiers: ["META"] },
* action: () => openCommandPalette(),
* description: "Open command palette",
* group: "GENERAL"
* });
*
* // Use tooltip for Tooltip component
* <Tooltip content={tooltipContent}>
* <button>Search</button>
* </Tooltip>
*
* // Or use keys directly for custom formatting
* <span>Press {keys} to search</span>
* ```
*/
export function useKeyboardShortcuts(): KeyboardShortcutContextType {
const context = useContext(KeyboardShortcutContext);
if (!context) {
throw new Error(
"useKeyboardShortcuts must be used within KeyboardShortcutProvider",
);
}
return context;
}
export function useKeyboardShortcut(shortcut: KeyboardShortcut): {
keys: ReactNode;
tooltipContent: ReactNode;
} {
const context = useContext(KeyboardShortcutContext);
if (!context) {
throw new Error(
"useKeyboardShortcut must be used within KeyboardShortcutProvider",
);
}
const { registerShortcut } = context;
useEffect(() => {
const cleanup = registerShortcut(shortcut);
return cleanup;
}, [shortcut, registerShortcut]);
const keys = <FormattedShortcut shortcut={shortcut} />;
const tooltipContent = (
<div className="flex flex-row items-center gap-2 text-[11px]">
{shortcut.description} {keys}
</div>
);
return { keys, tooltipContent };
}
function ShortcutListItem({ shortcut }: { shortcut: KeyboardShortcut }) {
return (
<div className="flex items-center justify-between gap-2">
<span className="text-sm text-dark-50 dark:text-dark-900">
{shortcut.description}
</span>
<FormattedShortcut shortcut={shortcut} />
</div>
);
}
function FormattedShortcut({ shortcut }: { shortcut: KeyboardShortcut }) {
const kbdClassName =
"inline-flex h-5 w-5 items-center justify-center rounded border border-light-400 bg-light-200 px-1.5 py-0.5 font-mono text-[8px] font-semibold text-center text-neutral-900 dark:border-dark-400 dark:bg-dark-200 dark:text-dark-950";
const stringifyModifier = (modifier: ModifierKey): string => {
const isMac =
typeof navigator !== "undefined" && navigator.userAgent.includes("Mac");
const isLinux =
typeof navigator !== "undefined" && navigator.userAgent.includes("Linux");
const info = ModifierKeyInfo[modifier];
if (isMac) return info.macSymbol;
if (isLinux) return info.linuxName;
return info.winName;
};
const formatStroke = (stroke: KeyStroke): ReactNode[] => {
const parts: ReactNode[] = [];
const modifierStrings = stroke.modifiers
? stroke.modifiers.map(stringifyModifier)
: [];
modifierStrings.forEach((mod) => {
parts.push(<kbd className={kbdClassName}>{mod}</kbd>);
});
parts.push(<kbd className={kbdClassName}>{stroke.key.toUpperCase()}</kbd>);
return parts;
};
if (shortcut.type === "SEQUENCE") {
const parts: ReactNode[] = [];
shortcut.strokes.forEach((stroke) => {
parts.push(...formatStroke(stroke));
});
return <span className="flex items-center gap-1 text-[11px]">{parts}</span>;
}
return (
<span className="inline-flex flex-shrink-0 items-center gap-1 text-[11px]">
{formatStroke(shortcut.stroke)}
</span>
);
}
/** Checks for conflicts given existing nodes and current path
* Throws an error if conflict is found
*
*/
function validateNoConflict(
existingNode: ShortcutTreeNode,
isLastKey: boolean,
shortcut: KeyboardShortcut,
conflictPath: string,
): void {
if (isLastKey) {
if (existingNode.type === "STEP") {
throw new ShortcutConflictError({
code: ShortcutConflictCode.ACTION_ON_SEQUENCE,
shortcut,
conflictPath,
});
} else {
throw new ShortcutConflictError({
code: ShortcutConflictCode.DUPLICATE_ACTION,
shortcut,
conflictPath,
existingShortcut: existingNode.shortcut,
});
}
} else if (existingNode.type === "ACTION") {
throw new ShortcutConflictError({
code: ShortcutConflictCode.SEQUENCE_ON_ACTION,
shortcut,
conflictPath,
existingShortcut: existingNode.shortcut,
});
}
}
/**
* Checks if the user is currently typing in an input field
*/
function isTypingInInput(event: KeyboardEvent): boolean {
if (!event.target) return false;
const target = event.target as HTMLElement;
const tagName = target.tagName.toLowerCase();
const isInput = tagName === "input" || tagName === "textarea";
const isContentEditable = target.isContentEditable;
return isInput || isContentEditable;
}
/**
* Serializes a KeyStroke to a consistent string format
* Returns lowercase string like "ctrl+shift+k" with alphabetically sorted modifiers
*/
function serializeKeyStroke(stroke: KeyStroke): string {
const key = stroke.key.toLowerCase();
if (!stroke.modifiers || stroke.modifiers.length === 0) return key;
const sorted = [...stroke.modifiers].sort();
return `${sorted.join("+")}+${key}`;
}
/**
* Converts a keyboard event to a KeyStroke
*/
function eventToKeyStroke(event: KeyboardEvent): KeyStroke {
const modifiers: ModifierKey[] = [];
if (event.altKey) modifiers.push(ModifierKey.ALT);
if (event.ctrlKey) modifiers.push(ModifierKey.CONTROL);
if (event.metaKey) modifiers.push(ModifierKey.META);
// Only include shift if the key is a letter (shift wasn't consumed to produce the character)
if (event.shiftKey && /^[a-zA-Z]$/.test(event.key))
modifiers.push(ModifierKey.SHIFT);
return { key: event.key, modifiers };
}
/**
* Serializes a keyboard event to a consistent string format
*/
function serializeEvent(event: KeyboardEvent): string {
return serializeKeyStroke(eventToKeyStroke(event));
}
/**
* Removes a path from the tree and prunes empty branches
*/
function removePathAndPrune(tree: ShortcutTreeLevel, path: string[]): void {
const [first, ...rest] = path;
if (!first) return;
if (rest.length === 0) {
delete tree[first];
return;
}
const node = tree[first];
if (!node || node.type !== "STEP") return;
removePathAndPrune(node.children, rest);
if (Object.keys(node.children).length === 0) {
delete tree[first];
}
}

View File

@@ -39,3 +39,17 @@
}
}
}
/* Tippy.js tooltip theme */
.tippy-box[data-theme~="tooltip"] {
@apply rounded-md border border-light-600 bg-white px-2 py-1 text-sm text-neutral-900 shadow-lg dark:border-dark-600 dark:bg-dark-100 dark:text-dark-1000;
}
.tippy-box[data-theme~="tooltip"] > .tippy-arrow::before {
@apply border-t-light-600 dark:border-t-dark-600;
}
.tippy-box[data-theme~="tooltip"][data-placement^="bottom"]
> .tippy-arrow::before {
@apply border-b-light-600 dark:border-b-dark-600;
}

View File

@@ -22,6 +22,8 @@ const loadMessages = async (locale: Locale) => {
return (await import("~/locales/ru/messages")).messages;
case "pl":
return (await import("~/locales/pl/messages")).messages;
case "ptbr":
return (await import("~/locales/ptbr/messages")).messages;
default:
return enMessages;
}

View File

@@ -1,10 +1,17 @@
import { format, isBefore, isSameYear, startOfDay } from "date-fns";
import { HiOutlinePaperClip } from "react-icons/hi";
import { HiBars3BottomLeft, HiChatBubbleLeft } from "react-icons/hi2";
import {
HiBars3BottomLeft,
HiChatBubbleLeft,
HiOutlineClock,
} from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
import Avatar from "~/components/Avatar";
import Badge from "~/components/Badge";
import CircularProgress from "~/components/CircularProgress";
import LabelIcon from "~/components/LabelIcon";
import { useLocalisation } from "~/hooks/useLocalisation";
import { getAvatarUrl } from "~/utils/helpers";
const Card = ({
@@ -15,6 +22,7 @@ const Card = ({
description,
comments,
attachments,
dueDate,
}: {
title: string;
labels: { name: string; colourCode: string | null }[];
@@ -36,7 +44,11 @@ const Card = ({
description: string | null;
comments: { publicId: string }[];
attachments?: { publicId: string }[];
dueDate?: Date | null;
}) => {
const { dateLocale } = useLocalisation();
const showYear = dueDate ? !isSameYear(dueDate, new Date()) : false;
const isOverdue = dueDate ? isBefore(dueDate, startOfDay(new Date())) : false;
const completedItems = checklists.reduce((acc, checklist) => {
return acc + checklist.items.filter((item) => item.completed).length;
}, 0);
@@ -51,6 +63,7 @@ const Card = ({
const hasDescription =
description && description.replace(/<[^>]*>/g, "").trim().length > 0;
const hasAttachments = attachments && attachments.length > 0;
const hasDueDate = !!dueDate;
return (
<div className="flex flex-col rounded-md border border-light-200 bg-light-50 px-3 py-2 text-sm text-neutral-900 dark:border-dark-200 dark:bg-dark-200 dark:text-dark-1000 dark:hover:bg-dark-300">
@@ -60,6 +73,7 @@ const Card = ({
checklists.length > 0 ||
hasDescription ||
comments.length > 0 ||
hasDueDate ||
hasAttachments ? (
<div className="mt-2 flex flex-col justify-end">
<div className="space-x-0.5">
@@ -77,6 +91,23 @@ const Card = ({
<HiBars3BottomLeft className="h-4 w-4" />
</div>
)}
{hasDueDate && dueDate && (
<div
className={twMerge(
"flex items-center gap-1",
isOverdue
? "text-red-600 dark:text-red-400"
: "text-light-800 dark:text-dark-800",
)}
>
<HiOutlineClock className="h-4 w-4" />
<span className="text-[11px]">
{format(dueDate, showYear ? "do MMM yyyy" : "do MMM", {
locale: dateLocale,
})}
</span>
</div>
)}
{comments.length > 0 && (
<div className="flex items-center gap-1 text-light-700 dark:text-dark-800">
<HiChatBubbleLeft className="h-4 w-4" />

View File

@@ -2,6 +2,7 @@ import { useRouter } from "next/router";
import { t } from "@lingui/core/macro";
import {
HiMiniXMark,
HiOutlineClock,
HiOutlineSquare3Stack3D,
HiOutlineTag,
HiOutlineUserCircle,
@@ -60,7 +61,13 @@ const Filters = ({
try {
await router.push({
pathname: router.pathname,
query: { ...router.query, members: [], labels: [], lists: [] },
query: {
...router.query,
members: [],
labels: [],
lists: [],
dueDate: [],
},
});
} catch (error) {
console.error(error);
@@ -99,6 +106,39 @@ const Filters = ({
selected: !!router.query.lists?.includes(list.publicId),
}));
const dueDateItems = [
{
key: "overdue",
value: t`Overdue`,
selected: !!router.query.dueDate?.includes("overdue"),
},
{
key: "today",
value: t`Due today`,
selected: !!router.query.dueDate?.includes("today"),
},
{
key: "tomorrow",
value: t`Due tomorrow`,
selected: !!router.query.dueDate?.includes("tomorrow"),
},
{
key: "next-week",
value: t`Due next week`,
selected: !!router.query.dueDate?.includes("next-week"),
},
{
key: "next-month",
value: t`Due next month`,
selected: !!router.query.dueDate?.includes("next-month"),
},
{
key: "no-due-date",
value: t`No dates`,
selected: !!router.query.dueDate?.includes("no-due-date"),
},
];
const groups = [
...(formattedMembers.length
? [
@@ -126,6 +166,12 @@ const Filters = ({
},
]
: []),
{
key: "dueDate",
label: t`Due date`,
icon: <HiOutlineClock size={16} />,
items: dueDateItems,
},
];
const handleSelect = async (
@@ -156,6 +202,7 @@ const Filters = ({
...formatToArray(router.query.members),
...formatToArray(router.query.labels),
...formatToArray(router.query.lists),
...formatToArray(router.query.dueDate),
].length;
return (

View File

@@ -1,6 +1,7 @@
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { useEffect } from "react";
import { format } from "date-fns";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import {
HiOutlineBarsArrowDown,
@@ -15,6 +16,7 @@ import type { WorkspaceMember } from "~/components/Editor";
import Avatar from "~/components/Avatar";
import Button from "~/components/Button";
import CheckboxDropdown from "~/components/CheckboxDropdown";
import DateSelector from "~/components/DateSelector";
import Editor from "~/components/Editor";
import Input from "~/components/Input";
import LabelIcon from "~/components/LabelIcon";
@@ -27,6 +29,7 @@ import { formatMemberDisplayName, getAvatarUrl } from "~/utils/helpers";
type NewCardFormInput = NewCardInput & {
isCreateAnotherEnabled: boolean;
dueDate?: Date | null;
};
interface QueryParams {
@@ -65,6 +68,7 @@ export function NewCardForm({
memberPublicIds: [],
isCreateAnotherEnabled: false,
position: "start",
dueDate: null,
},
resetOnClose: true,
});
@@ -80,6 +84,8 @@ export function NewCardForm({
const position = watch("position");
const title = watch("title");
const description = watch("description");
const dueDate = watch("dueDate");
const [isDateSelectorOpen, setIsDateSelectorOpen] = useState(false);
// saving form state whenever form values change
useEffect(() => {
@@ -137,6 +143,7 @@ export function NewCardForm({
title: args.title,
listId: 2,
description: "",
dueDate: args.dueDate ?? null,
labels: oldBoard.labels.filter((label) =>
args.labelPublicIds.includes(label.publicId),
),
@@ -193,6 +200,7 @@ export function NewCardForm({
memberPublicIds: [],
isCreateAnotherEnabled,
position,
dueDate: null,
};
reset(newFormState);
saveFormState(newFormState);
@@ -242,7 +250,7 @@ export function NewCardForm({
),
})) ?? [];
const onSubmit = (data: NewCardInput) => {
const onSubmit = (data: NewCardFormInput) => {
createCard.mutate({
title: data.title,
description: data.description,
@@ -250,6 +258,7 @@ export function NewCardForm({
labelPublicIds: data.labelPublicIds,
memberPublicIds: data.memberPublicIds,
position: data.position,
dueDate: data.dueDate ?? null,
});
};
@@ -448,6 +457,44 @@ export function NewCardForm({
</div>
</CheckboxDropdown>
</div>
<div className="relative w-fit">
<button
type="button"
onClick={() => setIsDateSelectorOpen(!isDateSelectorOpen)}
className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-600 bg-light-200 px-2 py-1 text-left text-xs text-light-800 hover:bg-light-300 dark:border-dark-600 dark:bg-dark-400 dark:text-dark-1000 dark:hover:bg-dark-500"
>
{dueDate ? (
<span>{format(dueDate, "MMM d, yyyy")}</span>
) : (
<>{t`Due date`}</>
)}
</button>
{isDateSelectorOpen && (
<>
<div
className="fixed inset-0 z-10"
onClick={() => setIsDateSelectorOpen(false)}
/>
<div
className="absolute left-0 top-full z-20 mt-2 rounded-md border border-light-200 bg-light-50 shadow-lg dark:border-dark-200 dark:bg-dark-100"
onClick={(e) => {
e.stopPropagation();
}}
onMouseDown={(e) => {
e.stopPropagation();
}}
>
<DateSelector
selectedDate={dueDate ?? undefined}
onDateSelect={(date) => {
setValue("dueDate", date ?? null);
setIsDateSelectorOpen(false);
}}
/>
</div>
</>
)}
</div>
<button
onClick={(e) => {
e.preventDefault();

View File

@@ -23,6 +23,8 @@ import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
import { PageHead } from "~/components/PageHead";
import PatternedBackground from "~/components/PatternedBackground";
import { StrictModeDroppable as Droppable } from "~/components/StrictModeDroppable";
import { Tooltip } from "~/components/Tooltip";
import { useKeyboardShortcut } from "~/providers/keyboard-shortcuts";
import { useModal } from "~/providers/modal";
import { usePopup } from "~/providers/popup";
import { useWorkspace } from "~/providers/workspace";
@@ -54,6 +56,15 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
useState<PublicListId>("");
const [isInitialLoading, setIsInitialLoading] = useState(true);
const { tooltipContent: createListShortcutTooltipContent } =
useKeyboardShortcut({
type: "PRESS",
stroke: { key: "C" },
action: () => boardId && openNewListForm(boardId),
description: t`Create new list`,
group: "ACTIONS",
});
const boardId = params?.boardId
? Array.isArray(params.boardId)
? params.boardId[0]
@@ -76,18 +87,26 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
});
};
const queryParams: {
boardPublicId: string;
members: string[];
labels: string[];
lists: string[];
type: "regular" | "template";
} = {
const semanticFilters = formatToArray(router.query.dueDate) as (
| "overdue"
| "today"
| "tomorrow"
| "next-week"
| "next-month"
| "no-due-date"
)[];
const boardType: "regular" | "template" = isTemplate ? "template" : "regular";
const queryParams = {
boardPublicId: boardId ?? "",
members: formatToArray(router.query.members),
labels: formatToArray(router.query.labels),
lists: formatToArray(router.query.lists),
type: isTemplate ? "template" : "regular",
...(semanticFilters.length > 0 && {
dueDateFilters: semanticFilters,
}),
type: boardType,
};
const {
@@ -427,20 +446,22 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
)}
</>
)}
<Button
iconLeft={
<HiOutlinePlusSmall
className="-mr-0.5 h-5 w-5"
aria-hidden="true"
/>
}
onClick={() => {
if (boardId) openNewListForm(boardId);
}}
disabled={!boardData}
>
{t`New list`}
</Button>
<Tooltip content={createListShortcutTooltipContent}>
<Button
iconLeft={
<HiOutlinePlusSmall
className="-mr-0.5 h-5 w-5"
aria-hidden="true"
/>
}
onClick={() => {
if (boardId) openNewListForm(boardId);
}}
disabled={!boardData}
>
{t`New list`}
</Button>
</Tooltip>
<BoardDropdown
isTemplate={!!isTemplate}
isLoading={!boardData}
@@ -554,6 +575,7 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
}
comments={card.comments ?? []}
attachments={card.attachments}
dueDate={card.dueDate ?? null}
/>
</Link>
)}

View File

@@ -6,6 +6,8 @@ import FeedbackModal from "~/components/FeedbackModal";
import Modal from "~/components/modal";
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
import { PageHead } from "~/components/PageHead";
import { Tooltip } from "~/components/Tooltip";
import { useKeyboardShortcut } from "~/providers/keyboard-shortcuts";
import { useModal } from "~/providers/modal";
import { useWorkspace } from "~/providers/workspace";
import { BoardsList } from "./components/BoardsList";
@@ -16,6 +18,15 @@ export default function BoardsPage({ isTemplate }: { isTemplate?: boolean }) {
const { openModal, modalContentType, isOpen } = useModal();
const { workspace } = useWorkspace();
const { tooltipContent: createModalShortcutTooltipContent } =
useKeyboardShortcut({
type: "PRESS",
stroke: { key: "C" },
action: () => openModal("NEW_BOARD"),
description: t`Create new ${isTemplate ? "template" : "board"}`,
group: "ACTIONS",
});
return (
<>
<PageHead
@@ -39,16 +50,18 @@ export default function BoardsPage({ isTemplate }: { isTemplate?: boolean }) {
{t`Import`}
</Button>
)}
<Button
type="button"
variant="primary"
onClick={() => openModal("NEW_BOARD")}
iconLeft={
<HiOutlinePlusSmall aria-hidden="true" className="h-4 w-4" />
}
>
{t`New`}
</Button>
<Tooltip content={createModalShortcutTooltipContent}>
<Button
type="button"
variant="primary"
onClick={() => openModal("NEW_BOARD")}
iconLeft={
<HiOutlinePlusSmall aria-hidden="true" className="h-4 w-4" />
}
>
{t`New`}
</Button>
</Tooltip>
</div>
</div>

View File

@@ -1,11 +1,12 @@
import type { Locale as DateFnsLocale } from "date-fns";
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { formatDistanceToNow } from "date-fns";
import { de, enGB, es, fr, it, nl } from "date-fns/locale";
import { format, formatDistanceToNow, isSameYear } from "date-fns";
import {
HiOutlineArrowLeft,
HiOutlineArrowRight,
HiOutlineCheckCircle,
HiOutlineClock,
HiOutlinePencil,
HiOutlinePlus,
HiOutlineTag,
@@ -24,15 +25,6 @@ import Comment from "./Comment";
type ActivityType =
NonNullable<GetCardByIdOutput>["activities"][number]["type"];
const dateLocaleMap = {
en: enGB,
fr: fr,
de: de,
es: es,
it: it,
nl: nl,
} as const;
const truncate = (value: string | null, maxLength = 50) => {
if (!value) return value;
return value.length > maxLength ? `${value.slice(0, maxLength - 1)}` : value;
@@ -47,6 +39,8 @@ const getActivityText = ({
isSelf,
label,
fromTitle,
toDueDate,
dateLocale,
}: {
type: ActivityType;
toTitle: string | null;
@@ -56,6 +50,9 @@ const getActivityText = ({
isSelf: boolean;
label: string | null;
fromTitle?: string | null;
fromDueDate?: Date | null;
toDueDate?: Date | null;
dateLocale: DateFnsLocale;
}) => {
const ACTIVITY_TYPE_MAP = {
"card.created": t`created the card`,
@@ -74,6 +71,9 @@ const getActivityText = ({
"card.updated.checklist.item.completed": t`completed a checklist item`,
"card.updated.checklist.item.uncompleted": t`marked a checklist item as incomplete`,
"card.updated.checklist.item.deleted": t`deleted a checklist item`,
"card.updated.dueDate.added": t`set the due date`,
"card.updated.dueDate.updated": t`updated the due date`,
"card.updated.dueDate.removed": t`removed the due date`,
} as const;
if (!(type in ACTIVITY_TYPE_MAP)) return null;
@@ -209,6 +209,38 @@ const getActivityText = ({
);
}
if (type === "card.updated.dueDate.added" && toDueDate) {
const showYear = !isSameYear(toDueDate, new Date());
const formattedDate = format(
toDueDate,
showYear ? "do MMM yyyy" : "do MMM",
{ locale: dateLocale },
);
return (
<Trans>
changed the due date to <TextHighlight>{formattedDate}</TextHighlight>
</Trans>
);
}
if (type === "card.updated.dueDate.updated" && toDueDate) {
const showYear = !isSameYear(toDueDate, new Date());
const formattedDate = format(
toDueDate,
showYear ? "do MMM yyyy" : "do MMM",
{ locale: dateLocale },
);
return (
<Trans>
changed the due date to <TextHighlight>{formattedDate}</TextHighlight>
</Trans>
);
}
if (type === "card.updated.dueDate.removed") {
return <Trans>removed the due date</Trans>;
}
return baseText;
};
@@ -229,6 +261,9 @@ const ACTIVITY_ICON_MAP: Partial<Record<ActivityType, React.ReactNode | null>> =
"card.updated.checklist.item.completed": <HiOutlineCheckCircle />,
"card.updated.checklist.item.uncompleted": <HiOutlineCheckCircle />,
"card.updated.checklist.item.deleted": <HiOutlineTrash />,
"card.updated.dueDate.added": <HiOutlineClock />,
"card.updated.dueDate.updated": <HiOutlineClock />,
"card.updated.dueDate.removed": <HiOutlineClock />,
} as const;
const getActivityIcon = (
@@ -260,9 +295,7 @@ const ActivityList = ({
isViewOnly?: boolean;
}) => {
const { data } = authClient.useSession();
const { locale } = useLocalisation();
const currentDateLocale = dateLocaleMap[locale] || enGB;
const { dateLocale } = useLocalisation();
return (
<div className="flex flex-col space-y-4 pt-4">
@@ -276,6 +309,9 @@ const ActivityList = ({
isSelf: activity.member?.user?.id === data?.user.id,
label: activity.label?.name ?? null,
fromTitle: activity.fromTitle ?? null,
fromDueDate: activity.fromDueDate ?? null,
toDueDate: activity.toDueDate ?? null,
dateLocale: dateLocale,
});
if (activity.type === "card.updated.comment.added")
@@ -330,7 +366,7 @@ const ActivityList = ({
<span className="space-x-1 text-light-900 dark:text-dark-800">
{formatDistanceToNow(new Date(activity.createdAt), {
addSuffix: true,
locale: currentDateLocale,
locale: dateLocale,
})}
</span>
</p>

View File

@@ -0,0 +1,141 @@
import { t } from "@lingui/core/macro";
import { format } from "date-fns";
import { useEffect, useState } from "react";
import { HiMiniPlus } from "react-icons/hi2";
import DateSelector from "~/components/DateSelector";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
interface DueDateSelectorProps {
cardPublicId: string;
dueDate: Date | null | undefined;
isLoading?: boolean;
}
export function DueDateSelector({
cardPublicId,
dueDate,
isLoading = false,
}: DueDateSelectorProps) {
const { showPopup } = usePopup();
const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false);
const [pendingDate, setPendingDate] = useState<Date | null | undefined>(
dueDate,
);
// Sync pendingDate with dueDate when it changes externally
useEffect(() => {
if (!isOpen) {
setPendingDate(dueDate);
}
}, [dueDate, isOpen]);
const updateDueDate = api.card.update.useMutation({
onMutate: async (update) => {
await utils.card.byId.cancel();
const previousCard = utils.card.byId.getData({ cardPublicId });
utils.card.byId.setData({ cardPublicId }, (oldCard) => {
if (!oldCard) return oldCard;
return {
...oldCard,
dueDate:
update.dueDate !== undefined
? (update.dueDate as Date | null)
: oldCard.dueDate,
};
});
return { previousCard };
},
onError: (_error, _update, context) => {
utils.card.byId.setData({ cardPublicId }, context?.previousCard);
showPopup({
header: t`Unable to update due date`,
message: t`Please try again later, or contact customer support.`,
icon: "error",
});
},
onSettled: async () => {
await utils.card.byId.invalidate({ cardPublicId });
await utils.board.byId.invalidate();
},
});
const handleDateSelect = (date: Date | undefined) => {
// Only update local state, don't fire mutation
setPendingDate(date ?? null);
};
const handleBackdropClick = () => {
// Only fire mutation if date actually changed
const pendingIsNull = pendingDate === null || pendingDate === undefined;
const dueIsNull = dueDate === null || dueDate === undefined;
let dateChanged = false;
if (pendingIsNull && !dueIsNull) {
dateChanged = true;
} else if (!pendingIsNull && dueIsNull) {
dateChanged = true;
} else if (!pendingIsNull && !dueIsNull) {
// Both are non-null at this point
if (pendingDate instanceof Date && dueDate instanceof Date) {
dateChanged = pendingDate.getTime() !== dueDate.getTime();
}
}
// Close popover immediately
setIsOpen(false);
// Fire mutation if date changed (optimistic update will handle UI)
if (dateChanged) {
updateDueDate.mutate({
cardPublicId,
dueDate: pendingDate ?? null,
});
}
};
return (
<div className="relative flex w-full items-center text-left">
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
disabled={isLoading}
className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 py-1 pl-2 text-left text-xs 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"
>
{dueDate ? (
<span>{format(dueDate, "MMM d, yyyy")}</span>
) : (
<>
<HiMiniPlus size={22} className="pr-2" />
{t`Set due date`}
</>
)}
</button>
{isOpen && (
<>
<div className="fixed inset-0 z-10" onClick={handleBackdropClick} />
<div
className="absolute -left-8 top-full z-20 mt-2 rounded-md border border-light-200 bg-light-50 shadow-lg dark:border-dark-200 dark:bg-dark-100"
onClick={(e) => {
e.stopPropagation();
}}
onMouseDown={(e) => {
e.stopPropagation();
}}
>
<DateSelector
selectedDate={pendingDate ?? undefined}
onDateSelect={handleDateSelect}
/>
</div>
</>
)}
</div>
);
}

View File

@@ -78,7 +78,7 @@ 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">
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 py-1 pl-2 text-left text-xs 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>
</CheckboxDropdown>

View File

@@ -111,7 +111,7 @@ 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">
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-50 py-1 pl-2 text-left text-xs 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 }) => (

View File

@@ -27,6 +27,7 @@ import { DeleteCardConfirmation } from "./components/DeleteCardConfirmation";
import { DeleteChecklistConfirmation } from "./components/DeleteChecklistConfirmation";
import { DeleteCommentConfirmation } from "./components/DeleteCommentConfirmation";
import Dropdown from "./components/Dropdown";
import { DueDateSelector } from "./components/DueDateSelector";
import LabelSelector from "./components/LabelSelector";
import ListSelector from "./components/ListSelector";
import MemberSelector from "./components/MemberSelector";
@@ -124,7 +125,7 @@ export function CardRightPanel({ isTemplate }: { isTemplate?: boolean }) {
/>
</div>
{!isTemplate && (
<div className="flex w-full flex-row">
<div className="mb-4 flex w-full flex-row">
<p className="my-2 mb-2 w-[100px] text-sm font-medium">{t`Members`}</p>
<MemberSelector
cardPublicId={cardId ?? ""}
@@ -133,6 +134,14 @@ export function CardRightPanel({ isTemplate }: { isTemplate?: boolean }) {
/>
</div>
)}
<div className="mb-4 flex w-full flex-row">
<p className="my-2 mb-2 w-[100px] text-sm font-medium">{t`Due date`}</p>
<DueDateSelector
cardPublicId={cardId ?? ""}
dueDate={card?.dueDate}
isLoading={!card}
/>
</div>
</div>
);
}
@@ -147,6 +156,7 @@ export default function CardPage({ isTemplate }: { isTemplate?: boolean }) {
getModalState,
clearModalState,
isOpen,
modalStates,
} = useModal();
const { showPopup } = usePopup();
const { workspace } = useWorkspace();
@@ -183,6 +193,21 @@ export default function CardPage({ isTemplate }: { isTemplate?: boolean }) {
},
});
const addOrRemoveLabel = api.card.addOrRemoveLabel.useMutation({
onError: () => {
showPopup({
header: t`Unable to add label`,
message: t`Please try again later, or contact customer support.`,
icon: "error",
});
},
onSettled: async () => {
if (cardId) {
await utils.card.byId.invalidate({ cardPublicId: cardId });
}
},
});
const { register, handleSubmit, setValue, watch } = useForm<FormValues>({
values: {
cardId: cardId ?? "",
@@ -199,6 +224,24 @@ export default function CardPage({ isTemplate }: { isTemplate?: boolean }) {
});
};
// this adds the new created label to selected labels
useEffect(() => {
const newLabelId = modalStates.NEW_LABEL_CREATED;
if (newLabelId && cardId) {
const isAlreadyAdded = card?.labels.some(
(label) => label.publicId === newLabelId,
);
if (!isAlreadyAdded) {
addOrRemoveLabel.mutate({
cardPublicId: cardId,
labelPublicId: newLabelId,
});
}
clearModalState("NEW_LABEL_CREATED");
}
}, [modalStates.NEW_LABEL_CREATED, card, cardId]);
// Open the new item form after creating a new checklist
useEffect(() => {
if (!card) return;

View File

@@ -1,15 +1,15 @@
import type { FC, SVGProps } from "react";
import { Trans } from "@lingui/react/macro";
import AirbusLogo from "/public/logos/airbus.svg";
import BitwardenLogo from "/public/logos/bitwarden.svg";
import CouchbaseLogo from "/public/logos/couchbase.svg";
import DeloitteLogo from "/public/logos/deloitte.svg";
import FastCompanyLogo from "/public/logos/fast_company.svg";
import LegoLogo from "/public/logos/lego.svg";
import LinkedinLogo from "/public/logos/linkedin.svg";
import SanaLogo from "/public/logos/sana.svg";
import WakamLogo from "/public/logos/wakam.svg";
import AirbusLogo from "~/assets/logos/airbus.svg";
import BitwardenLogo from "~/assets/logos/bitwarden.svg";
import CouchbaseLogo from "~/assets/logos/couchbase.svg";
import DeloitteLogo from "~/assets/logos/deloitte.svg";
import FastCompanyLogo from "~/assets/logos/fast_company.svg";
import LegoLogo from "~/assets/logos/lego.svg";
import LinkedinLogo from "~/assets/logos/linkedin.svg";
import SanaLogo from "~/assets/logos/sana.svg";
import WakamLogo from "~/assets/logos/wakam.svg";
type LogoComponent = FC<SVGProps<SVGSVGElement>>;
@@ -82,7 +82,7 @@ export default function Logos() {
<div className="absolute left-0 top-0 z-10 h-full w-8 bg-gradient-to-r from-white/60 to-transparent dark:from-dark-50" />
<div className="absolute right-0 top-0 z-10 h-full w-8 bg-gradient-to-l from-white/60 to-transparent dark:from-dark-50" />
<div className="animate-scroll flex" style={{ width: "max-content" }}>
<div className="flex animate-scroll" style={{ width: "max-content" }}>
<div className="flex flex-shrink-0 items-center space-x-12">
{logos.map((logo) => {
const LogoComponent: LogoComponent = logo.component;

View File

@@ -67,22 +67,23 @@ export function CardModal({
<div className="flex w-full items-center justify-between">
<button
className="absolute right-[2rem] top-[2rem] rounded p-1 hover:bg-light-300 focus:outline-none dark:hover:bg-dark-300"
onClick={async (e) => {
onClick={(e) => {
e.preventDefault();
closeModal();
setTimeout(async () => {
try {
await router.replace(
`/${workspaceSlug}/${boardSlug}`,
undefined,
{
shallow: true,
setTimeout(() => {
void router.replace(
{
pathname: router.pathname,
query: {
...router.query,
workspaceSlug,
boardSlug: [boardSlug],
},
);
} catch (error) {
console.error(error);
}
},
undefined,
{ shallow: true },
);
}, 400);
}}
>

View File

@@ -38,6 +38,15 @@ export default function PublicBoardView() {
? router.query.workspaceSlug[0]
: router.query.workspaceSlug;
const dueDateFilters = formatToArray(router.query.dueDate) as (
| "overdue"
| "today"
| "tomorrow"
| "next-week"
| "next-month"
| "no-due-date"
)[];
const { data, isLoading } = api.board.bySlug.useQuery(
{
boardSlug: boardSlug ?? "",
@@ -45,6 +54,9 @@ export default function PublicBoardView() {
members: formatToArray(router.query.members),
labels: formatToArray(router.query.labels),
lists: formatToArray(router.query.lists),
...(dueDateFilters.length > 0 && {
dueDateFilters: dueDateFilters,
}),
},
{
enabled: router.isReady && !!boardSlug,
@@ -76,7 +88,8 @@ export default function PublicBoardView() {
);
};
const splitPath = router.asPath.split("/");
const pathWithoutQuery = router.asPath.split("?")[0];
const splitPath = pathWithoutQuery?.split("/") ?? [];
const cardPublicId = splitPath.length > 3 ? splitPath[3] : null;
useEffect(() => {
@@ -172,27 +185,37 @@ export default function PublicBoardView() {
</span>
</div>
<div className="scrollbar-track-rounded-[4px] scrollbar-thumb-rounded-[4px] scrollbar-w-[8px] z-10 h-full max-h-[calc(100vh-265px)] min-h-[2rem] overflow-y-auto pr-1 scrollbar dark:scrollbar-track-dark-100 dark:scrollbar-thumb-dark-600">
{list.cards.map((card) => (
<Link
key={card.publicId}
href={`/${data.workspace.slug}/${data.slug}/${card.publicId}`}
className={`mb-2 flex !cursor-pointer flex-col`}
shallow={true}
onClick={() => {
openModal("CARD");
}}
>
<Card
title={card.title}
labels={card.labels}
checklists={card.checklists ?? []}
members={[]}
description={card.description}
comments={card.comments ?? []}
attachments={card.attachments}
/>
</Link>
))}
{list.cards.map((card) => {
return (
<Link
key={card.publicId}
href={{
pathname: router.pathname,
query: {
...router.query,
workspaceSlug: data.workspace.slug,
boardSlug: [data.slug, card.publicId],
},
}}
className={`mb-2 flex !cursor-pointer flex-col`}
shallow={true}
onClick={() => {
openModal("CARD");
}}
>
<Card
title={card.title}
labels={card.labels}
checklists={card.checklists ?? []}
members={[]}
description={card.description}
comments={card.comments ?? []}
attachments={card.attachments}
dueDate={card.dueDate ?? null}
/>
</Link>
);
})}
</div>
</div>
))}

View File

@@ -35,8 +35,12 @@ services:
- SMTP_PASSWORD=${SMTP_PASSWORD}
- SMTP_SECURE=${SMTP_SECURE}
- EMAIL_FROM=${EMAIL_FROM}
- PLUNK_API_KEY=${PLUNK_API_KEY}
- PLUNK_API_URL=${PLUNK_API_URL}
- SMTP_REJECT_UNAUTHORIZED=${SMTP_REJECT_UNAUTHORIZED}
# Notifications
- NOVU_API_KEY=${NOVU_API_KEY}
- DISCORD_WEBHOOK_URL=${DISCORD_WEBHOOK_URL}
- EMAIL_UNSUBSCRIBE_SECRET=${EMAIL_UNSUBSCRIBE_SECRET}
# S3 storage
- S3_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID}
@@ -61,6 +65,7 @@ services:
- BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS}
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
- BETTER_AUTH_ALLOWED_DOMAINS=${BETTER_AUTH_ALLOWED_DOMAINS}
# Analytics
- NEXT_PUBLIC_UMAMI_ID=${NEXT_PUBLIC_UMAMI_ID}

View File

@@ -24,6 +24,7 @@ services:
- SMTP_USER=${SMTP_USER}
- SMTP_PASSWORD=${SMTP_PASSWORD}
- EMAIL_FROM=${EMAIL_FROM}
- SMTP_REJECT_UNAUTHORIZED=${SMTP_REJECT_UNAUTHORIZED}
# Disable email features entirely (optional)
- NEXT_PUBLIC_DISABLE_EMAIL=${NEXT_PUBLIC_DISABLE_EMAIL}
@@ -59,6 +60,7 @@ services:
- BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS}
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
- BETTER_AUTH_ALLOWED_DOMAINS=${BETTER_AUTH_ALLOWED_DOMAINS}
- DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID}
- DISCORD_CLIENT_SECRET=${DISCORD_CLIENT_SECRET}
- GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}

View File

@@ -9,7 +9,7 @@ import { generateUID } from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import { generateUploadUrl } from "../utils/s3";
import { deleteObject, generateUploadUrl } from "../utils/s3";
export const attachmentRouter = createTRPCRouter({
generateUploadUrl: protectedProcedure
@@ -189,6 +189,18 @@ export const attachmentRouter = createTRPCRouter({
await assertUserInWorkspace(ctx.db, userId, workspaceId);
const bucket = process.env.NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME;
if (bucket) {
try {
await deleteObject(bucket, attachment.s3Key);
} catch (error) {
console.error(
`Failed to delete attachment from S3: ${attachment.s3Key}`,
error,
);
}
}
await cardAttachmentRepo.softDelete(ctx.db, {
attachmentId: attachment.id,
deletedAt: new Date(),

View File

@@ -8,7 +8,11 @@ import * as labelRepo from "@kan/db/repository/label.repo";
import * as listRepo from "@kan/db/repository/list.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { colours } from "@kan/shared/constants";
import { generateSlug, generateUID } from "@kan/shared/utils";
import {
convertDueDateFiltersToRanges,
generateSlug,
generateUID,
} from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
@@ -79,6 +83,18 @@ export const boardRouter = createTRPCRouter({
members: z.array(z.string().min(12)).optional(),
labels: z.array(z.string().min(12)).optional(),
lists: z.array(z.string().min(12)).optional(),
dueDateFilters: z
.array(
z.enum([
"overdue",
"today",
"tomorrow",
"next-week",
"next-month",
"no-due-date",
]),
)
.optional(),
type: z.enum(["regular", "template"]).optional(),
}),
)
@@ -105,6 +121,11 @@ export const boardRouter = createTRPCRouter({
await assertUserInWorkspace(ctx.db, userId, board.workspaceId);
// Convert semantic string filters to date ranges expected by the repo
const dueDateFilters = input.dueDateFilters
? convertDueDateFiltersToRanges(input.dueDateFilters)
: [];
const result = await boardRepo.getByPublicId(
ctx.db,
input.boardPublicId,
@@ -112,6 +133,7 @@ export const boardRouter = createTRPCRouter({
members: input.members ?? [],
labels: input.labels ?? [],
lists: input.lists ?? [],
dueDate: dueDateFilters,
type: input.type,
},
);
@@ -145,6 +167,18 @@ export const boardRouter = createTRPCRouter({
members: z.array(z.string().min(12)).optional(),
labels: z.array(z.string().min(12)).optional(),
lists: z.array(z.string().min(12)).optional(),
dueDateFilters: z
.array(
z.enum([
"overdue",
"today",
"tomorrow",
"next-week",
"next-month",
"no-due-date",
]),
)
.optional(),
}),
)
.output(z.custom<Awaited<ReturnType<typeof boardRepo.getBySlug>>>())
@@ -160,6 +194,11 @@ export const boardRouter = createTRPCRouter({
code: "NOT_FOUND",
});
// Convert semantic string filters to date ranges expected by the repo
const dueDateFilters = input.dueDateFilters
? convertDueDateFiltersToRanges(input.dueDateFilters)
: [];
const result = await boardRepo.getBySlug(
ctx.db,
input.boardSlug,
@@ -168,6 +207,7 @@ export const boardRouter = createTRPCRouter({
members: input.members ?? [],
labels: input.labels ?? [],
lists: input.lists ?? [],
dueDate: dueDateFilters,
},
);
@@ -239,6 +279,7 @@ export const boardRouter = createTRPCRouter({
members: [],
labels: [],
lists: [],
dueDate: [],
type: sourceBoardInfo.type,
},
);
@@ -281,12 +322,6 @@ export const boardRouter = createTRPCRouter({
sourceBoardId: sourceBoardInfo.id,
});
if (!result)
throw new TRPCError({
message: `Failed to create board from source`,
code: "INTERNAL_SERVER_ERROR",
});
return result;
}

View File

@@ -32,6 +32,7 @@ export const cardRouter = createTRPCRouter({
labelPublicIds: z.array(z.string().min(12)),
memberPublicIds: z.array(z.string().min(12)),
position: z.enum(["start", "end"]),
dueDate: z.date().nullable().optional(),
}),
)
.output(z.custom<Awaited<ReturnType<typeof cardRepo.create>>>())
@@ -69,6 +70,7 @@ export const cardRouter = createTRPCRouter({
createdBy: userId,
listId: list.id,
position: input.position,
dueDate: input.dueDate ?? null,
});
const newCardId = newCard.id;
@@ -686,6 +688,7 @@ export const cardRouter = createTRPCRouter({
description: z.string().optional(),
index: z.number().optional(),
listPublicId: z.string().min(12).optional(),
dueDate: z.date().nullable().optional(),
}),
)
.output(z.custom<Awaited<ReturnType<typeof cardRepo.update>>>())
@@ -746,15 +749,19 @@ export const cardRouter = createTRPCRouter({
title: string;
description: string | null;
publicId: string;
dueDate: Date | null;
}
| undefined;
if (input.title || input.description) {
const previousDueDate = existingCard.dueDate;
if (input.title || input.description || input.dueDate !== undefined) {
result = await cardRepo.update(
ctx.db,
{
...(input.title && { title: input.title }),
...(input.description && { description: input.description }),
...(input.dueDate !== undefined && { dueDate: input.dueDate }),
},
{ cardPublicId: input.cardPublicId },
);
@@ -796,6 +803,32 @@ export const cardRouter = createTRPCRouter({
});
}
if (
input.dueDate !== undefined &&
previousDueDate?.getTime() !== input.dueDate?.getTime()
) {
let activityType:
| "card.updated.dueDate.added"
| "card.updated.dueDate.updated"
| "card.updated.dueDate.removed";
if (!previousDueDate) {
activityType = "card.updated.dueDate.added";
} else if (!input.dueDate) {
activityType = "card.updated.dueDate.removed";
} else {
activityType = "card.updated.dueDate.updated";
}
activities.push({
type: activityType,
cardId: result.id,
createdBy: userId,
fromDueDate: previousDueDate ?? undefined,
toDueDate: input.dueDate ?? undefined,
});
}
if (newListId && existingCard.listId !== newListId) {
activities.push({
type: "card.updated.list" as const,

View File

@@ -1,4 +1,5 @@
import { TRPCError } from "@trpc/server";
import { env } from "next-runtime-env";
import { z } from "zod";
import * as inviteLinkRepo from "@kan/db/repository/inviteLink.repo";
@@ -307,7 +308,7 @@ export const memberRouter = createTRPCRouter({
return {
id: activeInviteLink.id,
inviteCode: activeInviteLink.code,
inviteLink: `${process.env.NEXT_PUBLIC_BASE_URL}/invite/${activeInviteLink.code}`,
inviteLink: `${env("NEXT_PUBLIC_BASE_URL")}/invite/${activeInviteLink.code}`,
isActive: true,
expiresAt: activeInviteLink.expiresAt ?? undefined,
};
@@ -415,7 +416,7 @@ export const memberRouter = createTRPCRouter({
return {
publicId: inviteLink.publicId,
inviteCode: inviteLink.code,
inviteLink: `${process.env.NEXT_PUBLIC_BASE_URL}/invite/${inviteLink.code}`,
inviteLink: `${env("NEXT_PUBLIC_BASE_URL")}/invite/${inviteLink.code}`,
expiresAt: inviteLink.expiresAt,
};
}),
@@ -476,7 +477,7 @@ export const memberRouter = createTRPCRouter({
openapi: {
summary: "Get invite information by code",
method: "GET",
path: "/workspaces/{workspacePublicId}/invites/{inviteCode}",
path: "/invites/{inviteCode}",
description: "Get invite information by invite code",
tags: ["Invites"],
protect: false,
@@ -521,7 +522,7 @@ export const memberRouter = createTRPCRouter({
openapi: {
summary: "Accept an invite link",
method: "POST",
path: "/workspaces/{workspacePublicId}/invites/accept",
path: "/invites/accept",
description: "Accepts an invitation via invite link",
tags: ["Invites"],
protect: false,

View File

@@ -1,4 +1,5 @@
import {
DeleteObjectCommand,
GetObjectCommand,
PutObjectCommand,
S3Client,
@@ -6,14 +7,19 @@ import {
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
export function createS3Client() {
const credentials =
process.env.S3_ACCESS_KEY_ID && process.env.S3_SECRET_ACCESS_KEY
? {
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
}
: undefined;
return new S3Client({
region: process.env.S3_REGION ?? "",
endpoint: process.env.S3_ENDPOINT ?? "",
forcePathStyle: process.env.S3_FORCE_PATH_STYLE === "true",
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID ?? "",
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY ?? "",
},
credentials,
});
}
@@ -51,3 +57,13 @@ export async function generateDownloadUrl(
{ expiresIn },
);
}
export async function deleteObject(bucket: string, key: string) {
const client = createS3Client();
await client.send(
new DeleteObjectCommand({
Bucket: bucket,
Key: key,
}),
);
}

View File

@@ -37,7 +37,7 @@
},
"prettier": "@kan/prettier-config",
"dependencies": {
"@better-auth/stripe": "^1.3.7",
"better-auth": "^1.3.7"
"@better-auth/stripe": "^1.4.6",
"better-auth": "^1.4.6"
}
}

View File

@@ -1,5 +1,8 @@
import type { Subscription } from "@better-auth/stripe";
import type Stripe from "stripe";
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { stripe } from "@better-auth/stripe";
import { ChatOrPushProviderEnum } from "@novu/api/models/components";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { createAuthEndpoint, createAuthMiddleware } from "better-auth/api";
@@ -14,7 +17,8 @@ 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 * as schema from "@kan/db/schema";
import { cloudMailerClient, sendEmail } from "@kan/email";
import { notificationClient, sendEmail } from "@kan/email";
import { createEmailUnsubscribeLink } from "@kan/shared";
import { createStripeClient } from "@kan/stripe";
export const configuredProviders = socialProviderList.reduce<
@@ -28,6 +32,9 @@ export const configuredProviders = socialProviderList.reduce<
requireSelectAccount?: boolean;
clientKey?: string;
issuer?: string;
// Google-specific optional hints
hostedDomain?: string;
hd?: string;
}
>
>((acc, provider) => {
@@ -65,6 +72,21 @@ export const configuredProviders = socialProviderList.reduce<
acc[provider].tenantId = "common";
acc[provider].requireSelectAccount = true;
}
// Add Google domain hint if allowed domains is configured
if (
provider === "google" &&
Object.keys(acc).includes("google") &&
acc[provider]
) {
const allowed = process.env.BETTER_AUTH_ALLOWED_DOMAINS?.split(",")
.map((d) => d.trim().toLowerCase())
.filter(Boolean);
if (allowed && allowed.length > 0) {
// Use the first domain as an authorization hint
acc[provider].hostedDomain = allowed[0];
acc[provider].hd = allowed[0];
}
}
if (
provider === "tiktok" &&
Object.keys(acc).includes("tiktok") &&
@@ -178,19 +200,17 @@ export const initAuth = (db: dbClient) => {
freeTrial: {
days: 14,
onTrialStart: async (subscription) => {
// Called when a trial starts
// @todo: send trial start email
// await sendTrialStartEmail(subscription.referenceId);
await triggerWorkflow(db, "trial-start", subscription);
},
onTrialEnd: async ({ subscription }, request) => {
// Called when a trial ends
// @todo: send trial end email
// await sendTrialEndEmail(user.email);
onTrialEnd: async ({ subscription }) => {
await triggerWorkflow(db, "trial-end", subscription);
},
onTrialExpired: async (subscription) => {
// Called when a trial expires without conversion
// @todo: send trial expired email
// await sendTrialExpiredEmail(subscription.referenceId);
await triggerWorkflow(
db,
"trial-expired",
subscription,
);
},
},
},
@@ -202,19 +222,17 @@ export const initAuth = (db: dbClient) => {
freeTrial: {
days: 14,
onTrialStart: async (subscription) => {
// Called when a trial starts
// @todo: send trial start email
// await sendTrialStartEmail(subscription.referenceId);
await triggerWorkflow(db, "trial-start", subscription);
},
onTrialEnd: async ({ subscription }, request) => {
// Called when a trial ends
// @todo: send trial end email
// await sendTrialEndEmail(user.email);
onTrialEnd: async ({ subscription }) => {
await triggerWorkflow(db, "trial-end", subscription);
},
onTrialExpired: async (subscription) => {
// Called when a trial expires without conversion
// @todo: send trial expired email
// await sendTrialExpiredEmail(subscription.referenceId);
await triggerWorkflow(
db,
"trial-expired",
subscription,
);
},
},
},
@@ -280,9 +298,8 @@ export const initAuth = (db: dbClient) => {
}),
]
: []),
// @todo: hasing is disabled due to a bug in the api key plugin
apiKey({
disableKeyHashing: true,
enableSessionForAPIKeys: true,
rateLimit: {
enabled: true,
timeWindow: 1000 * 60, // 1 minute
@@ -343,35 +360,40 @@ export const initAuth = (db: dbClient) => {
return Promise.resolve(false);
}
return Promise.resolve(true);
// Fall through to any additional checks below
}
// Enforce allowed domains (OIDC/social) if configured
const allowed = process.env.BETTER_AUTH_ALLOWED_DOMAINS?.split(",")
.map((d) => d.trim().toLowerCase())
.filter(Boolean);
if (allowed && allowed.length > 0) {
const domain = user.email.split("@")[1]?.toLowerCase();
if (!domain || !allowed.includes(domain)) {
return Promise.resolve(false);
}
}
return Promise.resolve(true);
},
async after(user) {
if (cloudMailerClient) {
await cloudMailerClient.events.track({
event: "user-signup",
email: user.email,
data: {
name: user.name,
userId: user.id,
},
});
}
let avatarKey = user.image;
if (
user.image &&
!user.image.includes(process.env.NEXT_PUBLIC_STORAGE_DOMAIN!)
) {
try {
const credentials =
env("S3_ACCESS_KEY_ID") && env("S3_SECRET_ACCESS_KEY")
? {
accessKeyId: env("S3_ACCESS_KEY_ID")!,
secretAccessKey: env("S3_SECRET_ACCESS_KEY")!,
}
: undefined;
const client = new S3Client({
region: env("S3_REGION") ?? "",
endpoint: env("S3_ENDPOINT") ?? "",
forcePathStyle: env("S3_FORCE_PATH_STYLE") === "true",
credentials: {
accessKeyId: env("S3_ACCESS_KEY_ID") ?? "",
secretAccessKey: env("S3_SECRET_ACCESS_KEY") ?? "",
},
credentials,
});
const allowedFileExtensions = ["jpg", "jpeg", "png", "webp"];
@@ -391,6 +413,9 @@ export const initAuth = (db: dbClient) => {
ACL: "public-read",
}),
);
avatarKey = key;
await userRepo.update(db, user.id, {
image: key,
});
@@ -398,6 +423,58 @@ export const initAuth = (db: dbClient) => {
console.error(error);
}
}
if (notificationClient) {
try {
const [firstName, ...rest] = user.name
.split(" ")
.filter(Boolean);
const lastName = rest.length ? rest.join(" ") : undefined;
const avatarUrl = avatarKey
? `${env("NEXT_PUBLIC_STORAGE_URL")}/${env("NEXT_PUBLIC_AVATAR_BUCKET_NAME")}/${avatarKey}`
: undefined;
const unsubscribeUrl = await createEmailUnsubscribeLink(
user.id,
);
await notificationClient.trigger({
to: {
subscriberId: user.id,
firstName: firstName,
lastName: lastName,
email: user.email,
avatar: avatarUrl,
data: {
emailVerified: user.emailVerified,
stripeCustomerId: user.stripeCustomerId,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
},
},
payload: {
emailUnsubscribeUrl: unsubscribeUrl,
},
workflowId: "user-signup",
});
await notificationClient.subscribers.credentials.update(
{
providerId: ChatOrPushProviderEnum.Discord,
credentials: {
webhookUrl: process.env.DISCORD_WEBHOOK_URL!,
},
integrationIdentifier: "discord",
},
user.id,
);
} catch (error) {
console.error(
"Error adding user to notification client",
error,
);
}
}
},
},
},
@@ -435,3 +512,37 @@ export const initAuth = (db: dbClient) => {
},
});
};
async function triggerWorkflow(
db: dbClient,
workflowId: string,
subscription: Subscription,
cancellationDetails?: Stripe.Subscription.CancellationDetails | null,
) {
try {
if (!subscription.stripeCustomerId || !notificationClient) return;
const user = await userRepo.getByStripeCustomerId(
db,
subscription.stripeCustomerId,
);
if (!user || !notificationClient) return;
const unsubscribeUrl = await createEmailUnsubscribeLink(user.id);
await notificationClient.trigger({
to: {
subscriberId: user.id,
},
payload: {
...subscription,
cancellationDetails,
emailUnsubscribeUrl: unsubscribeUrl,
},
workflowId,
});
} catch (error) {
console.error("Error triggering workflow", error);
}
}

View File

@@ -0,0 +1,6 @@
ALTER TYPE "public"."card_activity_type" ADD VALUE 'card.updated.dueDate.added' BEFORE 'card.archived';--> statement-breakpoint
ALTER TYPE "public"."card_activity_type" ADD VALUE 'card.updated.dueDate.updated' BEFORE 'card.archived';--> statement-breakpoint
ALTER TYPE "public"."card_activity_type" ADD VALUE 'card.updated.dueDate.removed' BEFORE 'card.archived';--> statement-breakpoint
ALTER TABLE "card_activity" ADD COLUMN "fromDueDate" timestamp;--> statement-breakpoint
ALTER TABLE "card_activity" ADD COLUMN "toDueDate" timestamp;--> statement-breakpoint
ALTER TABLE "card" ADD COLUMN "dueDate" timestamp;

File diff suppressed because it is too large Load Diff

View File

@@ -141,6 +141,13 @@
"when": 1762807463569,
"tag": "20251110204423_AddCardAttachments",
"breakpoints": true
},
{
"idx": 20,
"version": "7",
"when": 1764621815672,
"tag": "20251201204335_AddCardDueDates",
"breakpoints": true
}
]
}

View File

@@ -1,4 +1,15 @@
import { and, asc, desc, eq, inArray, isNull, or } from "drizzle-orm";
import {
and,
asc,
desc,
eq,
gte,
inArray,
isNotNull,
isNull,
lt,
or,
} from "drizzle-orm";
import type { dbClient } from "@kan/db/client";
import type { BoardVisibilityStatus } from "@kan/db/schema";
@@ -65,6 +76,39 @@ export const getIdByPublicId = async (db: dbClient, boardPublicId: string) => {
return board;
};
interface DueDateFilter {
startDate?: Date;
endDate?: Date;
hasNoDueDate?: boolean;
}
const buildDueDateWhere = (filters: DueDateFilter[]) => {
if (!filters.length) return undefined;
const clauses = filters
.map((filter) => {
const conditions: ReturnType<typeof and>[] = [];
if (filter.hasNoDueDate) {
conditions.push(isNull(cards.dueDate));
} else {
conditions.push(isNotNull(cards.dueDate));
if (filter.startDate)
conditions.push(gte(cards.dueDate, filter.startDate));
if (filter.endDate) conditions.push(lt(cards.dueDate, filter.endDate));
}
return conditions.length > 0 ? and(...conditions) : undefined;
})
.filter((clause): clause is NonNullable<typeof clause> => !!clause);
if (!clauses.length) return undefined;
return or(...clauses);
};
export const getByPublicId = async (
db: dbClient,
boardPublicId: string,
@@ -72,6 +116,7 @@ export const getByPublicId = async (
members: string[];
labels: string[];
lists: string[];
dueDate: DueDateFilter[];
type: "regular" | "template" | undefined;
},
) => {
@@ -164,6 +209,7 @@ export const getByPublicId = async (
description: true,
listId: true,
index: true,
dueDate: true,
},
with: {
labels: {
@@ -236,6 +282,7 @@ export const getByPublicId = async (
where: and(
cardIds.length > 0 ? inArray(cards.publicId, cardIds) : undefined,
isNull(cards.deletedAt),
buildDueDateWhere(filters.dueDate),
),
orderBy: [asc(cards.index)],
},
@@ -291,6 +338,7 @@ export const getBySlug = async (
members: string[];
labels: string[];
lists: string[];
dueDate: DueDateFilter[];
},
) => {
let cardIds: string[] = [];
@@ -353,6 +401,7 @@ export const getBySlug = async (
description: true,
listId: true,
index: true,
dueDate: true,
},
with: {
labels: {
@@ -405,6 +454,7 @@ export const getBySlug = async (
where: and(
cardIds.length > 0 ? inArray(cards.publicId, cardIds) : undefined,
isNull(cards.deletedAt),
buildDueDateWhere(filters.dueDate),
),
orderBy: [asc(cards.index)],
},

View File

@@ -23,6 +23,7 @@ export const create = async (
createdBy: string;
listId: number;
position: "start" | "end";
dueDate?: Date | null;
},
) => {
return db.transaction(async (tx) => {
@@ -71,6 +72,7 @@ export const create = async (
createdBy: cardInput.createdBy,
listId: cardInput.listId,
index: index,
dueDate: cardInput.dueDate ?? null,
})
.returning({ id: cards.id, listId: cards.listId });
@@ -163,6 +165,7 @@ export const update = async (
cardInput: {
title?: string;
description?: string;
dueDate?: Date | null;
},
args: {
cardPublicId: string;
@@ -173,6 +176,8 @@ export const update = async (
.set({
title: cardInput.title,
description: cardInput.description,
dueDate: cardInput.dueDate !== undefined ? cardInput.dueDate : undefined,
updatedAt: new Date(),
})
.where(and(eq(cards.publicId, args.cardPublicId), isNull(cards.deletedAt)))
.returning({
@@ -180,6 +185,7 @@ export const update = async (
publicId: cards.publicId,
title: cards.title,
description: cards.description,
dueDate: cards.dueDate,
});
return result;
@@ -214,6 +220,7 @@ export const getByPublicId = (db: dbClient, cardPublicId: string) => {
title: true,
description: true,
listId: true,
dueDate: true,
},
where: eq(cards.publicId, cardPublicId),
});
@@ -397,6 +404,7 @@ export const getWithListAndMembersByPublicId = async (
publicId: true,
title: true,
description: true,
dueDate: true,
},
with: {
labels: {
@@ -531,6 +539,8 @@ export const getWithListAndMembersByPublicId = async (
toTitle: true,
fromDescription: true,
toDescription: true,
fromDueDate: true,
toDueDate: true,
},
with: {
fromList: {
@@ -780,6 +790,7 @@ export const reorder = async (
publicId: true,
title: true,
description: true,
dueDate: true,
},
where: eq(cards.id, card.id),
});

View File

@@ -22,6 +22,8 @@ export const create = async (
commentId?: number;
fromComment?: string;
toComment?: string;
fromDueDate?: Date;
toDueDate?: Date;
sourceBoardId?: number;
},
) => {
@@ -45,6 +47,8 @@ export const create = async (
commentId: activityInput.commentId,
fromComment: activityInput.fromComment,
toComment: activityInput.toComment,
fromDueDate: activityInput.fromDueDate,
toDueDate: activityInput.toDueDate,
sourceBoardId: activityInput.sourceBoardId,
})
.returning({ id: cardActivities.id });
@@ -68,6 +72,8 @@ export const bulkCreate = async (
fromDescription?: string;
toDescription?: string;
createdBy: string;
fromDueDate?: Date;
toDueDate?: Date;
sourceBoardId?: number;
}[],
) => {

View File

@@ -28,6 +28,15 @@ export const getById = async (db: dbClient, userId: string) => {
});
};
export const getByStripeCustomerId = async (
db: dbClient,
stripeCustomerId: string,
) => {
return await db.query.users.findFirst({
where: eq(users.stripeCustomerId, stripeCustomerId),
});
};
export const getByEmail = (db: dbClient, email: string) => {
return db.query.users.findFirst({
columns: {

View File

@@ -44,6 +44,9 @@ export const activityTypes = [
"card.updated.checklist.item.deleted",
"card.updated.attachment.added",
"card.updated.attachment.removed",
"card.updated.dueDate.added",
"card.updated.dueDate.updated",
"card.updated.dueDate.removed",
"card.archived",
] as const;
@@ -70,6 +73,7 @@ export const cards = pgTable("card", {
.notNull()
.references(() => lists.id, { onDelete: "cascade" }),
importId: bigint("importId", { mode: "number" }).references(() => imports.id),
dueDate: timestamp("dueDate"),
}).enableRLS();
export const cardsRelations = relations(cards, ({ one, many }) => ({
@@ -137,6 +141,8 @@ export const cardActivities = pgTable("card_activity", {
),
fromComment: text("fromComment"),
toComment: text("toComment"),
fromDueDate: timestamp("fromDueDate"),
toDueDate: timestamp("toDueDate"),
sourceBoardId: bigint("sourceBoardId", { mode: "number" }).references(
() => boards.id,
{ onDelete: "set null" },

View File

@@ -23,10 +23,10 @@
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
"@plunk/node": "^3.0.3",
"@react-email/components": "^0.0.25",
"@novu/api": "^3.11.0",
"@react-email/components": "^1.0.1",
"nodemailer": "^7.0.3",
"react-email": "^3.0.1"
"react-email": "^5.0.6"
},
"devDependencies": {
"@kan/eslint-config": "workspace:*",

View File

@@ -1,10 +0,0 @@
import Plunk from "@plunk/node";
export const cloudMailerClient =
process.env.NEXT_PUBLIC_KAN_ENV === "cloud" &&
process.env.PLUNK_API_KEY &&
process.env.PLUNK_API_URL
? new Plunk(process.env.PLUNK_API_KEY ?? "", {
baseUrl: process.env.PLUNK_API_URL,
})
: null;

View File

@@ -1,4 +1,4 @@
export const name = "email";
export { sendEmail } from "./sendEmail";
export { cloudMailerClient } from "./cloudMailerClient";
export { notificationClient } from "./notificationClient";

View File

@@ -0,0 +1,6 @@
import { Novu } from "@novu/api";
export const notificationClient =
process.env.NEXT_PUBLIC_KAN_ENV === "cloud" && process.env.NOVU_API_KEY
? new Novu({ secretKey: process.env.NOVU_API_KEY })
: null;

View File

@@ -1,7 +1,6 @@
import { render } from "@react-email/render";
import nodemailer from "nodemailer";
import { cloudMailerClient } from "./cloudMailerClient";
import JoinWorkspaceTemplate from "./templates/join-workspace";
import MagicLinkTemplate from "./templates/magic-link";
import ResetPasswordTemplate from "./templates/reset-password";
@@ -17,7 +16,17 @@ const emailTemplates: Record<Templates, React.FC> = {
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: process.env.SMTP_SECURE !== "false",
secure:
process.env.SMTP_SECURE === undefined
? true
: process.env.SMTP_SECURE?.toLowerCase() === "true",
tls: {
// do not fail on invalid certs
rejectUnauthorized:
process.env.SMTP_REJECT_UNAUTHORIZED === undefined
? true
: process.env.SMTP_REJECT_UNAUTHORIZED?.toLowerCase() === "true",
},
...(process.env.SMTP_USER &&
process.env.SMTP_PASSWORD && {
auth: {
@@ -45,15 +54,6 @@ export const sendEmail = async (
html,
};
// if (cloudMailerClient) {
// const response = await cloudMailerClient.emails.send({
// ...options,
// body: html,
// });
// return response;
// }
const response = await transporter.sendMail(options);
if (!response.accepted.length) {

View File

@@ -36,6 +36,9 @@
},
"prettier": "@kan/prettier-config",
"dependencies": {
"nanoid": "^5.0.9"
"date-fns": "^4.1.0",
"jose": "^6.1.2",
"nanoid": "^5.0.9",
"next-runtime-env": "^1.7.2"
}
}

View File

@@ -0,0 +1,63 @@
import { addDays, endOfDay, startOfDay } from "date-fns";
export type DueDateFilterKey =
| "overdue"
| "today"
| "tomorrow"
| "next-week"
| "next-month"
| "no-due-date";
export interface DueDateFilter {
startDate?: Date;
endDate?: Date;
hasNoDueDate?: boolean;
}
export const convertDueDateFiltersToRanges = (
filters: DueDateFilterKey[],
): DueDateFilter[] => {
if (!filters.length) return [];
const today = startOfDay(new Date());
const tomorrow = addDays(today, 1);
const nextWeekEnd = addDays(today, 8);
const nextMonthEnd = addDays(today, 31);
return filters.map((filter) => {
switch (filter) {
case "overdue":
return {
endDate: today,
};
case "today":
return {
startDate: today,
endDate: endOfDay(today),
};
case "tomorrow":
return {
startDate: tomorrow,
endDate: endOfDay(tomorrow),
};
case "next-week": {
return {
startDate: today,
endDate: nextWeekEnd,
};
}
case "next-month": {
return {
startDate: nextWeekEnd,
endDate: nextMonthEnd,
};
}
case "no-due-date":
return {
hasNoDueDate: true,
};
default:
return {};
}
});
};

View File

@@ -0,0 +1,32 @@
import { SignJWT } from "jose";
import { env } from "next-runtime-env";
const encoder = new TextEncoder();
/**
* Creates a longlived unsubscribe link for a given user/subscriber.
*
* `${NEXT_PUBLIC_BASE_URL}/unsubscribe?token=<jwt>`
*
* The JWT payload only contains the subscriberId. There is no expiry
* on purpose unsubscribe links should remain valid indefinitely.
*
*/
export async function createEmailUnsubscribeLink(
userId: string,
): Promise<string | null> {
const baseUrl = env("NEXT_PUBLIC_BASE_URL");
const secret = process.env.EMAIL_UNSUBSCRIBE_SECRET;
if (!baseUrl || !secret) {
// Environment not configured for unsubscribe links.
return null;
}
const token = await new SignJWT({ subscriberId: userId })
.setProtectedHeader({ alg: "HS256" })
// No expiration on purpose; unsubscribe links are longlived.
.sign(encoder.encode(secret));
return `${baseUrl}/unsubscribe?token=${encodeURIComponent(token)}`;
}

View File

@@ -1,3 +1,5 @@
export * from "./generateUID";
export * from "./generateSlug";
export * from "./subscriptions";
export * from "./email";
export * from "./dueDateFilters";

View File

@@ -71,4 +71,13 @@ export const updateSubscriptionSeats = async (
return updatedSubscription;
};
export const getCancellationDetails = async (
stripeSubscriptionId: string,
): Promise<Stripe.Subscription.CancellationDetails | null> => {
const stripe = createStripeClient();
const stripeSubscription =
await stripe.subscriptions.retrieve(stripeSubscriptionId);
return stripeSubscription.cancellation_details ?? null;
};
export { createStripeClient };

2644
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,7 @@
"TRELLO_APP_SECRET",
"GOOGLE_CLIENT_ID",
"GOOGLE_CLIENT_SECRET",
"BETTER_AUTH_ALLOWED_DOMAINS",
"DISCORD_CLIENT_ID",
"DISCORD_CLIENT_SECRET",
"GITHUB_CLIENT_ID",
@@ -96,9 +97,11 @@
"SMTP_USER",
"SMTP_PASSWORD",
"SMTP_SECURE",
"SMTP_REJECT_UNAUTHORIZED",
"NEXT_PUBLIC_KAN_ENV",
"NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY",
"STRIPE_SECRET_KEY",
"DISCORD_WEBHOOK_URL",
"STRIPE_WEBHOOK_SECRET",
"STRIPE_WEBHOOK_SECRET_LEGACY",
"STRIPE_PRO_PLAN_MONTHLY_PRICE_ID",
@@ -120,8 +123,8 @@
"PORT",
"BETTER_AUTH_SECRET",
"BETTER_AUTH_TRUSTED_ORIGINS",
"PLUNK_API_KEY",
"PLUNK_API_URL"
"NOVU_API_KEY",
"EMAIL_UNSUBSCRIBE_SECRET"
],
"globalPassThroughEnv": [
"NODE_ENV",