Compare commits

..

22 Commits

Author SHA1 Message Date
Henry
bcb28a3429 fix: persist filters when opening card modal 2025-11-30 22:31:47 +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
Henry
1651faae14 fix(cloud): reenable magic login on cloud and extend session duration (#248)
* fix(cloud): reenable magic login

* feat: extend session duration
2025-11-20 00:01:23 +00:00
Henry
b472c5ce93 feat: card attachments (#247)
* feat: add card attachment schema

* feat: setup s3 util funcs

* feat: add attachments router and repo funcs

* feat: add upload button

* feat: add thumbnails and attachment viewer

* feat: add download and delete button

* feat: add file viewer and downloads

* feat: update compose and readme

* chore: build lang

* feat: display attachments on public boards

* chore: update compiled translations
2025-11-19 21:34:43 +00:00
Arpit Soni
af6490896a feat: add list filter to board view (#241)
* feat: add list filter to board view

* feat: implement API-driven list filtering

* refactor: remove unnecessary list join from card filtering queries

* feat: enable list filter options for public boards

* refactor(perf): move to main query

---------

Co-authored-by: Henry <henry_ball@hotmail.co.uk>
2025-11-17 21:25:50 +00:00
exception-raised
c0a3c37d95 feat: add tiptap markdown support, add checklist support from trello (#245)
* feat: add tiptap markdown support, add checklist support from trello

* feat(api): implement bulk creation of checklists and items
2025-11-17 20:31:57 +00:00
Morfixx
c30da618ec fix(web): logic of disabling credential login (#244)
* fix(web): logic of disabling credential login

If credentials are disabled, then don't show the email login form at all. If credentials are disabled and no social providers are present, show message saying none are available.

* chore: update lang

---------

Co-authored-by: Henry <henry_ball@hotmail.co.uk>
2025-11-14 22:24:43 +00:00
Henry
735db21227 feat(cloud): enable free trial (#236)
* feat(cloud): add free trial

* fix: center workspace name icon when collapsed

* feat: add 14 day free trial notice to pricing page

* feat: hide upgrade button when on active subscription

* chore: translations
2025-10-31 00:09:24 +00:00
Henry
e8d52de5e4 feat(cloud): oss friends (#235)
* feat: add link to oss friends to footer

* feat: add oss friends page

* chore: build lang

* feat: add skeleton loader

* chore: rebuild lang

* chore: rebuild lang
2025-10-29 21:31:44 +00:00
90 changed files with 7893 additions and 1343 deletions

View File

@@ -29,6 +29,7 @@ S3_SECRET_ACCESS_KEY=
S3_FORCE_PATH_STYLE=
NEXT_PUBLIC_STORAGE_URL=
NEXT_PUBLIC_AVATAR_BUCKET_NAME=
NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME=
NEXT_PUBLIC_STORAGE_DOMAIN=
# Auth config (optional)

View File

@@ -170,6 +170,7 @@ pnpm dev
| `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` |

View File

@@ -11,6 +11,7 @@ checksums:
"%2410%2Fmonth/singular": cab5e752ed83096e34fca780d90e1fb9
"%248%2Fmonth/singular": 4667034934bb2bc3d569c70b94205b51
1%20user/singular: 3b547431ab12f0fba84307e6a81109d8
14%20day%20free%20trial/singular: b2f7a80b5184d79ddd56b78d57d28225
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
@@ -59,6 +60,7 @@ checksums:
Are%20you%20sure%20you%20want%20to%20delete%20your%20account%3F/singular: 428b74d781063224c3e63980e1094ec3
assigned%20%3C0%3E%7B0%7D%3C%2F0%3E%20to%20the%20card/singular: 9df6d89fa83706123dedd8cba2243d04
Assignees/singular: 96fc0e28a783d80decfd4716a440ac08
Attachment%20uploaded/singular: c0233f8c2560f95ac0834f35eb07b9e7
Awaiting%20Customer/singular: 122c108794f321c10e05a08f09b11457
Backlog/singular: 35919fd5b10ad212839a75e72829fe4e
Basic%20Kanban/singular: e46ebc406749486f3907436970d4d6c7
@@ -107,6 +109,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
@@ -165,11 +168,13 @@ 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
Edit%20board%20URL/singular: d8276dfc0189f371ec7d80a2047c07aa
Edit%20comment/singular: 7e4b46525fcb6b47b71798e31c46e374
Edit%20label/singular: 0309e0be1512b1e0b0ceb87c69a53d03
@@ -208,6 +213,7 @@ checksums:
Failed%20to%20copy%20invite%20link/singular: 635884d5ed8d6ee20b85a003939b4ae7
Failed%20to%20create%20board/singular: a746e2afe881c3bf0a8e82a931ca3495
Failed%20to%20login%20with%20%7B0%7D.%20Please%20try%20again./singular: 669a4b4247a73f53fb9b8b16e42d166f
Failed%20to%20upload%20attachment.%20Please%20try%20again./singular: f8a50d1c8491404f73d3cf701e11f297
FAQ/singular: 47e0ee2eb40b4e7e732e05e2233fc71c
FAQs/singular: dc36d7992372ccd419b39ef51cf5b16c
Fast%20%26%20lightweight/singular: 4f2a80c555a5427b94eef5124aa10775
@@ -294,6 +300,7 @@ checksums:
Link%20copied/singular: 4a4805700b31ec65bcb3ca962bccfd1b
List/singular: 94f13e7ef909a4de9db7abaa1f9f0b61
List%20name/singular: e925e2e6ccaf0eb4064a888aaea8d3c2
Lists/singular: 9f4a73afc8de321175d71935134ef066
Login%20%7C%20kan.bn/singular: 42a6c8dcd73e0d46e652646dc86871eb
Logout/singular: 07948fdf20705e04a7bf68ab197512bf
Long-term/singular: 51cb6f0b112250c5a091ca5f0866904b
@@ -328,8 +335,10 @@ checksums:
Newsletter/singular: dcfb9666301924431ac8ae88e5b6f9d7
Next%20Steps/singular: 12392fe73ae60b6e5a9d851256b8477e
No%20%7B0%7D/singular: a7a95e1dfdb4396da9e2be0e27f42323
No%20authentication%20methods%20are%20currently%20available/singular: f718c896810da3612202887f9a4374fa
No%20boards%20found/singular: e044088d2c51b6bdf660fafa86ee1e17
No%20credit%20card%20required/singular: 2090aa4171dc0b60735069f89b592883
No%20download%20URL%20available%20for%20this%20attachment./singular: e367d39420b2242f9d2fd749c87f446a
No%20lists/singular: cedf633d99c77ff4356e089f2d98c0a6
No%20results%20found%20for%20%22%7BdebouncedQuery%7D%22./singular: 5db6294712528cd897b15ae36f4fd834
Offer/singular: 82b4e0c9a3f5b4bd93590847de7c32a1
@@ -426,6 +435,7 @@ checksums:
Social%20Media/singular: 2227508afb0d38a027e323207e47d4a2
Software%20Development/singular: 67659097ef6bcb194839858b2c4f3a3c
Star%20on%20Github/singular: e5cb0428f1ac6485688f120d78742875
Start%2014%20day%20free%20trial/singular: 4be2811a8f3455618032ccb59e40658b
Subscription%20Required/singular: 05f2b4abfc36def17756a6969983cf86
Success/singular: c43827becada6750f7a25890905f38b9
Supercharge%20your%20workspace%20for%20just%20%2429%2Fmonth.%20Here's%20what%20you'll%20get%3A/singular: 21af8119141afcce5f12da489f34db6a
@@ -474,6 +484,7 @@ checksums:
Unable%20to%20create%20list/singular: 7fbbf8314f8d08a4123c7daef09fed05
Unable%20to%20create%20template/singular: 7495f1434c9188849bffd015811d5337
Unable%20to%20create%20workspace/singular: c1c949477e121d73ed6683eddf9cd874
Unable%20to%20delete%20attachment/singular: 99d0b17b78d44e15f13d876d155f0efc
Unable%20to%20delete%20card/singular: 50ff06aa1265fe464f776aba33f1bef9
Unable%20to%20delete%20checklist/singular: d7067e2c009f4919f0cca77df3c228ee
Unable%20to%20delete%20checklist%20item/singular: a74144593b625e4d8d7ecf734462c60d
@@ -499,16 +510,16 @@ 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%20title/singular: 98cf8f12923ec1a58e767e3a40863b21
updated%20the%20title%20to%20%3C0%3E%7B0%7D%3C%2F0%3E/singular: aca196d729401d8adc5c8ca9d558840e
Upgrade/singular: 63c3b52882e0d779859307d672c178c2
Upgrade%20to%20Pro/singular: 972773025e763ddf53273df6d37a729a
Upgrade%20to%20Pro%20(%2429%2Fmonth)/singular: 9a91ea70f5eb55aac96640bf0fd7feff
Upgrade%20to%20Team%20Plan/singular: 40e2ef151266937f8bbe63b3d361f645
Upload%20failed/singular: 5b7b755f05330164dbfb00096c500ee3
Urgent/singular: e79afcd4398e52e10e7e8243a7eecabe
URL%20can%20only%20contain%20letters%2C%20numbers%2C%20and%20hyphens/singular: 875d37d2d920f98ef60a314eda8a5d9a
URL%20cannot%20exceed%2024%20characters/singular: 395c25732b84ac1f5b63ee310da08e93
@@ -525,6 +536,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
@@ -557,16 +569,19 @@ 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
Your%20account%20has%20been%20deleted./singular: 8c8d944e07388c5877effdb2c2803dcf
Your%20boards%20have%20been%20imported./singular: 403972e7a25afc2415762c1c2b1ec868
Your%20display%20name%20has%20been%20updated./singular: 15e5fff36c554c16ec5214427fae1bf4
Your%20file%20has%20been%20uploaded%20successfully./singular: 19d84e934877efc0fef285ff3d6e7849
Your%20password%20has%20been%20changed./singular: 8f730f68d52b7efa727aa0784282ebcb
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

@@ -30,23 +30,50 @@ const config = {
typescript: { ignoreBuildErrors: true },
images: {
remotePatterns: [
{
protocol: "https",
hostname:
env("S3_FORCE_PATH_STYLE") === "true"
? `${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`
: `*.${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`,
},
{
protocol: "http",
hostname: "localhost",
},
{
protocol: "https",
hostname: "*.googleusercontent.com",
},
],
remotePatterns: (() => {
/** @type {Array<{protocol: "http" | "https", hostname: string}>} */
const patterns = [
{
protocol: "https",
hostname:
env("S3_FORCE_PATH_STYLE") === "true"
? `${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`
: `*.${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`,
},
{
protocol: "http",
hostname: "localhost",
},
{
protocol: "https",
hostname: "*.googleusercontent.com",
},
];
// Extract root domain from S3_ENDPOINT and add wildcard pattern
const s3Endpoint = env("S3_ENDPOINT");
if (s3Endpoint) {
try {
const url = new URL(s3Endpoint);
const hostname = url.hostname;
const protocol = url.protocol.replace(":", "");
// Extract root domain (last 2 parts: e.g. cloudflarestorage.com)
const parts = hostname.split(".");
if (parts.length >= 2) {
const rootDomain = parts.slice(-2).join(".");
patterns.push({
protocol: protocol === "http" ? "http" : "https",
hostname: `*.${rootDomain}`,
});
}
} catch {
// If S3_ENDPOINT is not a valid URL, ignore it
}
}
return patterns;
})(),
},
webpack(config) {
config.module.rules.push({

View File

@@ -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:",
@@ -46,6 +47,7 @@
"aws-sdk": "^2.1692.0",
"date-fns": "^4.1.0",
"geist": "^1.3.1",
"jose": "^6.1.2",
"next": "^15.3.4",
"next-logger": "^5.0.1",
"next-runtime-env": "^1.7.2",
@@ -65,6 +67,7 @@
"superjson": "2.2.1",
"tailwind-merge": "^2.5.2",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8",
"zod": "catalog:"
},
"devDependencies": {

View File

@@ -303,6 +303,10 @@ export function Auth({ setIsMagicLinkSent, isSignUp }: AuthProps) {
const password = watch("password");
const isMagicLinkAvailable = useMemo(() => {
return isCloudEnv || (isEmailSendingEnabled && !isSignUp);
}, [isCloudEnv, isEmailSendingEnabled, isSignUp]);
// Determine if we should operate in magic link mode for current form state (login only)
const isMagicLinkMode = useMemo(() => {
// Magic link only viable when email sending enabled AND not sign up.
@@ -358,7 +362,16 @@ export function Auth({ setIsMagicLinkSent, isSignUp }: AuthProps) {
</div>
)}
<form onSubmit={handleSubmit(onSubmit)}>
{socialProviders?.length !== 0 && (
{!isCredentialsEnabled && socialProviders?.length === 0 && (
<div className="flex w-full items-center gap-4">
<div className="h-[1px] w-1/3 bg-light-600 dark:bg-dark-600" />
<span className="text-center text-sm text-light-900 dark:text-dark-900">
{t`No authentication methods are currently available`}
</span>
<div className="h-[1px] w-1/3 bg-light-600 dark:bg-dark-600" />
</div>
)}
{!isCredentialsEnabled && socialProviders?.length !== 0 && (
<div className="mb-[1.5rem] flex w-full items-center gap-4">
<div className="h-[1px] w-full bg-light-600 dark:bg-dark-600" />
<span className="text-sm text-light-900 dark:text-dark-900">
@@ -381,46 +394,54 @@ export function Auth({ setIsMagicLinkSent, isSignUp }: AuthProps) {
)}
</div>
)}
<div>
<Input
{...register("email", { required: true })}
placeholder={t`Enter your email address`}
/>
{errors.email && (
<p className="mt-2 text-xs text-red-400">
{t`Please enter a valid email address`}
</p>
)}
</div>
{isCredentialsEnabled && (
<div>
<Input
type="password"
{...register("password", { required: true })}
placeholder={t`Enter your password`}
/>
{errors.password && (
<p className="mt-2 text-xs text-red-400">
{errors.password.message ?? t`Please enter a valid password`}
</p>
{(isCredentialsEnabled || isMagicLinkAvailable) && (
<>
<div>
<Input
{...register("email", { required: true })}
placeholder={t`Enter your email address`}
/>
{errors.email && (
<p className="mt-2 text-xs text-red-400">
{t`Please enter a valid email address`}
</p>
)}
</div>
{isCredentialsEnabled && (
<div>
<Input
type="password"
{...register("password", { required: true })}
placeholder={t`Enter your password`}
/>
{errors.password && (
<p className="mt-2 text-xs text-red-400">
{errors.password.message ??
t`Please enter a valid password`}
</p>
)}
</div>
)}
</div>
</>
)}
{loginError && (
<p className="mt-2 text-xs text-red-400">{loginError}</p>
)}
</div>
<div className="mt-[1.5rem] flex items-center gap-4">
<Button
isLoading={isLoginWithEmailPending}
fullWidth
size="lg"
variant="secondary"
>
{isSignUp ? t`Sign up with ` : t`Continue with `}
{isMagicLinkMode ? t`magic link` : t`email`}
</Button>
</div>
{(isCredentialsEnabled || isMagicLinkAvailable) && (
<div className="mt-[1.5rem] flex items-center gap-4">
<Button
isLoading={isLoginWithEmailPending}
fullWidth
size="lg"
variant="secondary"
>
{isSignUp ? t`Sign up with ` : t`Continue with `}
{isMagicLinkMode ? t`magic link` : t`email`}
</Button>
</div>
)}
</form>
</div>
);

View File

@@ -12,6 +12,7 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
href?: string;
fullWidth?: boolean;
openInNewTab?: boolean;
iconOnly?: boolean;
}
const Button = ({
@@ -24,6 +25,7 @@ const Button = ({
href,
fullWidth,
openInNewTab,
iconOnly,
...props
}: ButtonProps) => {
const classes = twMerge(
@@ -32,6 +34,15 @@ const Button = ({
size === "sm" && "text-xs",
size === "lg" && "py-[0.65rem]",
fullWidth && "w-full",
iconOnly && "p-0",
iconOnly &&
(size === "xs"
? "h-6 w-6"
: size === "sm"
? "h-8 w-8"
: size === "lg"
? "h-10 w-10"
: "h-9 w-9"),
variant === "primary" &&
"bg-light-1000 dark:bg-dark-1000 dark:text-dark-50",
variant === "secondary" &&
@@ -47,19 +58,60 @@ const Button = ({
<span className="relative flex items-center justify-center">
{isLoading && (
<span className="absolute">
<LoadingSpinner size={size} />
<LoadingSpinner size={size === "xs" ? "sm" : size} />
</span>
)}
<div
className={twMerge(
"flex items-center",
isLoading ? "invisible" : "visible",
)}
>
{iconLeft && <span className="mr-2">{iconLeft}</span>}
{children}
{iconRight && <span className="ml-1">{iconRight}</span>}
</div>
{iconOnly ? (
<div
className={twMerge(
"flex items-center",
isLoading ? "invisible" : "visible",
)}
>
{iconLeft ?? iconRight}
</div>
) : (
<div
className={twMerge(
fullWidth
? "grid w-full grid-cols-[auto_1fr_auto] items-center gap-x-2"
: "flex items-center",
isLoading ? "invisible" : "visible",
)}
>
{fullWidth && !iconLeft && iconRight && (
<span className="col-start-1 opacity-0">{iconRight}</span>
)}
{iconLeft && (
<span
className={twMerge(
fullWidth ? "col-start-1 justify-self-start" : "mr-2",
)}
>
{iconLeft}
</span>
)}
<span
className={twMerge(
fullWidth ? "col-start-2 justify-self-center text-center" : "",
)}
>
{children}
</span>
{iconRight && (
<span
className={twMerge(
fullWidth ? "col-start-3 justify-self-end" : "ml-1",
)}
>
{iconRight}
</span>
)}
{fullWidth && !iconRight && iconLeft && (
<span className="col-start-3 opacity-0">{iconLeft}</span>
)}
</div>
)}
</span>
);

View File

@@ -7,6 +7,7 @@ import type { Instance as TippyInstance } from "tippy.js";
import { Button } from "@headlessui/react";
import { t } from "@lingui/core/macro";
import Link from "@tiptap/extension-link";
import Mention from "@tiptap/extension-mention";
import Placeholder from "@tiptap/extension-placeholder";
import {
BubbleMenu,
@@ -39,9 +40,10 @@ import {
} from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
import tippy from "tippy.js";
import Mention from "@tiptap/extension-mention";
import Avatar from "./Avatar";
import { Markdown } from "tiptap-markdown";
import { getAvatarUrl } from "~/utils/helpers";
import Avatar from "./Avatar";
declare module "@tiptap/core" {
interface Commands<ReturnType> {
@@ -77,7 +79,7 @@ export interface RenderSuggestionsProps {
command: (item: SlashCommandItem) => void;
}
export type WorkspaceMember = {
export interface WorkspaceMember {
publicId: string;
user: {
id: string;
@@ -85,7 +87,7 @@ export type WorkspaceMember = {
image: string | null;
} | null;
email: string;
};
}
const CommandsList = forwardRef<
{ onKeyDown: (props: SuggestionKeyDownProps) => boolean },
@@ -200,7 +202,11 @@ const RenderSuggestions = () => {
};
};
type MentionItem = { id: string; label: string; image: string | null };
interface MentionItem {
id: string;
label: string;
image: string | null;
}
const MentionList = forwardRef<
{ onKeyDown: (props: SuggestionKeyDownProps) => boolean },
@@ -238,30 +244,32 @@ const MentionList = forwardRef<
return (
<div className="w-56 rounded-md border-[1px] border-light-200 bg-light-50 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-500 dark:bg-dark-200">
<div className="max-h-[350px] overflow-y-auto p-1 scrollbar-thin scrollbar-thumb-light-200 dark:scrollbar-thumb-dark-300">
{items.length > 0 ? items.map((item, index) => (
<button
key={item.id}
onClick={() => command(item)}
className={twMerge(
"group flex w-full items-center rounded-[5px] p-2 hover:bg-light-200 dark:hover:bg-dark-300",
index === selectedIndex && "bg-light-200 dark:bg-dark-300",
)}
>
<Avatar
size="xs"
name={item.label}
imageUrl={
item.image ? getAvatarUrl(item.image) : undefined
}
email={item.label}
/>
<span className="ml-3 text-[12px] font-medium text-dark-900 dark:text-dark-1000">
{item.label}
</span>
</button>
)) : (
{items.length > 0 ? (
items.map((item, index) => (
<button
key={item.id}
onClick={() => command(item)}
className={twMerge(
"group flex w-full items-center rounded-[5px] p-2 hover:bg-light-200 dark:hover:bg-dark-300",
index === selectedIndex && "bg-light-200 dark:bg-dark-300",
)}
>
<Avatar
size="xs"
name={item.label}
imageUrl={item.image ? getAvatarUrl(item.image) : undefined}
email={item.label}
/>
<span className="ml-3 text-[12px] font-medium text-dark-900 dark:text-dark-1000">
{item.label}
</span>
</button>
))
) : (
<div className="flex items-center justify-start p-2">
<span className="text-dark-900 text-[12px] dark:text-dark-1000">No results</span>
<span className="text-[12px] text-dark-900 dark:text-dark-1000">
No results
</span>
</div>
)}
</div>
@@ -436,6 +444,7 @@ export default function Editor({
{
extensions: [
StarterKit,
Markdown,
Placeholder.configure({
placeholder: readOnly
? ""
@@ -462,35 +471,37 @@ export default function Editor({
}),
Mention.configure({
HTMLAttributes: {
class: 'mention',
class: "mention",
},
suggestion: {
char: "@",
items: ({ query }: { query: string }) => {
const all: MentionItem[] = workspaceMembers.map((member: WorkspaceMember) => ({
id: member.publicId,
label: member?.user?.name ?? member.email,
image: member?.user?.image ?? null,
}));
const all: MentionItem[] = workspaceMembers.map(
(member: WorkspaceMember) => ({
id: member.publicId,
label: member?.user?.name ?? member.email,
image: member?.user?.image ?? null,
}),
);
const q = query.toLowerCase();
return all.filter((u) => u.label.toLowerCase().includes(q));
},
command: ({ editor, range, props }: any) => {
const mentionHTML = `<span data-type="mention" data-id="${props.id}" data-label="${props.label}">@${props.label}</span>&nbsp;`;
editor
.chain()
.focus()
.deleteRange(range)
.insertContent(mentionHTML)
.focus()
.run();
},
render: renderMentionSuggestions,
},
renderText({ options, node }) {
return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`;
const mentionHTML = `<span data-type="mention" data-id="${props.id}" data-label="${props.label}">@${props.label}</span>&nbsp;`;
editor
.chain()
.focus()
.deleteRange(range)
.insertContent(mentionHTML)
.focus()
.run();
},
render: renderMentionSuggestions,
},
renderText({ options, node }) {
return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`;
},
}),
],
content,

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

@@ -2,14 +2,20 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { Button } from "@headlessui/react";
import { t } from "@lingui/core/macro";
import { env } from "next-runtime-env";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import { HiBolt } from "react-icons/hi2";
import {
TbLayoutSidebarLeftCollapse,
TbLayoutSidebarLeftExpand,
} from "react-icons/tb";
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";
@@ -18,9 +24,13 @@ import settingsIconDark from "~/assets/settings-dark.json";
import settingsIconLight from "~/assets/settings-light.json";
import templatesIconDark from "~/assets/templates-dark.json";
import templatesIconLight from "~/assets/templates-light.json";
import ButtonComponent from "~/components/Button";
import ReactiveButton from "~/components/ReactiveButton";
import UserMenu from "~/components/UserMenu";
import WorkspaceMenu from "~/components/WorkspaceMenu";
import { useModal } from "~/providers/modal";
import { useWorkspace } from "~/providers/workspace";
import { api } from "~/utils/api";
interface SideNavigationProps {
user: UserType;
@@ -39,13 +49,23 @@ export default function SideNavigation({
onCloseSideNav,
}: SideNavigationProps) {
const router = useRouter();
const { workspace } = useWorkspace();
const [isCollapsed, setIsCollapsed] = useState(false);
const [isInitialised, setIsInitialised] = useState(false);
const { openModal } = useModal();
const { data: workspaceData } = api.workspace.byId.useQuery({
workspacePublicId: workspace.publicId,
});
const subscriptions = workspaceData?.subscriptions as
| Subscription[]
| undefined;
useEffect(() => {
const savedState = localStorage.getItem("kan_sidebar-collapsed");
if (savedState !== null) {
setIsCollapsed(JSON.parse(savedState));
setIsCollapsed(Boolean(JSON.parse(savedState)));
}
setIsInitialised(true);
}, []);
@@ -61,30 +81,65 @@ export default function SideNavigation({
const { pathname } = router;
const { theme, resolvedTheme } = useTheme();
const { resolvedTheme } = useTheme();
const isCloudEnv = env("NEXT_PUBLIC_KAN_ENV") === "cloud";
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`,
},
},
];
@@ -101,10 +156,10 @@ export default function SideNavigation({
)}
>
<div>
<div className="hidden h-14 items-center justify-between pb-[18px] pt-1.5 md:flex">
<div className="hidden h-[45px] items-center justify-between pb-3 md:flex">
{!isCollapsed && (
<Link href="/" className="block">
<h1 className="pl-2 text-lg font-bold tracking-tight text-neutral-900 dark:text-dark-1000">
<h1 className="pl-2 text-[16px] font-bold tracking-tight text-neutral-900 dark:text-dark-1000">
kan.bn
</h1>
</Link>
@@ -142,13 +197,14 @@ export default function SideNavigation({
json={item.icon}
isCollapsed={isCollapsed}
onCloseSideNav={onCloseSideNav}
keyboardShortcut={item.keyboardShortcut}
/>
</li>
))}
</ul>
</div>
<div className="space-y-3">
<div className="space-y-2">
<UserMenu
email={user.email ?? ""}
imageUrl={user.image ?? undefined}
@@ -156,6 +212,33 @@ export default function SideNavigation({
isCollapsed={isCollapsed}
onCloseSideNav={onCloseSideNav}
/>
{isCloudEnv &&
!hasActiveSubscription(subscriptions, "pro") &&
!hasActiveSubscription(subscriptions, "team") && (
<div className={twMerge(isCollapsed && "flex justify-center")}>
{isCollapsed ? (
<ButtonComponent
iconLeft={<HiBolt />}
variant="secondary"
href="/settings/workspace?upgrade=pro"
aria-label="Upgrade to Pro"
title="Upgrade to Pro"
iconOnly
onClick={() => openModal("UPGRADE_TO_PRO")}
/>
) : (
<ButtonComponent
iconLeft={<HiBolt />}
fullWidth
variant="secondary"
href="/settings/workspace?upgrade=pro"
onClick={() => openModal("UPGRADE_TO_PRO")}
>
{t`Upgrade to Pro`}
</ButtonComponent>
)}
</div>
)}
</div>
</nav>
</>

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 (
<>
@@ -49,13 +51,14 @@ export default function WorkspaceMenu({
<div
className={twMerge(
"flex items-center justify-start gap-1",
isCollapsed && "md:flex-col-reverse md:items-stretch",
isCollapsed && "md:flex-col-reverse md:items-center",
)}
>
<Menu.Button
className={twMerge(
"mb-1 flex h-[34px] min-w-0 flex-1 items-center justify-start rounded-md p-1.5 hover:bg-light-200 dark:hover:bg-dark-200",
isCollapsed && "md:mb-1.5 md:h-9 md:w-9 md:flex-none md:p-0",
isCollapsed &&
"md:mb-1.5 md:h-9 md:w-9 md:flex-none md:justify-center md:p-0",
)}
title={isCollapsed ? workspace.name : undefined}
>
@@ -83,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,9 +89,10 @@ 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(),
NEXT_PUBLIC_STORAGE_DOMAIN: z.string().optional(),
NEXT_PUBLIC_ALLOW_CREDENTIALS: z
.string()
@@ -125,6 +128,8 @@ export const env = createEnv({
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
NEXT_PUBLIC_STORAGE_URL: process.env.NEXT_PUBLIC_STORAGE_URL,
NEXT_PUBLIC_AVATAR_BUCKET_NAME: process.env.NEXT_PUBLIC_AVATAR_BUCKET_NAME,
NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME:
process.env.NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME,
NEXT_PUBLIC_STORAGE_DOMAIN: process.env.NEXT_PUBLIC_STORAGE_DOMAIN,
NEXT_PUBLIC_ALLOW_CREDENTIALS: process.env.NEXT_PUBLIC_ALLOW_CREDENTIALS,
NEXT_PUBLIC_DISABLE_SIGN_UP: process.env.NEXT_PUBLIC_DISABLE_SIGN_UP,

View File

@@ -31,17 +31,17 @@ msgstr "{0}"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:442
#: src/views/board/components/NewCardForm.tsx:443
msgid "{0} labels"
msgstr "{0} Labels"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:387
#: src/views/board/index.tsx:389
msgid "{0} not found"
msgstr "{0} nicht gefunden"
@@ -63,6 +63,10 @@ msgstr "$8/Monat"
msgid "1 user"
msgstr "1 Benutzer"
#: src/views/pricing/components/PricingTiers.tsx:136
msgid "14 day free trial"
msgstr "14 Tage kostenlose Testversion"
#: src/views/home/index.tsx:71
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
msgstr "Eine leistungsstarke, flexible Kanban-App, die dir hilft, Arbeit zu organisieren, Fortschritte zu verfolgen und Ergebnisse zu liefern alles an einem Ort."
@@ -75,8 +79,8 @@ msgstr "Konto"
msgid "Account deleted"
msgstr "Konto gelöscht"
#: src/views/card/index.tsx:291
#: src/views/public/board/CardModal.tsx:143
#: src/views/card/index.tsx:343
#: src/views/public/board/CardModal.tsx:157
msgid "Activity"
msgstr "Aktivität"
@@ -105,7 +109,7 @@ msgstr "Element hinzufügen..."
msgid "Add checklist"
msgstr "Checkliste hinzufügen"
#: src/components/Editor.tsx:442
#: src/components/Editor.tsx:451
msgid "Add description... (type '/' to open commands or '@' to mention)"
msgstr "Beschreibung hinzufügen... (tippe '/' um Befehle zu öffnen oder '@' um jemanden zu erwähnen)"
@@ -119,7 +123,7 @@ msgid "Add label"
msgstr "Label hinzufügen"
#: src/views/card/components/MemberSelector.tsx:130
#: src/views/members/components/InviteMemberForm.tsx:250
#: src/views/members/components/InviteMemberForm.tsx:257
msgid "Add member"
msgstr "Mitglied hinzufügen"
@@ -154,7 +158,7 @@ msgstr "hat Checklistenelement <0>{0}</0> hinzugefügt"
msgid "added label <0>{0}</0>"
msgstr "hat Label <0>{0}</0> hinzugefügt"
#: src/views/members/components/InviteMemberForm.tsx:320
#: src/views/members/components/InviteMemberForm.tsx:327
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
msgstr "Das Hinzufügen eines neuen Mitglieds kostet zusätzlich {price} ({billingType}) pro Platz."
@@ -183,7 +187,7 @@ msgstr "Beim Trennen deines Trello-Kontos ist ein Fehler aufgetreten."
msgid "An unexpected error occurred. Please try again later."
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut."
#: src/views/members/components/InviteMemberForm.tsx:304
#: src/views/members/components/InviteMemberForm.tsx:311
msgid "Anyone with this link can join your workspace"
msgstr "Jeder mit diesem Link kann deinem Arbeitsbereich beitreten"
@@ -266,6 +270,10 @@ msgstr "hat <0>{0}</0> der Karte zugewiesen"
msgid "Assignees"
msgstr "Zugewiesene Personen"
#: src/views/card/components/AttachmentUpload.tsx:25
msgid "Attachment uploaded"
msgstr "Anhang hochgeladen"
#: src/views/boards/components/TemplateBoards.tsx:47
msgid "Awaiting Customer"
msgstr "Warten auf Kunden"
@@ -300,14 +308,14 @@ msgstr "Abrechnungsportal"
msgid "Blog Post"
msgstr "Blogbeitrag"
#: src/views/board/index.tsx:361
#: src/views/card/index.tsx:216
#: src/views/public/board/index.tsx:100
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:229
#: src/views/public/board/index.tsx:101
msgid "Board"
msgstr "Board"
#: src/components/NewWorkspaceForm.tsx:349
#: src/views/settings/components/UpgradeToProConfirmation.tsx:81
#: src/views/settings/components/UpgradeToProConfirmation.tsx:83
msgid "Board analytics"
msgstr "Board-Analytik"
@@ -319,7 +327,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:151
#: src/views/public/board/index.tsx:153
msgid "Board not found"
msgstr "Board nicht gefunden"
@@ -327,19 +335,19 @@ msgstr "Board nicht gefunden"
msgid "Board sharing & invites"
msgstr "Board-Freigabe & Einladungen"
#: src/views/board/components/UpdateBoardSlugForm.tsx:45
#: src/views/board/components/UpdateBoardSlugForm.tsx:46
msgid "Board URL can only contain letters, numbers, and hyphens"
msgstr "Board-URL darf nur Buchstaben, Zahlen und Bindestriche enthalten"
#: src/views/board/components/UpdateBoardSlugForm.tsx:43
#: src/views/board/components/UpdateBoardSlugForm.tsx:44
msgid "Board URL cannot exceed 60 characters"
msgstr "Board-URL darf 60 Zeichen nicht überschreiten"
#: src/views/public/board/index.tsx:67
#: src/views/public/board/index.tsx:68
msgid "Board URL copied to clipboard"
msgstr "Board-URL in die Zwischenablage kopiert"
#: src/views/board/components/UpdateBoardSlugForm.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:42
msgid "Board URL must be at least 3 characters long"
msgstr "Board-URL muss mindestens 3 Zeichen lang sein"
@@ -348,11 +356,11 @@ msgstr "Board-URL muss mindestens 3 Zeichen lang sein"
msgid "Board visibility"
msgstr "Board-Sichtbarkeit"
#: src/views/board/components/VisibilityButton.tsx:45
#: src/views/board/components/VisibilityButton.tsx:46
msgid "Board visibility updated"
msgstr "Board-Sichtbarkeit aktualisiert"
#: src/components/SideNavigation.tsx:70
#: src/components/SideNavigation.tsx:91
msgid "Boards"
msgstr "Boards"
@@ -379,19 +387,20 @@ msgstr "Fehlerbericht"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
#: src/views/settings/components/UpgradeToProConfirmation.tsx:104
#: src/views/settings/components/UpgradeToProConfirmation.tsx:106
msgid "Cancel"
msgstr "Abbrechen"
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "Card"
msgstr "Karte"
#: src/views/card/index.tsx:262
#: src/views/card/index.tsx:263
#: src/views/card/index.tsx:299
msgid "Card not found"
msgstr "Karte nicht gefunden"
#: src/views/board/components/NewCardForm.tsx:309
#: src/views/board/components/NewCardForm.tsx:310
msgid "Card title"
msgstr "Kartentitel"
@@ -424,7 +433,7 @@ msgstr "Checklistenname"
msgid "Checklists"
msgstr "Checklisten"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/Filters.tsx:180
msgid "Clear filters"
msgstr "Filter löschen"
@@ -447,7 +456,7 @@ msgstr "Arbeite nahtlos mit deinem Team zusammen."
#: src/components/NewWorkspaceForm.tsx:352
#: src/views/home/components/Features.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:84
#: src/views/settings/components/UpgradeToProConfirmation.tsx:86
msgid "Coming soon"
msgstr "Demnächst verfügbar"
@@ -485,6 +494,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"
@@ -515,12 +528,12 @@ msgstr "Kontaktiere uns"
msgid "Content Creation"
msgstr "Content-Erstellung"
#: src/components/AuthForm.tsx:420
#: 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}"
@@ -538,8 +551,8 @@ msgid "Create {0}"
msgstr "{0} erstellen"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:468
#: src/views/board/components/NewListForm.tsx:140
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Weitere erstellen"
@@ -547,7 +560,7 @@ msgstr "Weitere erstellen"
msgid "Create API key"
msgstr "API-Schlüssel erstellen"
#: src/views/board/components/NewCardForm.tsx:478
#: src/views/board/components/NewCardForm.tsx:479
msgid "Create card"
msgstr "Karte erstellen"
@@ -555,7 +568,7 @@ msgstr "Karte erstellen"
msgid "Create checklist"
msgstr "Checkliste erstellen"
#: src/views/members/components/InviteMemberForm.tsx:342
#: src/views/members/components/InviteMemberForm.tsx:349
msgid "Create invite link"
msgstr "Einladungslink erstellen"
@@ -563,7 +576,7 @@ msgstr "Einladungslink erstellen"
msgid "Create label"
msgstr "Label erstellen"
#: src/views/board/components/NewListForm.tsx:152
#: src/views/board/components/NewListForm.tsx:153
msgid "Create list"
msgstr "Liste erstellen"
@@ -576,12 +589,12 @@ msgstr "Neue(n) {0} erstellen"
msgid "Create new key"
msgstr "Neuen Schlüssel erstellen"
#: src/views/board/components/NewCardForm.tsx:404
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Neues Label erstellen"
#: src/views/board/index.tsx:475
#: src/views/board/index.tsx:478
msgid "Create new list"
msgstr "Neue Liste erstellen"
@@ -590,7 +603,7 @@ msgid "Create template"
msgstr "Vorlage erstellen"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:148
#: src/components/WorkspaceMenu.tsx:149
msgid "Create workspace"
msgstr "Arbeitsbereich erstellen"
@@ -632,7 +645,7 @@ msgid "Custom workspace link"
msgstr "Benutzerdefinierter Workspace-Link"
#: src/components/NewWorkspaceForm.tsx:342
#: src/views/settings/components/UpgradeToProConfirmation.tsx:74
#: src/views/settings/components/UpgradeToProConfirmation.tsx:76
msgid "Custom workspace URL"
msgstr "Benutzerdefinierte Workspace-URL"
@@ -644,7 +657,7 @@ msgstr "Kundensupport"
msgid "Dark"
msgstr "Dunkel"
#: src/views/members/components/InviteMemberForm.tsx:341
#: src/views/members/components/InviteMemberForm.tsx:348
msgid "Deactivate invite link"
msgstr "Einladungslink deaktivieren"
@@ -684,8 +697,8 @@ msgid "Delete template"
msgstr "Vorlage löschen"
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
#: src/views/settings/WorkspaceSettings.tsx:96
#: src/views/settings/WorkspaceSettings.tsx:107
#: src/views/settings/WorkspaceSettings.tsx:97
#: src/views/settings/WorkspaceSettings.tsx:108
msgid "Delete workspace"
msgstr "Workspace löschen"
@@ -739,6 +752,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"
@@ -765,8 +782,12 @@ msgstr "Du hast noch kein Konto? <0><1>Registrieren</1></0>"
msgid "Done"
msgstr "Erledigt"
#: src/views/card/components/AttachmentThumbnails.tsx:141
msgid "Download failed"
msgstr "Download fehlgeschlagen"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:115
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
msgstr "Board-URL bearbeiten"
@@ -778,7 +799,7 @@ msgstr "Kommentar bearbeiten"
msgid "Edit label"
msgstr "Label bearbeiten"
#: src/views/board/components/UpdateBoardSlugForm.tsx:161
#: src/views/board/components/UpdateBoardSlugForm.tsx:162
msgid "Edit workspace URL"
msgstr "Workspace-URL bearbeiten"
@@ -786,11 +807,11 @@ msgstr "Workspace-URL bearbeiten"
msgid "Editing"
msgstr "Bearbeitung"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "E-Mail"
#: src/views/members/components/InviteMemberForm.tsx:265
#: src/views/members/components/InviteMemberForm.tsx:272
msgid "Email"
msgstr "E-Mail"
@@ -806,11 +827,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:387
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Gib deine E-Mail-Adresse ein"
#: src/components/AuthForm.tsx:375
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Gib deinen Namen ein"
@@ -818,13 +839,13 @@ msgstr "Gib deinen Namen ein"
msgid "Enter your new password"
msgstr "Geben Sie Ihr neues Passwort ein"
#: src/components/AuthForm.tsx:400
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Gib dein Passwort ein"
#: src/views/boards/components/NewBoardForm.tsx:77
#: src/views/boards/components/NewBoardForm.tsx:92
#: src/views/members/components/InviteMemberForm.tsx:208
#: src/views/members/components/InviteMemberForm.tsx:215
msgid "Error"
msgstr "Fehler"
@@ -881,7 +902,7 @@ msgstr "Fehler beim Aktualisieren des Arbeitsbereichsnamens"
msgid "Error updating workspace URL"
msgstr "Fehler beim Aktualisieren der Workspace-URL"
#: src/views/members/components/InviteMemberForm.tsx:233
#: src/views/members/components/InviteMemberForm.tsx:240
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
msgid "Error upgrading subscription"
msgstr "Fehler beim Upgrade des Abonnements"
@@ -913,7 +934,7 @@ msgstr "Ausführung"
msgid "Failed to accept invitation. Please try again later, or contact customer support."
msgstr "Einladung konnte nicht angenommen werden. Bitte versuche es später erneut oder kontaktiere den Kundensupport."
#: src/views/members/components/InviteMemberForm.tsx:209
#: src/views/members/components/InviteMemberForm.tsx:216
msgid "Failed to copy invite link"
msgstr "Einladungslink konnte nicht kopiert werden"
@@ -927,6 +948,11 @@ msgstr "Board konnte nicht erstellt werden"
msgid "Failed to login with {0}. Please try again."
msgstr "Anmeldung mit {0} fehlgeschlagen. Bitte versuche es erneut."
#: src/views/card/components/AttachmentUpload.tsx:33
#: src/views/card/components/AttachmentUpload.tsx:79
msgid "Failed to upload attachment. Please try again."
msgstr "Fehler beim Hochladen des Anhangs. Bitte versuchen Sie es erneut."
#: src/views/home/components/Header.tsx:41
msgid "FAQ"
msgstr "FAQ"
@@ -970,7 +996,7 @@ msgstr "Feedback"
msgid "Feedback sent"
msgstr "Feedback gesendet"
#: src/views/board/components/Filters.tsx:149
#: src/views/board/components/Filters.tsx:174
msgid "Filter"
msgstr "Filter"
@@ -983,13 +1009,11 @@ msgid "For long-term sustainability, we recognise all good open source projects
msgstr "Für langfristige Nachhaltigkeit erkennen wir an, dass alle guten Open-Source-Projekte eine Einnahmequelle benötigen. Unsere stammt aus dem kostenpflichtigen Cloud-Angebot für Workspaces mit mehreren Benutzern und für benutzerdefinierte Workspace-Slugs. Es besteht keine Verpflichtung, es zu nutzen, und Sie können die Software kostenlos auf Ihrer eigenen Infrastruktur selbst hosten."
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/PricingTiers.tsx:30
#: src/views/pricing/components/PricingTiers.tsx:30
msgid "Free"
msgstr "Kostenlos"
#: src/views/members/components/InviteMemberForm.tsx:326
#: src/views/members/components/InviteMemberForm.tsx:333
#: src/views/members/index.tsx:208
msgid "Free Plan"
msgstr "Kostenloser Plan"
@@ -1026,7 +1050,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:467
#: src/views/board/index.tsx:470
msgid "Get started by creating a new list"
msgstr "Beginne mit dem Erstellen einer neuen Liste"
@@ -1181,11 +1205,11 @@ msgstr "Ungültige Einladung"
msgid "Invite"
msgstr "Einladen"
#: src/views/members/components/InviteMemberForm.tsx:202
#: src/views/members/components/InviteMemberForm.tsx:209
msgid "Invite link copied"
msgstr "Einladungslink kopiert"
#: src/views/members/components/InviteMemberForm.tsx:203
#: src/views/members/components/InviteMemberForm.tsx:210
msgid "Invite link copied to clipboard"
msgstr "Einladungslink in die Zwischenablage kopiert"
@@ -1198,11 +1222,11 @@ msgid "Invite links require a Team or Pro subscription. Please upgrade your work
msgstr "Einladungslinks erfordern ein Team- oder Pro-Abonnement. Bitte upgraden Sie Ihren Workspace."
#: src/views/card/components/MemberSelector.tsx:111
#: src/views/members/components/InviteMemberForm.tsx:365
#: src/views/members/components/InviteMemberForm.tsx:367
msgid "Invite member"
msgstr "Mitglied einladen"
#: src/views/members/components/InviteMemberForm.tsx:329
#: src/views/members/components/InviteMemberForm.tsx:336
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
msgstr "Das Einladen von Mitgliedern erfordert einen Team-Plan. Sie werden weitergeleitet, um Ihren Workspace zu upgraden."
@@ -1240,9 +1264,9 @@ 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:101
#: src/views/board/components/NewCardForm.tsx:408
#: src/views/card/index.tsx:117
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
msgid "Labels"
msgstr "Labels"
@@ -1259,7 +1283,7 @@ msgid "Language"
msgstr "Sprache"
#: src/components/NewWorkspaceForm.tsx:335
#: src/views/settings/components/UpgradeToProConfirmation.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:69
msgid "Launch offer"
msgstr "Einführungsangebot"
@@ -1292,18 +1316,22 @@ msgstr "Lizenz"
msgid "Light"
msgstr "Hell"
#: src/views/public/board/index.tsx:65
#: src/views/public/board/index.tsx:66
msgid "Link copied"
msgstr "Link kopiert"
#: src/views/card/index.tsx:109
#: src/views/card/index.tsx:111
msgid "List"
msgstr "Liste"
#: src/views/board/components/NewListForm.tsx:128
#: src/views/board/components/NewListForm.tsx:129
msgid "List name"
msgstr "Listenname"
#: src/views/board/components/Filters.tsx:123
msgid "Lists"
msgstr "Listen"
#: src/views/auth/login/index.tsx:33
msgid "Login | kan.bn"
msgstr "Login | kan.bn"
@@ -1324,7 +1352,7 @@ msgstr "Von Teams weltweit geliebt"
msgid "Low Priority"
msgstr "Niedrige Priorität"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "Magic Link"
@@ -1345,10 +1373,10 @@ msgstr "hat Checklistenelement <0>{0}</0> als unvollständig markiert"
msgid "Medium Priority"
msgstr "Mittlere Priorität"
#: src/components/SideNavigation.tsx:80
#: src/views/board/components/Filters.tsx:93
#: src/views/board/components/NewCardForm.tsx:366
#: src/views/card/index.tsx:126
#: 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/views/members/index.tsx:178
msgid "Members"
msgstr "Mitglieder"
@@ -1401,7 +1429,7 @@ msgstr "Neue(r) {0}"
msgid "New API key"
msgstr "Neuer API-Schlüssel"
#: src/views/board/components/NewCardForm.tsx:292
#: src/views/board/components/NewCardForm.tsx:293
msgid "New card"
msgstr "Neue Karte"
@@ -1417,8 +1445,8 @@ msgstr "Neuer Import"
msgid "New label"
msgstr "Neues Label"
#: src/views/board/components/NewListForm.tsx:112
#: src/views/board/index.tsx:439
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
msgid "New list"
msgstr "Neue Liste"
@@ -1455,6 +1483,10 @@ msgstr "Nächste Schritte"
msgid "No {0}"
msgstr "Keine {0}"
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Derzeit sind keine Authentifizierungsmethoden verfügbar"
#: src/views/boards/components/ImportBoardsForm.tsx:242
msgid "No boards found"
msgstr "Keine Boards gefunden"
@@ -1463,7 +1495,11 @@ msgstr "Keine Boards gefunden"
msgid "No credit card required"
msgstr "Keine Kreditkarte erforderlich"
#: src/views/board/index.tsx:464
#: 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
msgid "No lists"
msgstr "Keine Listen"
@@ -1483,7 +1519,7 @@ msgstr "Einarbeitung"
msgid "Once you delete your account, there is no going back. This action cannot be undone."
msgstr "Sobald Sie Ihr Konto löschen, gibt es kein Zurück mehr. Diese Aktion kann nicht rückgängig gemacht werden."
#: src/views/settings/WorkspaceSettings.tsx:99
#: src/views/settings/WorkspaceSettings.tsx:100
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."
@@ -1495,7 +1531,7 @@ msgstr "Open Source"
msgid "Open Source Friends"
msgstr "Open-Source-Freunde"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "oder"
@@ -1577,15 +1613,15 @@ msgstr "Plattform"
msgid "Please confirm your new password"
msgstr "Bitte bestätigen Sie Ihr neues Passwort"
#: src/components/AuthForm.tsx:391
#: 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:379
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Bitte gib einen gültigen Namen ein"
#: src/components/AuthForm.tsx:404
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Bitte gib ein gültiges Passwort ein"
@@ -1596,15 +1632,16 @@ 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:176
#: src/views/board/components/NewListForm.tsx:78
#: src/views/board/components/NewCardForm.tsx:177
#: 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:72
#: src/views/board/components/VisibilityButton.tsx:53
#: src/views/board/index.tsx:149
#: src/views/board/index.tsx:205
#: 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/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
#: src/views/card/components/ChecklistItemRow.tsx:88
#: src/views/card/components/ChecklistNameInput.tsx:46
@@ -1618,10 +1655,10 @@ msgstr "Bitte wähle eine Datei zum Hochladen aus."
#: 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:175
#: src/views/card/index.tsx:177
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:234
#: src/views/members/components/InviteMemberForm.tsx:241
#: src/views/settings/components/Avatar.tsx:78
#: src/views/settings/components/Avatar.tsx:219
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
@@ -1654,7 +1691,7 @@ msgstr "Preise"
#: src/views/pricing/components/Pricing.tsx:56
#: src/views/pricing/components/PricingTiers.tsx:53
#: src/views/settings/components/UpgradeToProConfirmation.tsx:91
#: src/views/settings/components/UpgradeToProConfirmation.tsx:93
msgid "Priority email support"
msgstr "Prioritäts-E-Mail-Support"
@@ -1662,7 +1699,7 @@ msgstr "Prioritäts-E-Mail-Support"
msgid "Privacy policy"
msgstr "Datenschutzrichtlinie"
#: src/views/board/components/VisibilityButton.tsx:73
#: src/views/board/components/VisibilityButton.tsx:74
msgid "Private"
msgstr "Privat"
@@ -1670,7 +1707,7 @@ msgstr "Privat"
msgid "Pro Plan"
msgstr "Pro-Plan"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
msgid "Pro Plan ∞"
msgstr "Pro-Plan ∞"
@@ -1682,7 +1719,7 @@ msgstr "Profilbild aktualisiert"
msgid "Profile picture"
msgstr "Profilbild"
#: src/views/board/components/VisibilityButton.tsx:68
#: src/views/board/components/VisibilityButton.tsx:69
msgid "Public"
msgstr "Öffentlich"
@@ -1835,7 +1872,7 @@ msgid "Senior"
msgstr "Senior"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:85
#: src/components/SideNavigation.tsx:106
msgid "Settings"
msgstr "Einstellungen"
@@ -1884,7 +1921,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:420
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Registrieren mit "
@@ -1909,6 +1946,11 @@ msgstr "Softwareentwicklung"
msgid "Star on Github"
msgstr "Stern auf Github"
#: src/views/members/components/InviteMemberForm.tsx:359
#: src/views/settings/components/UpgradeToProConfirmation.tsx:111
msgid "Start 14 day free trial"
msgstr "14 Tage kostenlose Testversion starten"
#: src/views/members/components/InviteMemberForm.tsx:122
msgid "Subscription Required"
msgstr "Abonnement erforderlich"
@@ -1918,7 +1960,7 @@ msgstr "Abonnement erforderlich"
msgid "Success"
msgstr "Erfolg"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:55
#: src/views/settings/components/UpgradeToProConfirmation.tsx:57
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:"
@@ -1935,7 +1977,7 @@ msgstr "Unterstütze die Entwicklung des Projekts"
msgid "System"
msgstr "System"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
#: src/views/members/index.tsx:207
msgid "Team Plan"
msgstr "Team-Plan"
@@ -1946,8 +1988,8 @@ msgstr "Team-Plan"
msgid "Teams"
msgstr "Teams"
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:396
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
msgid "Template"
msgstr "Vorlage"
@@ -1967,7 +2009,7 @@ msgstr "Vorlagenname darf 100 Zeichen nicht überschreiten"
msgid "Template name is required"
msgstr "Vorlagenname ist erforderlich"
#: src/components/SideNavigation.tsx:75
#: src/components/SideNavigation.tsx:96
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Vorlagen"
@@ -1997,7 +2039,7 @@ msgid "The open source <0/> alternative to Trello"
msgstr "Die open source <0/> alternative zu Trello"
#. placeholder {0}: isPublic ? "public" : "private"
#: src/views/board/components/VisibilityButton.tsx:46
#: src/views/board/components/VisibilityButton.tsx:47
msgid "The visibility of your board has been set to {0}."
msgstr "Die Sichtbarkeit deines Boards wurde auf {0} gesetzt."
@@ -2021,11 +2063,11 @@ 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:154
#: src/views/public/board/index.tsx:156
msgid "This board is private or does not exist"
msgstr "Dieses Board ist privat oder existiert nicht"
#: src/views/board/components/UpdateBoardSlugForm.tsx:135
#: src/views/board/components/UpdateBoardSlugForm.tsx:136
msgid "This board URL has already been taken"
msgstr "Diese Board-URL ist bereits vergeben"
@@ -2095,7 +2137,7 @@ 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:173
#: src/views/board/components/NewCardForm.tsx:174
msgid "Unable to create card"
msgstr "Karte konnte nicht erstellt werden"
@@ -2103,7 +2145,7 @@ msgstr "Karte konnte nicht erstellt werden"
msgid "Unable to create checklist"
msgstr "Checkliste kann nicht erstellt werden"
#: src/views/board/components/NewListForm.tsx:77
#: src/views/board/components/NewListForm.tsx:78
msgid "Unable to create list"
msgstr "Liste konnte nicht erstellt werden"
@@ -2116,6 +2158,10 @@ msgstr "Vorlage konnte nicht erstellt werden"
msgid "Unable to create workspace"
msgstr "Workspace konnte nicht erstellt werden"
#: src/views/card/components/AttachmentThumbnails.tsx:72
msgid "Unable to delete attachment"
msgstr "Anhang kann nicht gelöscht werden"
#: src/views/card/components/DeleteCardConfirmation.tsx:51
msgid "Unable to delete card"
msgstr "Karte konnte nicht gelöscht werden"
@@ -2140,16 +2186,16 @@ msgstr "Mitglied konnte nicht entfernt werden"
msgid "Unable to send feedback"
msgstr "Feedback konnte nicht gesendet werden"
#: src/views/board/components/UpdateBoardSlugForm.tsx:71
#: src/views/board/components/UpdateBoardSlugForm.tsx:72
msgid "Unable to update board URL"
msgstr "Board-URL kann nicht aktualisiert werden"
#: src/views/board/components/VisibilityButton.tsx:52
#: src/views/board/components/VisibilityButton.tsx:53
msgid "Unable to update board visibility"
msgstr "Board-Sichtbarkeit konnte nicht aktualisiert werden"
#: src/views/board/index.tsx:204
#: src/views/card/index.tsx:174
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
msgid "Unable to update card"
msgstr "Karte konnte nicht aktualisiert werden"
@@ -2169,7 +2215,7 @@ msgstr "Kommentar konnte nicht aktualisiert werden"
msgid "Unable to update labels"
msgstr "Labels konnten nicht aktualisiert werden"
#: src/views/board/index.tsx:148
#: src/views/board/index.tsx:150
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Liste konnte nicht aktualisiert werden"
@@ -2217,7 +2263,7 @@ msgid "Unlimited lists"
msgstr "Unbegrenzte listen"
#: src/components/NewWorkspaceForm.tsx:332
#: src/views/settings/components/UpgradeToProConfirmation.tsx:64
#: src/views/settings/components/UpgradeToProConfirmation.tsx:66
msgid "Unlimited members"
msgstr "Unbegrenzte Mitglieder"
@@ -2225,7 +2271,12 @@ msgstr "Unbegrenzte Mitglieder"
msgid "Unlimited workspaces"
msgstr "Unbegrenzte Workspaces"
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
#: 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
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
@@ -2254,12 +2305,9 @@ msgstr "hat den Titel aktualisiert"
msgid "updated the title to <0>{0}</0>"
msgstr "hat den Titel in <0>{0}</0> geändert"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:109
msgid "Upgrade"
msgstr "Upgrade"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
#: src/views/settings/WorkspaceSettings.tsx:89
#: src/components/SideNavigation.tsx:202
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
msgstr "Upgrade auf Pro"
@@ -2267,9 +2315,10 @@ msgstr "Upgrade auf Pro"
msgid "Upgrade to Pro ($29/month)"
msgstr "Upgrade auf Pro ($29/Monat)"
#: src/views/members/components/InviteMemberForm.tsx:357
msgid "Upgrade to Team Plan"
msgstr "Upgrade auf Team-Plan"
#: src/views/card/components/AttachmentUpload.tsx:32
#: src/views/card/components/AttachmentUpload.tsx:78
msgid "Upload failed"
msgstr "Hochladen fehlgeschlagen"
#: src/views/boards/components/TemplateBoards.tsx:38
#: src/views/boards/components/TemplateBoards.tsx:55
@@ -2320,7 +2369,7 @@ msgstr "Verwalte deine Abrechnung und dein Abonnement."
msgid "View docs"
msgstr "Dokumentation ansehen"
#: src/views/public/board/index.tsx:128
#: src/views/public/board/index.tsx:129
msgid "View only"
msgstr "Nur ansehen"
@@ -2328,11 +2377,11 @@ msgstr "Nur ansehen"
msgid "View our roadmap."
msgstr "Sieh dir unsere roadmap an."
#: src/views/public/board/index.tsx:157
#: src/views/public/board/index.tsx:159
msgid "View workspace"
msgstr "Workspace anzeigen"
#: src/views/board/components/VisibilityButton.tsx:91
#: src/views/board/components/VisibilityButton.tsx:92
msgid "Visibility"
msgstr "Sichtbarkeit"
@@ -2340,6 +2389,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. "
@@ -2369,10 +2422,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:361
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:100
#: src/views/public/board/index.tsx:101
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Arbeitsbereich"
@@ -2478,7 +2531,11 @@ msgstr "Sie haben sich erfolgreich angemeldet."
msgid "You have been signed up successfully."
msgstr "Sie haben sich erfolgreich registriert."
#: src/views/members/components/InviteMemberForm.tsx:319
#: 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!"
@@ -2502,6 +2559,10 @@ msgstr "Deine Boards wurden importiert."
msgid "Your display name has been updated."
msgstr "Dein Anzeigename wurde aktualisiert."
#: src/views/card/components/AttachmentUpload.tsx:26
msgid "Your file has been uploaded successfully."
msgstr "Ihre Datei wurde erfolgreich hochgeladen."
#: src/views/settings/components/ChangePasswordConfirmation.tsx:70
msgid "Your password has been changed."
msgstr "Ihr Passwort wurde geändert."
@@ -2518,6 +2579,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

@@ -31,17 +31,17 @@ msgstr "{0}"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:442
#: src/views/board/components/NewCardForm.tsx:443
msgid "{0} labels"
msgstr "{0} labels"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:387
#: src/views/board/index.tsx:389
msgid "{0} not found"
msgstr "{0} not found"
@@ -67,6 +67,10 @@ msgstr "$8/month"
msgid "1 user"
msgstr "1 user"
#: src/views/pricing/components/PricingTiers.tsx:136
msgid "14 day free trial"
msgstr "14 day free trial"
#: src/views/home/index.tsx:71
#~ msgid ""
#~ "A powerful, flexible kanban app that helps you organise work,\n"
@@ -87,8 +91,12 @@ msgstr "Account"
msgid "Account deleted"
msgstr "Account deleted"
#: src/views/card/index.tsx:291
#: src/views/public/board/CardModal.tsx:143
#: src/providers/keyboard-shortcuts.tsx:55
#~ msgid "Actions"
#~ msgstr "Actions"
#: src/views/card/index.tsx:343
#: src/views/public/board/CardModal.tsx:157
msgid "Activity"
msgstr "Activity"
@@ -117,7 +125,7 @@ msgstr "Add an item..."
msgid "Add checklist"
msgstr "Add checklist"
#: src/components/Editor.tsx:442
#: src/components/Editor.tsx:451
msgid "Add description... (type '/' to open commands or '@' to mention)"
msgstr "Add description... (type '/' to open commands or '@' to mention)"
@@ -135,7 +143,7 @@ msgid "Add label"
msgstr "Add label"
#: src/views/card/components/MemberSelector.tsx:130
#: src/views/members/components/InviteMemberForm.tsx:250
#: src/views/members/components/InviteMemberForm.tsx:257
msgid "Add member"
msgstr "Add member"
@@ -174,7 +182,7 @@ msgstr "added label <0>{0}</0>"
#~ msgid "added label <0>{label}</0>"
#~ msgstr "added label <0>{label}</0>"
#: src/views/members/components/InviteMemberForm.tsx:320
#: src/views/members/components/InviteMemberForm.tsx:327
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
msgstr "Adding a new member will cost an additional {price} ({billingType}) per seat."
@@ -207,7 +215,7 @@ msgstr "An error occurred while disconnecting your Trello account."
msgid "An unexpected error occurred. Please try again later."
msgstr "An unexpected error occurred. Please try again later."
#: src/views/members/components/InviteMemberForm.tsx:304
#: src/views/members/components/InviteMemberForm.tsx:311
msgid "Anyone with this link can join your workspace"
msgstr "Anyone with this link can join your workspace"
@@ -302,6 +310,10 @@ msgstr "assigned <0>{0}</0> to the card"
msgid "Assignees"
msgstr "Assignees"
#: src/views/card/components/AttachmentUpload.tsx:25
msgid "Attachment uploaded"
msgstr "Attachment uploaded"
#: src/views/boards/components/TemplateBoards.tsx:47
msgid "Awaiting Customer"
msgstr "Awaiting Customer"
@@ -340,14 +352,14 @@ msgstr "Billing portal"
msgid "Blog Post"
msgstr "Blog Post"
#: src/views/board/index.tsx:361
#: src/views/card/index.tsx:216
#: src/views/public/board/index.tsx:100
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:229
#: src/views/public/board/index.tsx:101
msgid "Board"
msgstr "Board"
#: src/components/NewWorkspaceForm.tsx:349
#: src/views/settings/components/UpgradeToProConfirmation.tsx:81
#: src/views/settings/components/UpgradeToProConfirmation.tsx:83
msgid "Board analytics"
msgstr "Board analytics"
@@ -368,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:151
#: src/views/public/board/index.tsx:153
msgid "Board not found"
msgstr "Board not found"
@@ -376,19 +388,19 @@ msgstr "Board not found"
msgid "Board sharing & invites"
msgstr "Board sharing & invites"
#: src/views/board/components/UpdateBoardSlugForm.tsx:45
#: src/views/board/components/UpdateBoardSlugForm.tsx:46
msgid "Board URL can only contain letters, numbers, and hyphens"
msgstr "Board URL can only contain letters, numbers, and hyphens"
#: src/views/board/components/UpdateBoardSlugForm.tsx:43
#: src/views/board/components/UpdateBoardSlugForm.tsx:44
msgid "Board URL cannot exceed 60 characters"
msgstr "Board URL cannot exceed 60 characters"
#: src/views/public/board/index.tsx:67
#: src/views/public/board/index.tsx:68
msgid "Board URL copied to clipboard"
msgstr "Board URL copied to clipboard"
#: src/views/board/components/UpdateBoardSlugForm.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:42
msgid "Board URL must be at least 3 characters long"
msgstr "Board URL must be at least 3 characters long"
@@ -397,11 +409,11 @@ msgstr "Board URL must be at least 3 characters long"
msgid "Board visibility"
msgstr "Board visibility"
#: src/views/board/components/VisibilityButton.tsx:45
#: src/views/board/components/VisibilityButton.tsx:46
msgid "Board visibility updated"
msgstr "Board visibility updated"
#: src/components/SideNavigation.tsx:70
#: src/components/SideNavigation.tsx:91
msgid "Boards"
msgstr "Boards"
@@ -436,19 +448,20 @@ msgstr "Bug Report"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
#: src/views/settings/components/UpgradeToProConfirmation.tsx:104
#: src/views/settings/components/UpgradeToProConfirmation.tsx:106
msgid "Cancel"
msgstr "Cancel"
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "Card"
msgstr "Card"
#: src/views/card/index.tsx:262
#: src/views/card/index.tsx:263
#: src/views/card/index.tsx:299
msgid "Card not found"
msgstr "Card not found"
#: src/views/board/components/NewCardForm.tsx:309
#: src/views/board/components/NewCardForm.tsx:310
msgid "Card title"
msgstr "Card title"
@@ -485,7 +498,7 @@ msgstr "Checklist name"
msgid "Checklists"
msgstr "Checklists"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/Filters.tsx:180
msgid "Clear filters"
msgstr "Clear filters"
@@ -508,7 +521,7 @@ msgstr "Collaborate seamlessly with your team."
#: src/components/NewWorkspaceForm.tsx:352
#: src/views/home/components/Features.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:84
#: src/views/settings/components/UpgradeToProConfirmation.tsx:86
msgid "Coming soon"
msgstr "Coming soon"
@@ -550,6 +563,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"
@@ -580,12 +597,12 @@ msgstr "Contact us"
msgid "Content Creation"
msgstr "Content Creation"
#: src/components/AuthForm.tsx:420
#: 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}"
@@ -603,8 +620,8 @@ msgid "Create {0}"
msgstr "Create {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:468
#: src/views/board/components/NewListForm.tsx:140
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Create another"
@@ -620,7 +637,7 @@ msgstr "Create API key"
#~ msgid "Create board"
#~ msgstr "Create board"
#: src/views/board/components/NewCardForm.tsx:478
#: src/views/board/components/NewCardForm.tsx:479
msgid "Create card"
msgstr "Create card"
@@ -628,7 +645,7 @@ msgstr "Create card"
msgid "Create checklist"
msgstr "Create checklist"
#: src/views/members/components/InviteMemberForm.tsx:342
#: src/views/members/components/InviteMemberForm.tsx:349
msgid "Create invite link"
msgstr "Create invite link"
@@ -636,7 +653,7 @@ msgstr "Create invite link"
msgid "Create label"
msgstr "Create label"
#: src/views/board/components/NewListForm.tsx:152
#: src/views/board/components/NewListForm.tsx:153
msgid "Create list"
msgstr "Create list"
@@ -653,12 +670,12 @@ msgstr "Create new {0}"
msgid "Create new key"
msgstr "Create new key"
#: src/views/board/components/NewCardForm.tsx:404
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Create new label"
#: src/views/board/index.tsx:475
#: src/views/board/index.tsx:478
msgid "Create new list"
msgstr "Create new list"
@@ -671,7 +688,7 @@ msgid "Create template"
msgstr "Create template"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:148
#: src/components/WorkspaceMenu.tsx:149
msgid "Create workspace"
msgstr "Create workspace"
@@ -721,7 +738,7 @@ msgid "Custom workspace link"
msgstr "Custom workspace link"
#: src/components/NewWorkspaceForm.tsx:342
#: src/views/settings/components/UpgradeToProConfirmation.tsx:74
#: src/views/settings/components/UpgradeToProConfirmation.tsx:76
msgid "Custom workspace URL"
msgstr "Custom workspace URL"
@@ -733,7 +750,7 @@ msgstr "Customer Support"
msgid "Dark"
msgstr "Dark"
#: src/views/members/components/InviteMemberForm.tsx:341
#: src/views/members/components/InviteMemberForm.tsx:348
msgid "Deactivate invite link"
msgstr "Deactivate invite link"
@@ -773,8 +790,8 @@ msgid "Delete template"
msgstr "Delete template"
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
#: src/views/settings/WorkspaceSettings.tsx:96
#: src/views/settings/WorkspaceSettings.tsx:107
#: src/views/settings/WorkspaceSettings.tsx:97
#: src/views/settings/WorkspaceSettings.tsx:108
msgid "Delete workspace"
msgstr "Delete workspace"
@@ -828,6 +845,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"
@@ -854,8 +875,12 @@ msgstr "Don't have an account? <0><1>Sign up</1></0>"
msgid "Done"
msgstr "Done"
#: src/views/card/components/AttachmentThumbnails.tsx:141
msgid "Download failed"
msgstr "Download failed"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:115
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
msgstr "Edit board URL"
@@ -867,7 +892,7 @@ msgstr "Edit comment"
msgid "Edit label"
msgstr "Edit label"
#: src/views/board/components/UpdateBoardSlugForm.tsx:161
#: src/views/board/components/UpdateBoardSlugForm.tsx:162
msgid "Edit workspace URL"
msgstr "Edit workspace URL"
@@ -875,11 +900,11 @@ msgstr "Edit workspace URL"
msgid "Editing"
msgstr "Editing"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "email"
#: src/views/members/components/InviteMemberForm.tsx:265
#: src/views/members/components/InviteMemberForm.tsx:272
msgid "Email"
msgstr "Email"
@@ -895,11 +920,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:387
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Enter your email address"
#: src/components/AuthForm.tsx:375
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Enter your name"
@@ -907,13 +932,13 @@ msgstr "Enter your name"
msgid "Enter your new password"
msgstr "Enter your new password"
#: src/components/AuthForm.tsx:400
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Enter your password"
#: src/views/boards/components/NewBoardForm.tsx:77
#: src/views/boards/components/NewBoardForm.tsx:92
#: src/views/members/components/InviteMemberForm.tsx:208
#: src/views/members/components/InviteMemberForm.tsx:215
msgid "Error"
msgstr "Error"
@@ -970,7 +995,7 @@ msgstr "Error updating workspace name"
msgid "Error updating workspace URL"
msgstr "Error updating workspace URL"
#: src/views/members/components/InviteMemberForm.tsx:233
#: src/views/members/components/InviteMemberForm.tsx:240
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
msgid "Error upgrading subscription"
msgstr "Error upgrading subscription"
@@ -1002,7 +1027,7 @@ msgstr "Execution"
msgid "Failed to accept invitation. Please try again later, or contact customer support."
msgstr "Failed to accept invitation. Please try again later, or contact customer support."
#: src/views/members/components/InviteMemberForm.tsx:209
#: src/views/members/components/InviteMemberForm.tsx:216
msgid "Failed to copy invite link"
msgstr "Failed to copy invite link"
@@ -1016,6 +1041,11 @@ msgstr "Failed to create board"
msgid "Failed to login with {0}. Please try again."
msgstr "Failed to login with {0}. Please try again."
#: src/views/card/components/AttachmentUpload.tsx:33
#: src/views/card/components/AttachmentUpload.tsx:79
msgid "Failed to upload attachment. Please try again."
msgstr "Failed to upload attachment. Please try again."
#: src/views/home/components/Header.tsx:41
msgid "FAQ"
msgstr "FAQ"
@@ -1059,7 +1089,7 @@ msgstr "Feedback"
msgid "Feedback sent"
msgstr "Feedback sent"
#: src/views/board/components/Filters.tsx:149
#: src/views/board/components/Filters.tsx:174
msgid "Filter"
msgstr "Filter"
@@ -1086,7 +1116,7 @@ msgstr "For long-term sustainability, we recognise all good open source projects
msgid "Free"
msgstr "Free"
#: src/views/members/components/InviteMemberForm.tsx:326
#: src/views/members/components/InviteMemberForm.tsx:333
#: src/views/members/index.tsx:208
msgid "Free Plan"
msgstr "Free Plan"
@@ -1104,6 +1134,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
@@ -1127,7 +1161,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:467
#: src/views/board/index.tsx:470
msgid "Get started by creating a new list"
msgstr "Get started by creating a new list"
@@ -1161,6 +1195,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"
@@ -1290,11 +1340,11 @@ msgstr "Invite"
#~ msgid "Invite another"
#~ msgstr "Invite another"
#: src/views/members/components/InviteMemberForm.tsx:202
#: src/views/members/components/InviteMemberForm.tsx:209
msgid "Invite link copied"
msgstr "Invite link copied"
#: src/views/members/components/InviteMemberForm.tsx:203
#: src/views/members/components/InviteMemberForm.tsx:210
msgid "Invite link copied to clipboard"
msgstr "Invite link copied to clipboard"
@@ -1307,11 +1357,11 @@ msgid "Invite links require a Team or Pro subscription. Please upgrade your work
msgstr "Invite links require a Team or Pro subscription. Please upgrade your workspace."
#: src/views/card/components/MemberSelector.tsx:111
#: src/views/members/components/InviteMemberForm.tsx:365
#: src/views/members/components/InviteMemberForm.tsx:367
msgid "Invite member"
msgstr "Invite member"
#: src/views/members/components/InviteMemberForm.tsx:329
#: src/views/members/components/InviteMemberForm.tsx:336
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
msgstr "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
@@ -1349,9 +1399,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:101
#: src/views/board/components/NewCardForm.tsx:408
#: src/views/card/index.tsx:117
#: src/providers/keyboard-shortcuts.tsx:320
#~ msgid "Keyboard Shortcuts"
#~ msgstr "Keyboard Shortcuts"
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
msgid "Labels"
msgstr "Labels"
@@ -1368,7 +1422,7 @@ msgid "Language"
msgstr "Language"
#: src/components/NewWorkspaceForm.tsx:335
#: src/views/settings/components/UpgradeToProConfirmation.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:69
msgid "Launch offer"
msgstr "Launch offer"
@@ -1401,18 +1455,22 @@ msgstr "License"
msgid "Light"
msgstr "Light"
#: src/views/public/board/index.tsx:65
#: src/views/public/board/index.tsx:66
msgid "Link copied"
msgstr "Link copied"
#: src/views/card/index.tsx:109
#: src/views/card/index.tsx:111
msgid "List"
msgstr "List"
#: src/views/board/components/NewListForm.tsx:128
#: src/views/board/components/NewListForm.tsx:129
msgid "List name"
msgstr "List name"
#: src/views/board/components/Filters.tsx:123
msgid "Lists"
msgstr "Lists"
#: src/views/auth/login/index.tsx:33
msgid "Login | kan.bn"
msgstr "Login | kan.bn"
@@ -1433,7 +1491,7 @@ msgstr "Loved by teams worldwide"
msgid "Low Priority"
msgstr "Low Priority"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "magic link"
@@ -1454,10 +1512,10 @@ msgstr "marked checklist item <0>{0}</0> as incomplete"
msgid "Medium Priority"
msgstr "Medium Priority"
#: src/components/SideNavigation.tsx:80
#: src/views/board/components/Filters.tsx:93
#: src/views/board/components/NewCardForm.tsx:366
#: src/views/card/index.tsx:126
#: 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/views/members/index.tsx:178
msgid "Members"
msgstr "Members"
@@ -1500,6 +1558,10 @@ 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?"
@@ -1522,7 +1584,7 @@ msgstr "New API key"
#~ msgid "New board"
#~ msgstr "New board"
#: src/views/board/components/NewCardForm.tsx:292
#: src/views/board/components/NewCardForm.tsx:293
msgid "New card"
msgstr "New card"
@@ -1538,8 +1600,8 @@ msgstr "New import"
msgid "New label"
msgstr "New label"
#: src/views/board/components/NewListForm.tsx:112
#: src/views/board/index.tsx:439
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
msgid "New list"
msgstr "New list"
@@ -1580,6 +1642,10 @@ msgstr "Next Steps"
msgid "No {0}"
msgstr "No {0}"
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "No authentication methods are currently available"
#: src/views/boards/components/BoardsList.tsx:35
#~ msgid "No boards"
#~ msgstr "No boards"
@@ -1592,7 +1658,15 @@ msgstr "No boards found"
msgid "No credit card required"
msgstr "No credit card required"
#: src/views/board/index.tsx:464
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "No download URL available for this attachment."
#: src/providers/keyboard-shortcuts.tsx:333
#~ msgid "No keyboard shortcuts registered."
#~ msgstr "No keyboard shortcuts registered."
#: src/views/board/index.tsx:467
msgid "No lists"
msgstr "No lists"
@@ -1612,10 +1686,18 @@ msgstr "Onboarding"
msgid "Once you delete your account, there is no going back. This action cannot be undone."
msgstr "Once you delete your account, there is no going back. This action cannot be undone."
#: src/views/settings/WorkspaceSettings.tsx:99
#: src/views/settings/WorkspaceSettings.tsx:100
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"
@@ -1624,7 +1706,7 @@ msgstr "Open source"
msgid "Open Source Friends"
msgstr "Open Source Friends"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "or"
@@ -1706,15 +1788,15 @@ msgstr "Platform"
msgid "Please confirm your new password"
msgstr "Please confirm your new password"
#: src/components/AuthForm.tsx:391
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Please enter a valid email address"
#: src/components/AuthForm.tsx:379
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Please enter a valid name"
#: src/components/AuthForm.tsx:404
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Please enter a valid password"
@@ -1725,15 +1807,16 @@ 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:176
#: src/views/board/components/NewListForm.tsx:78
#: src/views/board/components/NewCardForm.tsx:177
#: 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:72
#: src/views/board/components/VisibilityButton.tsx:53
#: src/views/board/index.tsx:149
#: src/views/board/index.tsx:205
#: 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/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
#: src/views/card/components/ChecklistItemRow.tsx:88
#: src/views/card/components/ChecklistNameInput.tsx:46
@@ -1747,10 +1830,10 @@ msgstr "Please select a file to upload."
#: 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:175
#: src/views/card/index.tsx:177
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:234
#: src/views/members/components/InviteMemberForm.tsx:241
#: src/views/settings/components/Avatar.tsx:78
#: src/views/settings/components/Avatar.tsx:219
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
@@ -1783,7 +1866,7 @@ msgstr "Pricing"
#: src/views/pricing/components/Pricing.tsx:56
#: src/views/pricing/components/PricingTiers.tsx:53
#: src/views/settings/components/UpgradeToProConfirmation.tsx:91
#: src/views/settings/components/UpgradeToProConfirmation.tsx:93
msgid "Priority email support"
msgstr "Priority email support"
@@ -1791,7 +1874,7 @@ msgstr "Priority email support"
msgid "Privacy policy"
msgstr "Privacy policy"
#: src/views/board/components/VisibilityButton.tsx:73
#: src/views/board/components/VisibilityButton.tsx:74
msgid "Private"
msgstr "Private"
@@ -1799,7 +1882,7 @@ msgstr "Private"
msgid "Pro Plan"
msgstr "Pro Plan"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
msgid "Pro Plan ∞"
msgstr "Pro Plan ∞"
@@ -1811,7 +1894,7 @@ msgstr "Profile image updated"
msgid "Profile picture"
msgstr "Profile picture"
#: src/views/board/components/VisibilityButton.tsx:68
#: src/views/board/components/VisibilityButton.tsx:69
msgid "Public"
msgstr "Public"
@@ -1976,7 +2059,7 @@ msgid "Senior"
msgstr "Senior"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:85
#: src/components/SideNavigation.tsx:106
msgid "Settings"
msgstr "Settings"
@@ -2008,6 +2091,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"
@@ -2033,7 +2120,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:420
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Sign up with "
@@ -2058,6 +2145,11 @@ msgstr "Software Development"
msgid "Star on Github"
msgstr "Star on Github"
#: src/views/members/components/InviteMemberForm.tsx:359
#: src/views/settings/components/UpgradeToProConfirmation.tsx:111
msgid "Start 14 day free trial"
msgstr "Start 14 day free trial"
#: src/views/members/components/InviteMemberForm.tsx:122
msgid "Subscription Required"
msgstr "Subscription Required"
@@ -2067,7 +2159,7 @@ msgstr "Subscription Required"
msgid "Success"
msgstr "Success"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:55
#: src/views/settings/components/UpgradeToProConfirmation.tsx:57
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:"
@@ -2084,7 +2176,7 @@ msgstr "Support the development of the project"
msgid "System"
msgstr "System"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
#: src/views/members/index.tsx:207
msgid "Team Plan"
msgstr "Team Plan"
@@ -2095,8 +2187,8 @@ msgstr "Team Plan"
msgid "Teams"
msgstr "Teams"
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:396
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
msgid "Template"
msgstr "Template"
@@ -2116,7 +2208,7 @@ msgstr "Template name cannot exceed 100 characters"
msgid "Template name is required"
msgstr "Template name is required"
#: src/components/SideNavigation.tsx:75
#: src/components/SideNavigation.tsx:96
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Templates"
@@ -2146,7 +2238,7 @@ msgid "The open source <0/> alternative to Trello"
msgstr "The open source <0/> alternative to Trello"
#. placeholder {0}: isPublic ? "public" : "private"
#: src/views/board/components/VisibilityButton.tsx:46
#: src/views/board/components/VisibilityButton.tsx:47
msgid "The visibility of your board has been set to {0}."
msgstr "The visibility of your board has been set to {0}."
@@ -2170,11 +2262,11 @@ 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:154
#: src/views/public/board/index.tsx:156
msgid "This board is private or does not exist"
msgstr "This board is private or does not exist"
#: src/views/board/components/UpdateBoardSlugForm.tsx:135
#: src/views/board/components/UpdateBoardSlugForm.tsx:136
msgid "This board URL has already been taken"
msgstr "This board URL has already been taken"
@@ -2252,7 +2344,7 @@ msgstr "Unable to add checklist item"
msgid "Unable to add comment"
msgstr "Unable to add comment"
#: src/views/board/components/NewCardForm.tsx:173
#: src/views/board/components/NewCardForm.tsx:174
msgid "Unable to create card"
msgstr "Unable to create card"
@@ -2260,7 +2352,7 @@ msgstr "Unable to create card"
msgid "Unable to create checklist"
msgstr "Unable to create checklist"
#: src/views/board/components/NewListForm.tsx:77
#: src/views/board/components/NewListForm.tsx:78
msgid "Unable to create list"
msgstr "Unable to create list"
@@ -2273,6 +2365,10 @@ msgstr "Unable to create template"
msgid "Unable to create workspace"
msgstr "Unable to create workspace"
#: src/views/card/components/AttachmentThumbnails.tsx:72
msgid "Unable to delete attachment"
msgstr "Unable to delete attachment"
#: src/views/card/components/DeleteCardConfirmation.tsx:51
msgid "Unable to delete card"
msgstr "Unable to delete card"
@@ -2297,16 +2393,16 @@ msgstr "Unable to remove member"
msgid "Unable to send feedback"
msgstr "Unable to send feedback"
#: src/views/board/components/UpdateBoardSlugForm.tsx:71
#: src/views/board/components/UpdateBoardSlugForm.tsx:72
msgid "Unable to update board URL"
msgstr "Unable to update board URL"
#: src/views/board/components/VisibilityButton.tsx:52
#: src/views/board/components/VisibilityButton.tsx:53
msgid "Unable to update board visibility"
msgstr "Unable to update board visibility"
#: src/views/board/index.tsx:204
#: src/views/card/index.tsx:174
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
msgid "Unable to update card"
msgstr "Unable to update card"
@@ -2326,7 +2422,7 @@ msgstr "Unable to update comment"
msgid "Unable to update labels"
msgstr "Unable to update labels"
#: src/views/board/index.tsx:148
#: src/views/board/index.tsx:150
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Unable to update list"
@@ -2378,7 +2474,7 @@ msgid "Unlimited lists"
msgstr "Unlimited lists"
#: src/components/NewWorkspaceForm.tsx:332
#: src/views/settings/components/UpgradeToProConfirmation.tsx:64
#: src/views/settings/components/UpgradeToProConfirmation.tsx:66
msgid "Unlimited members"
msgstr "Unlimited members"
@@ -2386,7 +2482,12 @@ msgstr "Unlimited members"
msgid "Unlimited workspaces"
msgstr "Unlimited workspaces"
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
#: 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
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
@@ -2420,11 +2521,12 @@ msgstr "updated the title to <0>{0}</0>"
#~ msgstr "updated the title to <0>{toTitle}</0>"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:109
msgid "Upgrade"
msgstr "Upgrade"
#~ msgid "Upgrade"
#~ msgstr "Upgrade"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
#: src/views/settings/WorkspaceSettings.tsx:89
#: src/components/SideNavigation.tsx:202
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
msgstr "Upgrade to Pro"
@@ -2433,8 +2535,13 @@ msgid "Upgrade to Pro ($29/month)"
msgstr "Upgrade to Pro ($29/month)"
#: src/views/members/components/InviteMemberForm.tsx:357
msgid "Upgrade to Team Plan"
msgstr "Upgrade to Team Plan"
#~ msgid "Upgrade to Team Plan"
#~ msgstr "Upgrade to Team Plan"
#: src/views/card/components/AttachmentUpload.tsx:32
#: src/views/card/components/AttachmentUpload.tsx:78
msgid "Upload failed"
msgstr "Upload failed"
#: src/views/boards/components/TemplateBoards.tsx:38
#: src/views/boards/components/TemplateBoards.tsx:55
@@ -2485,7 +2592,7 @@ msgstr "View and manage your billing and subscription."
msgid "View docs"
msgstr "View docs"
#: src/views/public/board/index.tsx:128
#: src/views/public/board/index.tsx:129
msgid "View only"
msgstr "View only"
@@ -2493,11 +2600,11 @@ msgstr "View only"
msgid "View our roadmap."
msgstr "View our roadmap."
#: src/views/public/board/index.tsx:157
#: src/views/public/board/index.tsx:159
msgid "View workspace"
msgstr "View workspace"
#: src/views/board/components/VisibilityButton.tsx:91
#: src/views/board/components/VisibilityButton.tsx:92
msgid "Visibility"
msgstr "Visibility"
@@ -2505,6 +2612,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. "
@@ -2542,10 +2653,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:361
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:100
#: src/views/public/board/index.tsx:101
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Workspace"
@@ -2655,7 +2766,11 @@ msgstr "You have been logged in successfully."
msgid "You have been signed up successfully."
msgstr "You have been signed up successfully."
#: src/views/members/components/InviteMemberForm.tsx:319
#: 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!"
@@ -2679,6 +2794,10 @@ msgstr "Your boards have been imported."
msgid "Your display name has been updated."
msgstr "Your display name has been updated."
#: src/views/card/components/AttachmentUpload.tsx:26
msgid "Your file has been uploaded successfully."
msgstr "Your file has been uploaded successfully."
#: src/views/settings/components/ChangePasswordConfirmation.tsx:70
msgid "Your password has been changed."
msgstr "Your password has been changed."
@@ -2695,6 +2814,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

@@ -31,17 +31,17 @@ msgstr "{0}"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:442
#: src/views/board/components/NewCardForm.tsx:443
msgid "{0} labels"
msgstr "{0} etiquetas"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:387
#: src/views/board/index.tsx:389
msgid "{0} not found"
msgstr "{0} no encontrado"
@@ -63,6 +63,10 @@ msgstr "$8/mes"
msgid "1 user"
msgstr "1 usuario"
#: src/views/pricing/components/PricingTiers.tsx:136
msgid "14 day free trial"
msgstr "Prueba gratuita de 14 días"
#: src/views/home/index.tsx:71
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
msgstr "Una aplicación kanban potente y flexible que te ayuda a organizar el trabajo, seguir el progreso y entregar resultados, todo en un solo lugar."
@@ -75,8 +79,8 @@ msgstr "Cuenta"
msgid "Account deleted"
msgstr "Cuenta eliminada"
#: src/views/card/index.tsx:291
#: src/views/public/board/CardModal.tsx:143
#: src/views/card/index.tsx:343
#: src/views/public/board/CardModal.tsx:157
msgid "Activity"
msgstr "Actividad"
@@ -105,7 +109,7 @@ msgstr "Añadir un elemento..."
msgid "Add checklist"
msgstr "Añadir lista de verificación"
#: src/components/Editor.tsx:442
#: src/components/Editor.tsx:451
msgid "Add description... (type '/' to open commands or '@' to mention)"
msgstr "Añadir descripción... (escribe '/' para abrir comandos o '@' para mencionar)"
@@ -119,7 +123,7 @@ msgid "Add label"
msgstr "Añadir etiqueta"
#: src/views/card/components/MemberSelector.tsx:130
#: src/views/members/components/InviteMemberForm.tsx:250
#: src/views/members/components/InviteMemberForm.tsx:257
msgid "Add member"
msgstr "Añadir miembro"
@@ -154,7 +158,7 @@ msgstr "añadió el elemento <0>{0}</0> a la lista de verificación"
msgid "added label <0>{0}</0>"
msgstr "añadió la etiqueta <0>{0}</0>"
#: src/views/members/components/InviteMemberForm.tsx:320
#: src/views/members/components/InviteMemberForm.tsx:327
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
msgstr "Añadir un nuevo miembro costará {price} adicionales ({billingType}) por asiento."
@@ -183,7 +187,7 @@ msgstr "Ocurrió un error al desconectar tu cuenta de Trello."
msgid "An unexpected error occurred. Please try again later."
msgstr "Ha ocurrido un error inesperado. Por favor, inténtalo de nuevo más tarde."
#: src/views/members/components/InviteMemberForm.tsx:304
#: src/views/members/components/InviteMemberForm.tsx:311
msgid "Anyone with this link can join your workspace"
msgstr "Cualquier persona con este enlace puede unirse a tu espacio de trabajo"
@@ -266,6 +270,10 @@ msgstr "asignó <0>{0}</0> a la tarjeta"
msgid "Assignees"
msgstr "Asignados"
#: src/views/card/components/AttachmentUpload.tsx:25
msgid "Attachment uploaded"
msgstr "Archivo adjunto subido"
#: src/views/boards/components/TemplateBoards.tsx:47
msgid "Awaiting Customer"
msgstr "Esperando al cliente"
@@ -300,14 +308,14 @@ msgstr "Portal de facturación"
msgid "Blog Post"
msgstr "Entrada de blog"
#: src/views/board/index.tsx:361
#: src/views/card/index.tsx:216
#: src/views/public/board/index.tsx:100
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:229
#: src/views/public/board/index.tsx:101
msgid "Board"
msgstr "Tablero"
#: src/components/NewWorkspaceForm.tsx:349
#: src/views/settings/components/UpgradeToProConfirmation.tsx:81
#: src/views/settings/components/UpgradeToProConfirmation.tsx:83
msgid "Board analytics"
msgstr "Análisis del tablero"
@@ -319,7 +327,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:151
#: src/views/public/board/index.tsx:153
msgid "Board not found"
msgstr "Tablero no encontrado"
@@ -327,19 +335,19 @@ msgstr "Tablero no encontrado"
msgid "Board sharing & invites"
msgstr "Compartir tablero e invitaciones"
#: src/views/board/components/UpdateBoardSlugForm.tsx:45
#: src/views/board/components/UpdateBoardSlugForm.tsx:46
msgid "Board URL can only contain letters, numbers, and hyphens"
msgstr "La URL del tablero solo puede contener letras, números y guiones"
#: src/views/board/components/UpdateBoardSlugForm.tsx:43
#: src/views/board/components/UpdateBoardSlugForm.tsx:44
msgid "Board URL cannot exceed 60 characters"
msgstr "La URL del tablero no puede exceder los 60 caracteres"
#: src/views/public/board/index.tsx:67
#: src/views/public/board/index.tsx:68
msgid "Board URL copied to clipboard"
msgstr "URL del tablero copiada al portapapeles"
#: src/views/board/components/UpdateBoardSlugForm.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:42
msgid "Board URL must be at least 3 characters long"
msgstr "La URL del tablero debe tener al menos 3 caracteres"
@@ -348,11 +356,11 @@ msgstr "La URL del tablero debe tener al menos 3 caracteres"
msgid "Board visibility"
msgstr "Visibilidad del tablero"
#: src/views/board/components/VisibilityButton.tsx:45
#: src/views/board/components/VisibilityButton.tsx:46
msgid "Board visibility updated"
msgstr "Visibilidad del tablero actualizada"
#: src/components/SideNavigation.tsx:70
#: src/components/SideNavigation.tsx:91
msgid "Boards"
msgstr "Tableros"
@@ -379,19 +387,20 @@ msgstr "Informe de error"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
#: src/views/settings/components/UpgradeToProConfirmation.tsx:104
#: src/views/settings/components/UpgradeToProConfirmation.tsx:106
msgid "Cancel"
msgstr "Cancelar"
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "Card"
msgstr "Tarjeta"
#: src/views/card/index.tsx:262
#: src/views/card/index.tsx:263
#: src/views/card/index.tsx:299
msgid "Card not found"
msgstr "Tarjeta no encontrada"
#: src/views/board/components/NewCardForm.tsx:309
#: src/views/board/components/NewCardForm.tsx:310
msgid "Card title"
msgstr "Título de la tarjeta"
@@ -424,7 +433,7 @@ msgstr "Nombre de la lista de verificación"
msgid "Checklists"
msgstr "Listas de verificación"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/Filters.tsx:180
msgid "Clear filters"
msgstr "Borrar filtros"
@@ -447,7 +456,7 @@ msgstr "Colabora sin problemas con tu equipo."
#: src/components/NewWorkspaceForm.tsx:352
#: src/views/home/components/Features.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:84
#: src/views/settings/components/UpgradeToProConfirmation.tsx:86
msgid "Coming soon"
msgstr "Próximamente"
@@ -485,6 +494,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"
@@ -515,12 +528,12 @@ msgstr "Contáctanos"
msgid "Content Creation"
msgstr "Creación de contenido"
#: src/components/AuthForm.tsx:420
#: 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}"
@@ -538,8 +551,8 @@ msgid "Create {0}"
msgstr "Crear {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:468
#: src/views/board/components/NewListForm.tsx:140
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Crear otro"
@@ -547,7 +560,7 @@ msgstr "Crear otro"
msgid "Create API key"
msgstr "Crear clave API"
#: src/views/board/components/NewCardForm.tsx:478
#: src/views/board/components/NewCardForm.tsx:479
msgid "Create card"
msgstr "Crear tarjeta"
@@ -555,7 +568,7 @@ msgstr "Crear tarjeta"
msgid "Create checklist"
msgstr "Crear lista de verificación"
#: src/views/members/components/InviteMemberForm.tsx:342
#: src/views/members/components/InviteMemberForm.tsx:349
msgid "Create invite link"
msgstr "Crear enlace de invitación"
@@ -563,7 +576,7 @@ msgstr "Crear enlace de invitación"
msgid "Create label"
msgstr "Crear etiqueta"
#: src/views/board/components/NewListForm.tsx:152
#: src/views/board/components/NewListForm.tsx:153
msgid "Create list"
msgstr "Crear lista"
@@ -576,12 +589,12 @@ msgstr "Crear nuevo {0}"
msgid "Create new key"
msgstr "Crear nueva clave"
#: src/views/board/components/NewCardForm.tsx:404
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Crear nueva etiqueta"
#: src/views/board/index.tsx:475
#: src/views/board/index.tsx:478
msgid "Create new list"
msgstr "Crear nueva lista"
@@ -590,7 +603,7 @@ msgid "Create template"
msgstr "Crear plantilla"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:148
#: src/components/WorkspaceMenu.tsx:149
msgid "Create workspace"
msgstr "Crear espacio de trabajo"
@@ -632,7 +645,7 @@ msgid "Custom workspace link"
msgstr "Enlace de espacio de trabajo personalizado"
#: src/components/NewWorkspaceForm.tsx:342
#: src/views/settings/components/UpgradeToProConfirmation.tsx:74
#: src/views/settings/components/UpgradeToProConfirmation.tsx:76
msgid "Custom workspace URL"
msgstr "URL personalizada del espacio de trabajo"
@@ -644,7 +657,7 @@ msgstr "Atención al cliente"
msgid "Dark"
msgstr "Oscuro"
#: src/views/members/components/InviteMemberForm.tsx:341
#: src/views/members/components/InviteMemberForm.tsx:348
msgid "Deactivate invite link"
msgstr "Desactivar enlace de invitación"
@@ -684,8 +697,8 @@ msgid "Delete template"
msgstr "Eliminar plantilla"
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
#: src/views/settings/WorkspaceSettings.tsx:96
#: src/views/settings/WorkspaceSettings.tsx:107
#: src/views/settings/WorkspaceSettings.tsx:97
#: src/views/settings/WorkspaceSettings.tsx:108
msgid "Delete workspace"
msgstr "Eliminar espacio de trabajo"
@@ -739,6 +752,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"
@@ -765,8 +782,12 @@ msgstr "¿No tienes una cuenta? <0><1>Regístrate</1></0>"
msgid "Done"
msgstr "Terminado"
#: src/views/card/components/AttachmentThumbnails.tsx:141
msgid "Download failed"
msgstr "Error en la descarga"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:115
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
msgstr "Editar URL del tablero"
@@ -778,7 +799,7 @@ msgstr "Editar comentario"
msgid "Edit label"
msgstr "Editar etiqueta"
#: src/views/board/components/UpdateBoardSlugForm.tsx:161
#: src/views/board/components/UpdateBoardSlugForm.tsx:162
msgid "Edit workspace URL"
msgstr "Editar URL del espacio de trabajo"
@@ -786,11 +807,11 @@ msgstr "Editar URL del espacio de trabajo"
msgid "Editing"
msgstr "Editando"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "correo electrónico"
#: src/views/members/components/InviteMemberForm.tsx:265
#: src/views/members/components/InviteMemberForm.tsx:272
msgid "Email"
msgstr "Correo electrónico"
@@ -806,11 +827,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:387
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Introduce tu dirección de correo electrónico"
#: src/components/AuthForm.tsx:375
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Introduce tu nombre"
@@ -818,13 +839,13 @@ msgstr "Introduce tu nombre"
msgid "Enter your new password"
msgstr "Introduce tu nueva contraseña"
#: src/components/AuthForm.tsx:400
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Introduce tu contraseña"
#: src/views/boards/components/NewBoardForm.tsx:77
#: src/views/boards/components/NewBoardForm.tsx:92
#: src/views/members/components/InviteMemberForm.tsx:208
#: src/views/members/components/InviteMemberForm.tsx:215
msgid "Error"
msgstr "Error"
@@ -881,7 +902,7 @@ msgstr "Error al actualizar el nombre del espacio de trabajo"
msgid "Error updating workspace URL"
msgstr "Error al actualizar la URL del espacio de trabajo"
#: src/views/members/components/InviteMemberForm.tsx:233
#: src/views/members/components/InviteMemberForm.tsx:240
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
msgid "Error upgrading subscription"
msgstr "Error al actualizar la suscripción"
@@ -913,7 +934,7 @@ msgstr "Ejecución"
msgid "Failed to accept invitation. Please try again later, or contact customer support."
msgstr "No se pudo aceptar la invitación. Por favor, inténtalo de nuevo más tarde o contacta con atención al cliente."
#: src/views/members/components/InviteMemberForm.tsx:209
#: src/views/members/components/InviteMemberForm.tsx:216
msgid "Failed to copy invite link"
msgstr "No se pudo copiar el enlace de invitación"
@@ -927,6 +948,11 @@ msgstr "Error al crear el tablero"
msgid "Failed to login with {0}. Please try again."
msgstr "Error al iniciar sesión con {0}. Por favor, inténtalo de nuevo."
#: src/views/card/components/AttachmentUpload.tsx:33
#: src/views/card/components/AttachmentUpload.tsx:79
msgid "Failed to upload attachment. Please try again."
msgstr "Error al subir el archivo adjunto. Por favor, inténtalo de nuevo."
#: src/views/home/components/Header.tsx:41
msgid "FAQ"
msgstr "FAQ"
@@ -970,7 +996,7 @@ msgstr "Comentarios"
msgid "Feedback sent"
msgstr "Comentarios enviados"
#: src/views/board/components/Filters.tsx:149
#: src/views/board/components/Filters.tsx:174
msgid "Filter"
msgstr "Filtrar"
@@ -983,13 +1009,11 @@ msgid "For long-term sustainability, we recognise all good open source projects
msgstr "Para la sostenibilidad a largo plazo, reconocemos que todos los buenos proyectos de código abierto necesitan una fuente de ingresos. La nuestra proviene de la oferta de nube de pago para espacios de trabajo con múltiples usuarios y para slugs de espacio de trabajo personalizados. No hay obligación de usarla, y puedes alojar el software en tu propia infraestructura sin costo alguno."
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/PricingTiers.tsx:30
#: src/views/pricing/components/PricingTiers.tsx:30
msgid "Free"
msgstr "Gratis"
#: src/views/members/components/InviteMemberForm.tsx:326
#: src/views/members/components/InviteMemberForm.tsx:333
#: src/views/members/index.tsx:208
msgid "Free Plan"
msgstr "Plan gratuito"
@@ -1026,7 +1050,7 @@ msgstr "Comenzar"
msgid "Get started by creating a new {0}"
msgstr "Comienza creando un nuevo {0}"
#: src/views/board/index.tsx:467
#: src/views/board/index.tsx:470
msgid "Get started by creating a new list"
msgstr "Comienza creando una nueva lista"
@@ -1181,11 +1205,11 @@ msgstr "Invitación inválida"
msgid "Invite"
msgstr "Invitar"
#: src/views/members/components/InviteMemberForm.tsx:202
#: src/views/members/components/InviteMemberForm.tsx:209
msgid "Invite link copied"
msgstr "Enlace de invitación copiado"
#: src/views/members/components/InviteMemberForm.tsx:203
#: src/views/members/components/InviteMemberForm.tsx:210
msgid "Invite link copied to clipboard"
msgstr "Enlace de invitación copiado al portapapeles"
@@ -1198,11 +1222,11 @@ msgid "Invite links require a Team or Pro subscription. Please upgrade your work
msgstr "Los enlaces de invitación requieren una suscripción de Equipo o Pro. Por favor, actualiza tu espacio de trabajo."
#: src/views/card/components/MemberSelector.tsx:111
#: src/views/members/components/InviteMemberForm.tsx:365
#: src/views/members/components/InviteMemberForm.tsx:367
msgid "Invite member"
msgstr "Invitar miembro"
#: src/views/members/components/InviteMemberForm.tsx:329
#: src/views/members/components/InviteMemberForm.tsx:336
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
msgstr "Invitar miembros requiere un Plan de Equipo. Serás redirigido para actualizar tu espacio de trabajo."
@@ -1240,9 +1264,9 @@ 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:101
#: src/views/board/components/NewCardForm.tsx:408
#: src/views/card/index.tsx:117
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
msgid "Labels"
msgstr "Etiquetas"
@@ -1259,7 +1283,7 @@ msgid "Language"
msgstr "Idioma"
#: src/components/NewWorkspaceForm.tsx:335
#: src/views/settings/components/UpgradeToProConfirmation.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:69
msgid "Launch offer"
msgstr "Oferta de lanzamiento"
@@ -1292,18 +1316,22 @@ msgstr "Licencia"
msgid "Light"
msgstr "Claro"
#: src/views/public/board/index.tsx:65
#: src/views/public/board/index.tsx:66
msgid "Link copied"
msgstr "Enlace copiado"
#: src/views/card/index.tsx:109
#: src/views/card/index.tsx:111
msgid "List"
msgstr "Lista"
#: src/views/board/components/NewListForm.tsx:128
#: src/views/board/components/NewListForm.tsx:129
msgid "List name"
msgstr "Nombre de la lista"
#: src/views/board/components/Filters.tsx:123
msgid "Lists"
msgstr "Listas"
#: src/views/auth/login/index.tsx:33
msgid "Login | kan.bn"
msgstr "Iniciar sesión | kan.bn"
@@ -1324,7 +1352,7 @@ msgstr "Amado por equipos en todo el mundo"
msgid "Low Priority"
msgstr "Prioridad baja"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "enlace mágico"
@@ -1345,10 +1373,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:80
#: src/views/board/components/Filters.tsx:93
#: src/views/board/components/NewCardForm.tsx:366
#: src/views/card/index.tsx:126
#: 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/views/members/index.tsx:178
msgid "Members"
msgstr "Miembros"
@@ -1401,7 +1429,7 @@ msgstr "Nuevo {0}"
msgid "New API key"
msgstr "Nueva clave API"
#: src/views/board/components/NewCardForm.tsx:292
#: src/views/board/components/NewCardForm.tsx:293
msgid "New card"
msgstr "Nueva tarjeta"
@@ -1417,8 +1445,8 @@ msgstr "Nueva importación"
msgid "New label"
msgstr "Nueva etiqueta"
#: src/views/board/components/NewListForm.tsx:112
#: src/views/board/index.tsx:439
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
msgid "New list"
msgstr "Nueva lista"
@@ -1455,6 +1483,10 @@ msgstr "Próximos pasos"
msgid "No {0}"
msgstr "Sin {0}"
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "No hay métodos de autenticación disponibles actualmente"
#: src/views/boards/components/ImportBoardsForm.tsx:242
msgid "No boards found"
msgstr "No se encontraron tableros"
@@ -1463,7 +1495,11 @@ msgstr "No se encontraron tableros"
msgid "No credit card required"
msgstr "No se requiere tarjeta de crédito"
#: src/views/board/index.tsx:464
#: 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
msgid "No lists"
msgstr "Sin listas"
@@ -1483,7 +1519,7 @@ msgstr "Incorporación"
msgid "Once you delete your account, there is no going back. This action cannot be undone."
msgstr "Una vez que elimines tu cuenta, no hay vuelta atrás. Esta acción no se puede deshacer."
#: src/views/settings/WorkspaceSettings.tsx:99
#: src/views/settings/WorkspaceSettings.tsx:100
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."
@@ -1495,7 +1531,7 @@ msgstr "Código abierto"
msgid "Open Source Friends"
msgstr "Amigos de código abierto"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "o"
@@ -1577,15 +1613,15 @@ msgstr "Plataforma"
msgid "Please confirm your new password"
msgstr "Por favor, confirma tu nueva contraseña"
#: src/components/AuthForm.tsx:391
#: 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:379
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Por favor, introduce un nombre válido"
#: src/components/AuthForm.tsx:404
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Por favor, introduce una contraseña válida"
@@ -1596,15 +1632,16 @@ 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:176
#: src/views/board/components/NewListForm.tsx:78
#: src/views/board/components/NewCardForm.tsx:177
#: 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:72
#: src/views/board/components/VisibilityButton.tsx:53
#: src/views/board/index.tsx:149
#: src/views/board/index.tsx:205
#: 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/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
#: src/views/card/components/ChecklistItemRow.tsx:88
#: src/views/card/components/ChecklistNameInput.tsx:46
@@ -1618,10 +1655,10 @@ msgstr "Por favor selecciona un archivo para subir."
#: 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:175
#: src/views/card/index.tsx:177
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:234
#: src/views/members/components/InviteMemberForm.tsx:241
#: src/views/settings/components/Avatar.tsx:78
#: src/views/settings/components/Avatar.tsx:219
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
@@ -1654,7 +1691,7 @@ msgstr "Precios"
#: src/views/pricing/components/Pricing.tsx:56
#: src/views/pricing/components/PricingTiers.tsx:53
#: src/views/settings/components/UpgradeToProConfirmation.tsx:91
#: src/views/settings/components/UpgradeToProConfirmation.tsx:93
msgid "Priority email support"
msgstr "Soporte prioritario por email"
@@ -1662,7 +1699,7 @@ msgstr "Soporte prioritario por email"
msgid "Privacy policy"
msgstr "Política de privacidad"
#: src/views/board/components/VisibilityButton.tsx:73
#: src/views/board/components/VisibilityButton.tsx:74
msgid "Private"
msgstr "Privado"
@@ -1670,7 +1707,7 @@ msgstr "Privado"
msgid "Pro Plan"
msgstr "Plan Pro"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
msgid "Pro Plan ∞"
msgstr "Plan Pro ∞"
@@ -1682,7 +1719,7 @@ msgstr "Imagen de perfil actualizada"
msgid "Profile picture"
msgstr "Foto de perfil"
#: src/views/board/components/VisibilityButton.tsx:68
#: src/views/board/components/VisibilityButton.tsx:69
msgid "Public"
msgstr "Público"
@@ -1835,7 +1872,7 @@ msgid "Senior"
msgstr "Senior"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:85
#: src/components/SideNavigation.tsx:106
msgid "Settings"
msgstr "Configuración"
@@ -1884,7 +1921,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:420
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Registrarse con "
@@ -1909,6 +1946,11 @@ msgstr "Desarrollo de software"
msgid "Star on Github"
msgstr "Estrella en Github"
#: src/views/members/components/InviteMemberForm.tsx:359
#: src/views/settings/components/UpgradeToProConfirmation.tsx:111
msgid "Start 14 day free trial"
msgstr "Iniciar prueba gratuita de 14 días"
#: src/views/members/components/InviteMemberForm.tsx:122
msgid "Subscription Required"
msgstr "Suscripción requerida"
@@ -1918,7 +1960,7 @@ msgstr "Suscripción requerida"
msgid "Success"
msgstr "Éxito"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:55
#: src/views/settings/components/UpgradeToProConfirmation.tsx:57
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:"
@@ -1935,7 +1977,7 @@ msgstr "Apoya el desarrollo del proyecto"
msgid "System"
msgstr "Sistema"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
#: src/views/members/index.tsx:207
msgid "Team Plan"
msgstr "Plan de Equipo"
@@ -1946,8 +1988,8 @@ msgstr "Plan de Equipo"
msgid "Teams"
msgstr "Equipos"
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:396
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
msgid "Template"
msgstr "Plantilla"
@@ -1967,7 +2009,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:75
#: src/components/SideNavigation.tsx:96
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Plantillas"
@@ -1997,7 +2039,7 @@ msgid "The open source <0/> alternative to Trello"
msgstr "La alternativa de código abierto <0/> a Trello"
#. placeholder {0}: isPublic ? "public" : "private"
#: src/views/board/components/VisibilityButton.tsx:46
#: src/views/board/components/VisibilityButton.tsx:47
msgid "The visibility of your board has been set to {0}."
msgstr "La visibilidad de tu tablero ha sido establecida a {0}."
@@ -2021,11 +2063,11 @@ 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:154
#: src/views/public/board/index.tsx:156
msgid "This board is private or does not exist"
msgstr "Este tablero es privado o no existe"
#: src/views/board/components/UpdateBoardSlugForm.tsx:135
#: src/views/board/components/UpdateBoardSlugForm.tsx:136
msgid "This board URL has already been taken"
msgstr "Esta URL de tablero ya ha sido utilizada"
@@ -2095,7 +2137,7 @@ 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:173
#: src/views/board/components/NewCardForm.tsx:174
msgid "Unable to create card"
msgstr "No se puede crear la tarjeta"
@@ -2103,7 +2145,7 @@ msgstr "No se puede crear la tarjeta"
msgid "Unable to create checklist"
msgstr "No se puede crear la lista de verificación"
#: src/views/board/components/NewListForm.tsx:77
#: src/views/board/components/NewListForm.tsx:78
msgid "Unable to create list"
msgstr "No se puede crear la lista"
@@ -2116,6 +2158,10 @@ msgstr "No se puede crear la plantilla"
msgid "Unable to create workspace"
msgstr "No se puede crear el espacio de trabajo"
#: src/views/card/components/AttachmentThumbnails.tsx:72
msgid "Unable to delete attachment"
msgstr "No se puede eliminar el archivo adjunto"
#: src/views/card/components/DeleteCardConfirmation.tsx:51
msgid "Unable to delete card"
msgstr "No se puede eliminar la tarjeta"
@@ -2140,16 +2186,16 @@ msgstr "No se puede eliminar al miembro"
msgid "Unable to send feedback"
msgstr "No se pudieron enviar los comentarios"
#: src/views/board/components/UpdateBoardSlugForm.tsx:71
#: src/views/board/components/UpdateBoardSlugForm.tsx:72
msgid "Unable to update board URL"
msgstr "No se puede actualizar la URL del tablero"
#: src/views/board/components/VisibilityButton.tsx:52
#: src/views/board/components/VisibilityButton.tsx:53
msgid "Unable to update board visibility"
msgstr "No se puede actualizar la visibilidad del tablero"
#: src/views/board/index.tsx:204
#: src/views/card/index.tsx:174
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
msgid "Unable to update card"
msgstr "No se puede actualizar la tarjeta"
@@ -2169,7 +2215,7 @@ msgstr "No se puede actualizar el comentario"
msgid "Unable to update labels"
msgstr "No se pueden actualizar las etiquetas"
#: src/views/board/index.tsx:148
#: src/views/board/index.tsx:150
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "No se puede actualizar la lista"
@@ -2217,7 +2263,7 @@ msgid "Unlimited lists"
msgstr "Listas ilimitadas"
#: src/components/NewWorkspaceForm.tsx:332
#: src/views/settings/components/UpgradeToProConfirmation.tsx:64
#: src/views/settings/components/UpgradeToProConfirmation.tsx:66
msgid "Unlimited members"
msgstr "Miembros ilimitados"
@@ -2225,7 +2271,12 @@ msgstr "Miembros ilimitados"
msgid "Unlimited workspaces"
msgstr "Espacios de trabajo ilimitados"
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
#: 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
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
@@ -2254,12 +2305,9 @@ msgstr "actualizó el título"
msgid "updated the title to <0>{0}</0>"
msgstr "actualizó el título a <0>{0}</0>"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:109
msgid "Upgrade"
msgstr "Actualizar"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
#: src/views/settings/WorkspaceSettings.tsx:89
#: src/components/SideNavigation.tsx:202
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
msgstr "Actualizar a Pro"
@@ -2267,9 +2315,10 @@ msgstr "Actualizar a Pro"
msgid "Upgrade to Pro ($29/month)"
msgstr "Actualizar a Pro ($29/mes)"
#: src/views/members/components/InviteMemberForm.tsx:357
msgid "Upgrade to Team Plan"
msgstr "Actualizar al Plan de Equipo"
#: src/views/card/components/AttachmentUpload.tsx:32
#: src/views/card/components/AttachmentUpload.tsx:78
msgid "Upload failed"
msgstr "Error al subir"
#: src/views/boards/components/TemplateBoards.tsx:38
#: src/views/boards/components/TemplateBoards.tsx:55
@@ -2320,7 +2369,7 @@ msgstr "Ver y gestionar tu facturación y suscripción."
msgid "View docs"
msgstr "Ver documentación"
#: src/views/public/board/index.tsx:128
#: src/views/public/board/index.tsx:129
msgid "View only"
msgstr "Solo visualización"
@@ -2328,11 +2377,11 @@ msgstr "Solo visualización"
msgid "View our roadmap."
msgstr "Ver nuestra hoja de ruta."
#: src/views/public/board/index.tsx:157
#: src/views/public/board/index.tsx:159
msgid "View workspace"
msgstr "Ver espacio de trabajo"
#: src/views/board/components/VisibilityButton.tsx:91
#: src/views/board/components/VisibilityButton.tsx:92
msgid "Visibility"
msgstr "Visibilidad"
@@ -2340,6 +2389,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. "
@@ -2369,10 +2422,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:361
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:100
#: src/views/public/board/index.tsx:101
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Espacio de trabajo"
@@ -2478,7 +2531,11 @@ msgstr "Has iniciado sesión correctamente."
msgid "You have been signed up successfully."
msgstr "Te has registrado correctamente."
#: src/views/members/components/InviteMemberForm.tsx:319
#: 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!"
@@ -2502,6 +2559,10 @@ msgstr "Tus tableros han sido importados."
msgid "Your display name has been updated."
msgstr "Tu nombre de visualización ha sido actualizado."
#: src/views/card/components/AttachmentUpload.tsx:26
msgid "Your file has been uploaded successfully."
msgstr "Tu archivo se ha subido correctamente."
#: src/views/settings/components/ChangePasswordConfirmation.tsx:70
msgid "Your password has been changed."
msgstr "Tu contraseña ha sido cambiada."
@@ -2518,6 +2579,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

@@ -31,17 +31,17 @@ msgstr "{0}"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:442
#: src/views/board/components/NewCardForm.tsx:443
msgid "{0} labels"
msgstr "{0} étiquettes"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:387
#: src/views/board/index.tsx:389
msgid "{0} not found"
msgstr "{0} introuvable"
@@ -63,6 +63,10 @@ msgstr "8 $/mois"
msgid "1 user"
msgstr "1 utilisateur"
#: src/views/pricing/components/PricingTiers.tsx:136
msgid "14 day free trial"
msgstr "Essai gratuit de 14 jours"
#: src/views/home/index.tsx:71
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
msgstr "Une application kanban puissante et flexible qui vous aide à organiser le travail, suivre les progrès et livrer des résultats—le tout en un seul endroit."
@@ -75,8 +79,8 @@ msgstr "Compte"
msgid "Account deleted"
msgstr "Compte supprimé"
#: src/views/card/index.tsx:291
#: src/views/public/board/CardModal.tsx:143
#: src/views/card/index.tsx:343
#: src/views/public/board/CardModal.tsx:157
msgid "Activity"
msgstr "Activité"
@@ -105,7 +109,7 @@ msgstr "Ajouter un élément..."
msgid "Add checklist"
msgstr "Ajouter une checklist"
#: src/components/Editor.tsx:442
#: src/components/Editor.tsx:451
msgid "Add description... (type '/' to open commands or '@' to mention)"
msgstr "Ajouter une description... (tapez '/' pour ouvrir les commandes ou '@' pour mentionner)"
@@ -119,7 +123,7 @@ msgid "Add label"
msgstr "Ajouter une étiquette"
#: src/views/card/components/MemberSelector.tsx:130
#: src/views/members/components/InviteMemberForm.tsx:250
#: src/views/members/components/InviteMemberForm.tsx:257
msgid "Add member"
msgstr "Ajouter un membre"
@@ -154,7 +158,7 @@ msgstr "a ajouté l'élément <0>{0}</0> à la checklist"
msgid "added label <0>{0}</0>"
msgstr "a ajouté l'étiquette <0>{0}</0>"
#: src/views/members/components/InviteMemberForm.tsx:320
#: src/views/members/components/InviteMemberForm.tsx:327
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
msgstr "L'ajout d'un nouveau membre coûtera {price} supplémentaires ({billingType}) par siège."
@@ -183,7 +187,7 @@ msgstr "Une erreur s'est produite lors de la déconnexion de votre compte Trello
msgid "An unexpected error occurred. Please try again later."
msgstr "Une erreur inattendue s'est produite. Veuillez réessayer plus tard."
#: src/views/members/components/InviteMemberForm.tsx:304
#: src/views/members/components/InviteMemberForm.tsx:311
msgid "Anyone with this link can join your workspace"
msgstr "Toute personne disposant de ce lien peut rejoindre votre espace de travail"
@@ -266,6 +270,10 @@ msgstr "a assigné <0>{0}</0> à la carte"
msgid "Assignees"
msgstr "Assignés"
#: src/views/card/components/AttachmentUpload.tsx:25
msgid "Attachment uploaded"
msgstr "Pièce jointe téléversée"
#: src/views/boards/components/TemplateBoards.tsx:47
msgid "Awaiting Customer"
msgstr "En attente du client"
@@ -300,14 +308,14 @@ msgstr "Portail de facturation"
msgid "Blog Post"
msgstr "Article de blog"
#: src/views/board/index.tsx:361
#: src/views/card/index.tsx:216
#: src/views/public/board/index.tsx:100
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:229
#: src/views/public/board/index.tsx:101
msgid "Board"
msgstr "Tableau"
#: src/components/NewWorkspaceForm.tsx:349
#: src/views/settings/components/UpgradeToProConfirmation.tsx:81
#: src/views/settings/components/UpgradeToProConfirmation.tsx:83
msgid "Board analytics"
msgstr "Analyses du tableau"
@@ -319,7 +327,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:151
#: src/views/public/board/index.tsx:153
msgid "Board not found"
msgstr "Tableau introuvable"
@@ -327,19 +335,19 @@ msgstr "Tableau introuvable"
msgid "Board sharing & invites"
msgstr "Partage de tableau & invitations"
#: src/views/board/components/UpdateBoardSlugForm.tsx:45
#: src/views/board/components/UpdateBoardSlugForm.tsx:46
msgid "Board URL can only contain letters, numbers, and hyphens"
msgstr "L'URL du tableau ne peut contenir que des lettres, des chiffres et des traits d'union"
#: src/views/board/components/UpdateBoardSlugForm.tsx:43
#: src/views/board/components/UpdateBoardSlugForm.tsx:44
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:67
#: src/views/public/board/index.tsx:68
msgid "Board URL copied to clipboard"
msgstr "URL du tableau copiée dans le presse-papiers"
#: src/views/board/components/UpdateBoardSlugForm.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:42
msgid "Board URL must be at least 3 characters long"
msgstr "L'URL du tableau doit comporter au moins 3 caractères"
@@ -348,11 +356,11 @@ msgstr "L'URL du tableau doit comporter au moins 3 caractères"
msgid "Board visibility"
msgstr "Visibilité du tableau"
#: src/views/board/components/VisibilityButton.tsx:45
#: src/views/board/components/VisibilityButton.tsx:46
msgid "Board visibility updated"
msgstr "Visibilité du tableau mise à jour"
#: src/components/SideNavigation.tsx:70
#: src/components/SideNavigation.tsx:91
msgid "Boards"
msgstr "Tableaux"
@@ -379,19 +387,20 @@ msgstr "Rapport de bug"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
#: src/views/settings/components/UpgradeToProConfirmation.tsx:104
#: src/views/settings/components/UpgradeToProConfirmation.tsx:106
msgid "Cancel"
msgstr "Annuler"
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "Card"
msgstr "Carte"
#: src/views/card/index.tsx:262
#: src/views/card/index.tsx:263
#: src/views/card/index.tsx:299
msgid "Card not found"
msgstr "Carte introuvable"
#: src/views/board/components/NewCardForm.tsx:309
#: src/views/board/components/NewCardForm.tsx:310
msgid "Card title"
msgstr "Titre de la carte"
@@ -424,7 +433,7 @@ msgstr "Nom de la checklist"
msgid "Checklists"
msgstr "Listes de contrôle"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/Filters.tsx:180
msgid "Clear filters"
msgstr "Effacer les filtres"
@@ -447,7 +456,7 @@ msgstr "Collaborez en toute fluidité avec votre équipe."
#: src/components/NewWorkspaceForm.tsx:352
#: src/views/home/components/Features.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:84
#: src/views/settings/components/UpgradeToProConfirmation.tsx:86
msgid "Coming soon"
msgstr "Bientôt disponible"
@@ -485,6 +494,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"
@@ -515,12 +528,12 @@ msgstr "Contactez-nous"
msgid "Content Creation"
msgstr "Création de contenu"
#: src/components/AuthForm.tsx:420
#: 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}"
@@ -538,8 +551,8 @@ msgid "Create {0}"
msgstr "Créer {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:468
#: src/views/board/components/NewListForm.tsx:140
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Créer un autre"
@@ -547,7 +560,7 @@ msgstr "Créer un autre"
msgid "Create API key"
msgstr "Créer une clé API"
#: src/views/board/components/NewCardForm.tsx:478
#: src/views/board/components/NewCardForm.tsx:479
msgid "Create card"
msgstr "Créer une carte"
@@ -555,7 +568,7 @@ msgstr "Créer une carte"
msgid "Create checklist"
msgstr "Créer une checklist"
#: src/views/members/components/InviteMemberForm.tsx:342
#: src/views/members/components/InviteMemberForm.tsx:349
msgid "Create invite link"
msgstr "Créer un lien d'invitation"
@@ -563,7 +576,7 @@ msgstr "Créer un lien d'invitation"
msgid "Create label"
msgstr "Créer une étiquette"
#: src/views/board/components/NewListForm.tsx:152
#: src/views/board/components/NewListForm.tsx:153
msgid "Create list"
msgstr "Créer une liste"
@@ -576,12 +589,12 @@ msgstr "Créer un nouveau {0}"
msgid "Create new key"
msgstr "Créer une nouvelle clé"
#: src/views/board/components/NewCardForm.tsx:404
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Créer une nouvelle étiquette"
#: src/views/board/index.tsx:475
#: src/views/board/index.tsx:478
msgid "Create new list"
msgstr "Créer une nouvelle liste"
@@ -590,7 +603,7 @@ msgid "Create template"
msgstr "Créer un modèle"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:148
#: src/components/WorkspaceMenu.tsx:149
msgid "Create workspace"
msgstr "Créer un espace de travail"
@@ -632,7 +645,7 @@ msgid "Custom workspace link"
msgstr "Lien d'espace de travail personnalisé"
#: src/components/NewWorkspaceForm.tsx:342
#: src/views/settings/components/UpgradeToProConfirmation.tsx:74
#: src/views/settings/components/UpgradeToProConfirmation.tsx:76
msgid "Custom workspace URL"
msgstr "URL d'espace de travail personnalisée"
@@ -644,7 +657,7 @@ msgstr "Support client"
msgid "Dark"
msgstr "Sombre"
#: src/views/members/components/InviteMemberForm.tsx:341
#: src/views/members/components/InviteMemberForm.tsx:348
msgid "Deactivate invite link"
msgstr "Désactiver le lien d'invitation"
@@ -684,8 +697,8 @@ msgid "Delete template"
msgstr "Supprimer le modèle"
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
#: src/views/settings/WorkspaceSettings.tsx:96
#: src/views/settings/WorkspaceSettings.tsx:107
#: src/views/settings/WorkspaceSettings.tsx:97
#: src/views/settings/WorkspaceSettings.tsx:108
msgid "Delete workspace"
msgstr "Supprimer l'espace de travail"
@@ -739,6 +752,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"
@@ -765,8 +782,12 @@ msgstr "Vous n'avez pas de compte ? <0><1>Inscrivez-vous</1></0>"
msgid "Done"
msgstr "Fait"
#: src/views/card/components/AttachmentThumbnails.tsx:141
msgid "Download failed"
msgstr "Échec du téléchargement"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:115
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
msgstr "Modifier l'URL du tableau"
@@ -778,7 +799,7 @@ msgstr "Modifier le commentaire"
msgid "Edit label"
msgstr "Modifier l'étiquette"
#: src/views/board/components/UpdateBoardSlugForm.tsx:161
#: src/views/board/components/UpdateBoardSlugForm.tsx:162
msgid "Edit workspace URL"
msgstr "Modifier l'URL de l'espace de travail"
@@ -786,11 +807,11 @@ msgstr "Modifier l'URL de l'espace de travail"
msgid "Editing"
msgstr "Édition"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "e-mail"
#: src/views/members/components/InviteMemberForm.tsx:265
#: src/views/members/components/InviteMemberForm.tsx:272
msgid "Email"
msgstr "E-mail"
@@ -806,11 +827,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:387
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Saisissez votre adresse e-mail"
#: src/components/AuthForm.tsx:375
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Saisissez votre nom"
@@ -818,13 +839,13 @@ msgstr "Saisissez votre nom"
msgid "Enter your new password"
msgstr "Saisissez votre nouveau mot de passe"
#: src/components/AuthForm.tsx:400
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Saisissez votre mot de passe"
#: src/views/boards/components/NewBoardForm.tsx:77
#: src/views/boards/components/NewBoardForm.tsx:92
#: src/views/members/components/InviteMemberForm.tsx:208
#: src/views/members/components/InviteMemberForm.tsx:215
msgid "Error"
msgstr "Erreur"
@@ -881,7 +902,7 @@ msgstr "Erreur lors de la mise à jour du nom de l'espace de travail"
msgid "Error updating workspace URL"
msgstr "Erreur lors de la mise à jour de l'URL de l'espace de travail"
#: src/views/members/components/InviteMemberForm.tsx:233
#: src/views/members/components/InviteMemberForm.tsx:240
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
msgid "Error upgrading subscription"
msgstr "Erreur lors de la mise à niveau de l'abonnement"
@@ -913,7 +934,7 @@ msgstr "Exécution"
msgid "Failed to accept invitation. Please try again later, or contact customer support."
msgstr "Échec de l'acceptation de l'invitation. Veuillez réessayer plus tard ou contacter le service client."
#: src/views/members/components/InviteMemberForm.tsx:209
#: src/views/members/components/InviteMemberForm.tsx:216
msgid "Failed to copy invite link"
msgstr "Échec de la copie du lien d'invitation"
@@ -927,6 +948,11 @@ msgstr "Échec de la création du tableau"
msgid "Failed to login with {0}. Please try again."
msgstr "Échec de connexion avec {0}. Veuillez réessayer."
#: src/views/card/components/AttachmentUpload.tsx:33
#: src/views/card/components/AttachmentUpload.tsx:79
msgid "Failed to upload attachment. Please try again."
msgstr "Échec du téléversement de la pièce jointe. Veuillez réessayer."
#: src/views/home/components/Header.tsx:41
msgid "FAQ"
msgstr "FAQ"
@@ -970,7 +996,7 @@ msgstr "Commentaires"
msgid "Feedback sent"
msgstr "Commentaires envoyés"
#: src/views/board/components/Filters.tsx:149
#: src/views/board/components/Filters.tsx:174
msgid "Filter"
msgstr "Filtrer"
@@ -983,13 +1009,11 @@ msgid "For long-term sustainability, we recognise all good open source projects
msgstr "Pour une durabilité à long terme, nous reconnaissons que tous les bons projets open source ont besoin d'une source de revenus. La nôtre provient de l'offre cloud payante pour les espaces de travail avec plusieurs utilisateurs et pour les slugs d'espace de travail personnalisés. Il n'y a aucune obligation de l'utiliser, et vous pouvez auto-héberger le logiciel sur votre propre infrastructure gratuitement."
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/PricingTiers.tsx:30
#: src/views/pricing/components/PricingTiers.tsx:30
msgid "Free"
msgstr "Gratuit"
#: src/views/members/components/InviteMemberForm.tsx:326
#: src/views/members/components/InviteMemberForm.tsx:333
#: src/views/members/index.tsx:208
msgid "Free Plan"
msgstr "Forfait gratuit"
@@ -1026,7 +1050,7 @@ msgstr "Commencer"
msgid "Get started by creating a new {0}"
msgstr "Commencez par créer un nouveau {0}"
#: src/views/board/index.tsx:467
#: src/views/board/index.tsx:470
msgid "Get started by creating a new list"
msgstr "Commencez par créer une nouvelle liste"
@@ -1181,11 +1205,11 @@ msgstr "Invitation invalide"
msgid "Invite"
msgstr "Inviter"
#: src/views/members/components/InviteMemberForm.tsx:202
#: src/views/members/components/InviteMemberForm.tsx:209
msgid "Invite link copied"
msgstr "Lien d'invitation copié"
#: src/views/members/components/InviteMemberForm.tsx:203
#: src/views/members/components/InviteMemberForm.tsx:210
msgid "Invite link copied to clipboard"
msgstr "Lien d'invitation copié dans le presse-papiers"
@@ -1198,11 +1222,11 @@ msgid "Invite links require a Team or Pro subscription. Please upgrade your work
msgstr "Les liens d'invitation nécessitent un abonnement Team ou Pro. Veuillez mettre à niveau votre espace de travail."
#: src/views/card/components/MemberSelector.tsx:111
#: src/views/members/components/InviteMemberForm.tsx:365
#: src/views/members/components/InviteMemberForm.tsx:367
msgid "Invite member"
msgstr "Inviter un membre"
#: src/views/members/components/InviteMemberForm.tsx:329
#: src/views/members/components/InviteMemberForm.tsx:336
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
msgstr "L'invitation de membres nécessite un forfait d'équipe. Vous serez redirigé pour mettre à niveau votre espace de travail."
@@ -1240,9 +1264,9 @@ 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:101
#: src/views/board/components/NewCardForm.tsx:408
#: src/views/card/index.tsx:117
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
msgid "Labels"
msgstr "Étiquettes"
@@ -1259,7 +1283,7 @@ msgid "Language"
msgstr "Langue"
#: src/components/NewWorkspaceForm.tsx:335
#: src/views/settings/components/UpgradeToProConfirmation.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:69
msgid "Launch offer"
msgstr "Offre de lancement"
@@ -1292,18 +1316,22 @@ msgstr "Licence"
msgid "Light"
msgstr "Clair"
#: src/views/public/board/index.tsx:65
#: src/views/public/board/index.tsx:66
msgid "Link copied"
msgstr "Lien copié"
#: src/views/card/index.tsx:109
#: src/views/card/index.tsx:111
msgid "List"
msgstr "Liste"
#: src/views/board/components/NewListForm.tsx:128
#: src/views/board/components/NewListForm.tsx:129
msgid "List name"
msgstr "Nom de la liste"
#: src/views/board/components/Filters.tsx:123
msgid "Lists"
msgstr "Listes"
#: src/views/auth/login/index.tsx:33
msgid "Login | kan.bn"
msgstr "Connexion | kan.bn"
@@ -1324,7 +1352,7 @@ msgstr "Adoré par les équipes du monde entier"
msgid "Low Priority"
msgstr "Priorité basse"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "lien magique"
@@ -1345,10 +1373,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:80
#: src/views/board/components/Filters.tsx:93
#: src/views/board/components/NewCardForm.tsx:366
#: src/views/card/index.tsx:126
#: 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/views/members/index.tsx:178
msgid "Members"
msgstr "Membres"
@@ -1401,7 +1429,7 @@ msgstr "Nouveau {0}"
msgid "New API key"
msgstr "Nouvelle clé API"
#: src/views/board/components/NewCardForm.tsx:292
#: src/views/board/components/NewCardForm.tsx:293
msgid "New card"
msgstr "Nouvelle carte"
@@ -1417,8 +1445,8 @@ msgstr "Nouvel import"
msgid "New label"
msgstr "Nouvelle étiquette"
#: src/views/board/components/NewListForm.tsx:112
#: src/views/board/index.tsx:439
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
msgid "New list"
msgstr "Nouvelle liste"
@@ -1455,6 +1483,10 @@ msgstr "Prochaines étapes"
msgid "No {0}"
msgstr "Aucun {0}"
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Aucune méthode d'authentification n'est actuellement disponible"
#: src/views/boards/components/ImportBoardsForm.tsx:242
msgid "No boards found"
msgstr "Aucun tableau trouvé"
@@ -1463,7 +1495,11 @@ msgstr "Aucun tableau trouvé"
msgid "No credit card required"
msgstr "Aucune carte de crédit requise"
#: src/views/board/index.tsx:464
#: 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
msgid "No lists"
msgstr "Aucune liste"
@@ -1483,7 +1519,7 @@ msgstr "Intégration"
msgid "Once you delete your account, there is no going back. This action cannot be undone."
msgstr "Une fois que vous supprimez votre compte, il n'y a pas de retour possible. Cette action ne peut pas être annulée."
#: src/views/settings/WorkspaceSettings.tsx:99
#: src/views/settings/WorkspaceSettings.tsx:100
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."
@@ -1495,7 +1531,7 @@ msgstr "Open source"
msgid "Open Source Friends"
msgstr "Amis Open Source"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "ou"
@@ -1577,15 +1613,15 @@ msgstr "Plateforme"
msgid "Please confirm your new password"
msgstr "Veuillez confirmer votre nouveau mot de passe"
#: src/components/AuthForm.tsx:391
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Veuillez saisir une adresse e-mail valide"
#: src/components/AuthForm.tsx:379
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Veuillez saisir un nom valide"
#: src/components/AuthForm.tsx:404
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Veuillez saisir un mot de passe valide"
@@ -1596,15 +1632,16 @@ 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:176
#: src/views/board/components/NewListForm.tsx:78
#: src/views/board/components/NewCardForm.tsx:177
#: 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:72
#: src/views/board/components/VisibilityButton.tsx:53
#: src/views/board/index.tsx:149
#: src/views/board/index.tsx:205
#: 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/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
#: src/views/card/components/ChecklistItemRow.tsx:88
#: src/views/card/components/ChecklistNameInput.tsx:46
@@ -1618,10 +1655,10 @@ msgstr "Veuillez sélectionner un fichier à télécharger."
#: 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:175
#: src/views/card/index.tsx:177
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:234
#: src/views/members/components/InviteMemberForm.tsx:241
#: src/views/settings/components/Avatar.tsx:78
#: src/views/settings/components/Avatar.tsx:219
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
@@ -1654,7 +1691,7 @@ msgstr "Tarifs"
#: src/views/pricing/components/Pricing.tsx:56
#: src/views/pricing/components/PricingTiers.tsx:53
#: src/views/settings/components/UpgradeToProConfirmation.tsx:91
#: src/views/settings/components/UpgradeToProConfirmation.tsx:93
msgid "Priority email support"
msgstr "Support par e-mail prioritaire"
@@ -1662,7 +1699,7 @@ msgstr "Support par e-mail prioritaire"
msgid "Privacy policy"
msgstr "Politique de confidentialité"
#: src/views/board/components/VisibilityButton.tsx:73
#: src/views/board/components/VisibilityButton.tsx:74
msgid "Private"
msgstr "Privé"
@@ -1670,7 +1707,7 @@ msgstr "Privé"
msgid "Pro Plan"
msgstr "Plan Pro"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
msgid "Pro Plan ∞"
msgstr "Plan Pro ∞"
@@ -1682,7 +1719,7 @@ msgstr "Image de profil mise à jour"
msgid "Profile picture"
msgstr "Photo de profil"
#: src/views/board/components/VisibilityButton.tsx:68
#: src/views/board/components/VisibilityButton.tsx:69
msgid "Public"
msgstr "Public"
@@ -1835,7 +1872,7 @@ msgid "Senior"
msgstr "Senior"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:85
#: src/components/SideNavigation.tsx:106
msgid "Settings"
msgstr "Paramètres"
@@ -1884,7 +1921,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:420
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "S'inscrire avec "
@@ -1909,6 +1946,11 @@ msgstr "Développement logiciel"
msgid "Star on Github"
msgstr "Étoile sur Github"
#: src/views/members/components/InviteMemberForm.tsx:359
#: src/views/settings/components/UpgradeToProConfirmation.tsx:111
msgid "Start 14 day free trial"
msgstr "Commencer l'essai gratuit de 14 jours"
#: src/views/members/components/InviteMemberForm.tsx:122
msgid "Subscription Required"
msgstr "Abonnement requis"
@@ -1918,7 +1960,7 @@ msgstr "Abonnement requis"
msgid "Success"
msgstr "Succès"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:55
#: src/views/settings/components/UpgradeToProConfirmation.tsx:57
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 :"
@@ -1935,7 +1977,7 @@ msgstr "Soutenir le développement du projet"
msgid "System"
msgstr "Système"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
#: src/views/members/index.tsx:207
msgid "Team Plan"
msgstr "Forfait d'équipe"
@@ -1946,8 +1988,8 @@ msgstr "Forfait d'équipe"
msgid "Teams"
msgstr "Équipes"
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:396
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
msgid "Template"
msgstr "Modèle"
@@ -1967,7 +2009,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:75
#: src/components/SideNavigation.tsx:96
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Modèles"
@@ -1997,7 +2039,7 @@ msgid "The open source <0/> alternative to Trello"
msgstr "L'alternative open source <0/> à Trello"
#. placeholder {0}: isPublic ? "public" : "private"
#: src/views/board/components/VisibilityButton.tsx:46
#: src/views/board/components/VisibilityButton.tsx:47
msgid "The visibility of your board has been set to {0}."
msgstr "La visibilité de votre tableau a été définie sur {0}."
@@ -2021,11 +2063,11 @@ 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:154
#: src/views/public/board/index.tsx:156
msgid "This board is private or does not exist"
msgstr "Ce tableau est privé ou n'existe pas"
#: src/views/board/components/UpdateBoardSlugForm.tsx:135
#: src/views/board/components/UpdateBoardSlugForm.tsx:136
msgid "This board URL has already been taken"
msgstr "Cette URL de tableau est déjà utilisée"
@@ -2095,7 +2137,7 @@ 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:173
#: src/views/board/components/NewCardForm.tsx:174
msgid "Unable to create card"
msgstr "Impossible de créer la carte"
@@ -2103,7 +2145,7 @@ msgstr "Impossible de créer la carte"
msgid "Unable to create checklist"
msgstr "Impossible de créer la liste de contrôle"
#: src/views/board/components/NewListForm.tsx:77
#: src/views/board/components/NewListForm.tsx:78
msgid "Unable to create list"
msgstr "Impossible de créer une liste"
@@ -2116,6 +2158,10 @@ msgstr "Impossible de créer le modèle"
msgid "Unable to create workspace"
msgstr "Impossible de créer l'espace de travail"
#: src/views/card/components/AttachmentThumbnails.tsx:72
msgid "Unable to delete attachment"
msgstr "Impossible de supprimer la pièce jointe"
#: src/views/card/components/DeleteCardConfirmation.tsx:51
msgid "Unable to delete card"
msgstr "Impossible de supprimer la carte"
@@ -2140,16 +2186,16 @@ msgstr "Impossible de supprimer le membre"
msgid "Unable to send feedback"
msgstr "Impossible d'envoyer les commentaires"
#: src/views/board/components/UpdateBoardSlugForm.tsx:71
#: src/views/board/components/UpdateBoardSlugForm.tsx:72
msgid "Unable to update board URL"
msgstr "Impossible de mettre à jour l'URL du tableau"
#: src/views/board/components/VisibilityButton.tsx:52
#: src/views/board/components/VisibilityButton.tsx:53
msgid "Unable to update board visibility"
msgstr "Impossible de mettre à jour la visibilité du tableau"
#: src/views/board/index.tsx:204
#: src/views/card/index.tsx:174
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
msgid "Unable to update card"
msgstr "Impossible de mettre à jour la carte"
@@ -2169,7 +2215,7 @@ msgstr "Impossible de mettre à jour le commentaire"
msgid "Unable to update labels"
msgstr "Impossible de mettre à jour les étiquettes"
#: src/views/board/index.tsx:148
#: src/views/board/index.tsx:150
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Impossible de mettre à jour la liste"
@@ -2217,7 +2263,7 @@ msgid "Unlimited lists"
msgstr "Listes illimitées"
#: src/components/NewWorkspaceForm.tsx:332
#: src/views/settings/components/UpgradeToProConfirmation.tsx:64
#: src/views/settings/components/UpgradeToProConfirmation.tsx:66
msgid "Unlimited members"
msgstr "Membres illimités"
@@ -2225,7 +2271,12 @@ msgstr "Membres illimités"
msgid "Unlimited workspaces"
msgstr "Espaces de travail illimités"
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
#: 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
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
@@ -2254,12 +2305,9 @@ 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/views/settings/components/UpgradeToProConfirmation.tsx:109
msgid "Upgrade"
msgstr "Mettre à niveau"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
#: src/views/settings/WorkspaceSettings.tsx:89
#: src/components/SideNavigation.tsx:202
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
msgstr "Passer à Pro"
@@ -2267,9 +2315,10 @@ msgstr "Passer à Pro"
msgid "Upgrade to Pro ($29/month)"
msgstr "Passer à Pro (29 $/mois)"
#: src/views/members/components/InviteMemberForm.tsx:357
msgid "Upgrade to Team Plan"
msgstr "Passer au forfait d'équipe"
#: src/views/card/components/AttachmentUpload.tsx:32
#: src/views/card/components/AttachmentUpload.tsx:78
msgid "Upload failed"
msgstr "Échec du téléversement"
#: src/views/boards/components/TemplateBoards.tsx:38
#: src/views/boards/components/TemplateBoards.tsx:55
@@ -2320,7 +2369,7 @@ msgstr "Consultez et gérez votre facturation et votre abonnement."
msgid "View docs"
msgstr "Voir la documentation"
#: src/views/public/board/index.tsx:128
#: src/views/public/board/index.tsx:129
msgid "View only"
msgstr "Affichage uniquement"
@@ -2328,11 +2377,11 @@ msgstr "Affichage uniquement"
msgid "View our roadmap."
msgstr "Consultez notre feuille de route."
#: src/views/public/board/index.tsx:157
#: src/views/public/board/index.tsx:159
msgid "View workspace"
msgstr "Voir l'espace de travail"
#: src/views/board/components/VisibilityButton.tsx:91
#: src/views/board/components/VisibilityButton.tsx:92
msgid "Visibility"
msgstr "Visibilité"
@@ -2340,6 +2389,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. "
@@ -2369,10 +2422,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:361
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:100
#: src/views/public/board/index.tsx:101
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Espace de travail"
@@ -2478,7 +2531,11 @@ msgstr "Vous vous êtes connecté avec succès."
msgid "You have been signed up successfully."
msgstr "Vous vous êtes inscrit avec succès."
#: src/views/members/components/InviteMemberForm.tsx:319
#: 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 !"
@@ -2502,6 +2559,10 @@ msgstr "Vos tableaux ont été importés."
msgid "Your display name has been updated."
msgstr "Votre nom d'affichage a été mis à jour."
#: src/views/card/components/AttachmentUpload.tsx:26
msgid "Your file has been uploaded successfully."
msgstr "Votre fichier a été téléversé avec succès."
#: src/views/settings/components/ChangePasswordConfirmation.tsx:70
msgid "Your password has been changed."
msgstr "Votre mot de passe a été modifié."
@@ -2518,6 +2579,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

@@ -31,17 +31,17 @@ msgstr "{0}"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:442
#: src/views/board/components/NewCardForm.tsx:443
msgid "{0} labels"
msgstr "{0} etichette"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:387
#: src/views/board/index.tsx:389
msgid "{0} not found"
msgstr "{0} non trovato"
@@ -63,6 +63,10 @@ msgstr "$8/mese"
msgid "1 user"
msgstr "1 utente"
#: src/views/pricing/components/PricingTiers.tsx:136
msgid "14 day free trial"
msgstr "Prova gratuita di 14 giorni"
#: src/views/home/index.tsx:71
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
msgstr "Un'app kanban potente e flessibile che ti aiuta a organizzare il lavoro, monitorare i progressi e ottenere risultati, tutto in un unico posto."
@@ -75,8 +79,8 @@ msgstr "Account"
msgid "Account deleted"
msgstr "Account eliminato"
#: src/views/card/index.tsx:291
#: src/views/public/board/CardModal.tsx:143
#: src/views/card/index.tsx:343
#: src/views/public/board/CardModal.tsx:157
msgid "Activity"
msgstr "Attività"
@@ -105,7 +109,7 @@ msgstr "Aggiungi un elemento..."
msgid "Add checklist"
msgstr "Aggiungi checklist"
#: src/components/Editor.tsx:442
#: src/components/Editor.tsx:451
msgid "Add description... (type '/' to open commands or '@' to mention)"
msgstr "Aggiungi descrizione... (digita '/' per aprire i comandi o '@' per menzionare)"
@@ -119,7 +123,7 @@ msgid "Add label"
msgstr "Aggiungi etichetta"
#: src/views/card/components/MemberSelector.tsx:130
#: src/views/members/components/InviteMemberForm.tsx:250
#: src/views/members/components/InviteMemberForm.tsx:257
msgid "Add member"
msgstr "Aggiungi membro"
@@ -154,7 +158,7 @@ msgstr "ha aggiunto l'elemento <0>{0}</0> alla checklist"
msgid "added label <0>{0}</0>"
msgstr "ha aggiunto l'etichetta <0>{0}</0>"
#: src/views/members/components/InviteMemberForm.tsx:320
#: src/views/members/components/InviteMemberForm.tsx:327
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
msgstr "L'aggiunta di un nuovo membro costerà un supplemento di {price} ({billingType}) per posto."
@@ -183,7 +187,7 @@ msgstr "Si è verificato un errore durante la disconnessione del tuo account Tre
msgid "An unexpected error occurred. Please try again later."
msgstr "Si è verificato un errore imprevisto. Riprova più tardi."
#: src/views/members/components/InviteMemberForm.tsx:304
#: src/views/members/components/InviteMemberForm.tsx:311
msgid "Anyone with this link can join your workspace"
msgstr "Chiunque abbia questo link può unirsi al tuo spazio di lavoro"
@@ -266,6 +270,10 @@ msgstr "ha assegnato <0>{0}</0> alla scheda"
msgid "Assignees"
msgstr "Assegnatari"
#: src/views/card/components/AttachmentUpload.tsx:25
msgid "Attachment uploaded"
msgstr "Allegato caricato"
#: src/views/boards/components/TemplateBoards.tsx:47
msgid "Awaiting Customer"
msgstr "In attesa del cliente"
@@ -300,14 +308,14 @@ msgstr "Portale di fatturazione"
msgid "Blog Post"
msgstr "Post del blog"
#: src/views/board/index.tsx:361
#: src/views/card/index.tsx:216
#: src/views/public/board/index.tsx:100
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:229
#: src/views/public/board/index.tsx:101
msgid "Board"
msgstr "Bacheca"
#: src/components/NewWorkspaceForm.tsx:349
#: src/views/settings/components/UpgradeToProConfirmation.tsx:81
#: src/views/settings/components/UpgradeToProConfirmation.tsx:83
msgid "Board analytics"
msgstr "Analisi della bacheca"
@@ -319,7 +327,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:151
#: src/views/public/board/index.tsx:153
msgid "Board not found"
msgstr "Bacheca non trovata"
@@ -327,19 +335,19 @@ msgstr "Bacheca non trovata"
msgid "Board sharing & invites"
msgstr "Condivisione bacheca & inviti"
#: src/views/board/components/UpdateBoardSlugForm.tsx:45
#: src/views/board/components/UpdateBoardSlugForm.tsx:46
msgid "Board URL can only contain letters, numbers, and hyphens"
msgstr "L'URL della bacheca può contenere solo lettere, numeri e trattini"
#: src/views/board/components/UpdateBoardSlugForm.tsx:43
#: src/views/board/components/UpdateBoardSlugForm.tsx:44
msgid "Board URL cannot exceed 60 characters"
msgstr "L'URL della bacheca non può superare i 60 caratteri"
#: src/views/public/board/index.tsx:67
#: src/views/public/board/index.tsx:68
msgid "Board URL copied to clipboard"
msgstr "URL della bacheca copiato negli appunti"
#: src/views/board/components/UpdateBoardSlugForm.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:42
msgid "Board URL must be at least 3 characters long"
msgstr "L'URL della bacheca deve contenere almeno 3 caratteri"
@@ -348,11 +356,11 @@ msgstr "L'URL della bacheca deve contenere almeno 3 caratteri"
msgid "Board visibility"
msgstr "Visibilità bacheca"
#: src/views/board/components/VisibilityButton.tsx:45
#: src/views/board/components/VisibilityButton.tsx:46
msgid "Board visibility updated"
msgstr "Visibilità della bacheca aggiornata"
#: src/components/SideNavigation.tsx:70
#: src/components/SideNavigation.tsx:91
msgid "Boards"
msgstr "Bacheche"
@@ -379,19 +387,20 @@ msgstr "Segnalazione bug"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
#: src/views/settings/components/UpgradeToProConfirmation.tsx:104
#: src/views/settings/components/UpgradeToProConfirmation.tsx:106
msgid "Cancel"
msgstr "Annulla"
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "Card"
msgstr "Carta"
#: src/views/card/index.tsx:262
#: src/views/card/index.tsx:263
#: src/views/card/index.tsx:299
msgid "Card not found"
msgstr "Carta non trovata"
#: src/views/board/components/NewCardForm.tsx:309
#: src/views/board/components/NewCardForm.tsx:310
msgid "Card title"
msgstr "Titolo della carta"
@@ -424,7 +433,7 @@ msgstr "Nome della checklist"
msgid "Checklists"
msgstr "Checklist"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/Filters.tsx:180
msgid "Clear filters"
msgstr "Cancella filtri"
@@ -447,7 +456,7 @@ msgstr "Collabora senza problemi con il tuo team."
#: src/components/NewWorkspaceForm.tsx:352
#: src/views/home/components/Features.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:84
#: src/views/settings/components/UpgradeToProConfirmation.tsx:86
msgid "Coming soon"
msgstr "Prossimamente"
@@ -485,6 +494,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"
@@ -515,12 +528,12 @@ msgstr "Contattaci"
msgid "Content Creation"
msgstr "Creazione contenuti"
#: src/components/AuthForm.tsx:420
#: 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}"
@@ -538,8 +551,8 @@ msgid "Create {0}"
msgstr "Crea {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:468
#: src/views/board/components/NewListForm.tsx:140
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Crea un altro"
@@ -547,7 +560,7 @@ msgstr "Crea un altro"
msgid "Create API key"
msgstr "Crea chiave API"
#: src/views/board/components/NewCardForm.tsx:478
#: src/views/board/components/NewCardForm.tsx:479
msgid "Create card"
msgstr "Crea carta"
@@ -555,7 +568,7 @@ msgstr "Crea carta"
msgid "Create checklist"
msgstr "Crea checklist"
#: src/views/members/components/InviteMemberForm.tsx:342
#: src/views/members/components/InviteMemberForm.tsx:349
msgid "Create invite link"
msgstr "Crea link di invito"
@@ -563,7 +576,7 @@ msgstr "Crea link di invito"
msgid "Create label"
msgstr "Crea etichetta"
#: src/views/board/components/NewListForm.tsx:152
#: src/views/board/components/NewListForm.tsx:153
msgid "Create list"
msgstr "Crea lista"
@@ -576,12 +589,12 @@ msgstr "Crea nuovo {0}"
msgid "Create new key"
msgstr "Crea nuova chiave"
#: src/views/board/components/NewCardForm.tsx:404
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Crea nuova etichetta"
#: src/views/board/index.tsx:475
#: src/views/board/index.tsx:478
msgid "Create new list"
msgstr "Crea nuova lista"
@@ -590,7 +603,7 @@ msgid "Create template"
msgstr "Crea template"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:148
#: src/components/WorkspaceMenu.tsx:149
msgid "Create workspace"
msgstr "Crea spazio di lavoro"
@@ -632,7 +645,7 @@ msgid "Custom workspace link"
msgstr "Link personalizzato dell'area di lavoro"
#: src/components/NewWorkspaceForm.tsx:342
#: src/views/settings/components/UpgradeToProConfirmation.tsx:74
#: src/views/settings/components/UpgradeToProConfirmation.tsx:76
msgid "Custom workspace URL"
msgstr "URL personalizzato dell'area di lavoro"
@@ -644,7 +657,7 @@ msgstr "Assistenza clienti"
msgid "Dark"
msgstr "Scuro"
#: src/views/members/components/InviteMemberForm.tsx:341
#: src/views/members/components/InviteMemberForm.tsx:348
msgid "Deactivate invite link"
msgstr "Disattiva link di invito"
@@ -684,8 +697,8 @@ msgid "Delete template"
msgstr "Elimina template"
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
#: src/views/settings/WorkspaceSettings.tsx:96
#: src/views/settings/WorkspaceSettings.tsx:107
#: src/views/settings/WorkspaceSettings.tsx:97
#: src/views/settings/WorkspaceSettings.tsx:108
msgid "Delete workspace"
msgstr "Elimina spazio di lavoro"
@@ -739,6 +752,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"
@@ -765,8 +782,12 @@ msgstr "Non hai un account? <0><1>Registrati</1></0>"
msgid "Done"
msgstr "Fatto"
#: src/views/card/components/AttachmentThumbnails.tsx:141
msgid "Download failed"
msgstr "Download fallito"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:115
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
msgstr "Modifica URL della bacheca"
@@ -778,7 +799,7 @@ msgstr "Modifica commento"
msgid "Edit label"
msgstr "Modifica etichetta"
#: src/views/board/components/UpdateBoardSlugForm.tsx:161
#: src/views/board/components/UpdateBoardSlugForm.tsx:162
msgid "Edit workspace URL"
msgstr "Modifica URL dell'area di lavoro"
@@ -786,11 +807,11 @@ msgstr "Modifica URL dell'area di lavoro"
msgid "Editing"
msgstr "Modifica"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "email"
#: src/views/members/components/InviteMemberForm.tsx:265
#: src/views/members/components/InviteMemberForm.tsx:272
msgid "Email"
msgstr "Email"
@@ -806,11 +827,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:387
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Inserisci il tuo indirizzo email"
#: src/components/AuthForm.tsx:375
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Inserisci il tuo nome"
@@ -818,13 +839,13 @@ msgstr "Inserisci il tuo nome"
msgid "Enter your new password"
msgstr "Inserisci la tua nuova password"
#: src/components/AuthForm.tsx:400
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Inserisci la tua password"
#: src/views/boards/components/NewBoardForm.tsx:77
#: src/views/boards/components/NewBoardForm.tsx:92
#: src/views/members/components/InviteMemberForm.tsx:208
#: src/views/members/components/InviteMemberForm.tsx:215
msgid "Error"
msgstr "Errore"
@@ -881,7 +902,7 @@ msgstr "Errore durante l'aggiornamento del nome dell'area di lavoro"
msgid "Error updating workspace URL"
msgstr "Errore durante l'aggiornamento dell'URL dell'area di lavoro"
#: src/views/members/components/InviteMemberForm.tsx:233
#: src/views/members/components/InviteMemberForm.tsx:240
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
msgid "Error upgrading subscription"
msgstr "Errore nell'aggiornamento dell'abbonamento"
@@ -913,7 +934,7 @@ msgstr "Esecuzione"
msgid "Failed to accept invitation. Please try again later, or contact customer support."
msgstr "Impossibile accettare l'invito. Riprova più tardi o contatta l'assistenza clienti."
#: src/views/members/components/InviteMemberForm.tsx:209
#: src/views/members/components/InviteMemberForm.tsx:216
msgid "Failed to copy invite link"
msgstr "Impossibile copiare il link di invito"
@@ -927,6 +948,11 @@ msgstr "Impossibile creare la bacheca"
msgid "Failed to login with {0}. Please try again."
msgstr "Accesso con {0} fallito. Riprova."
#: src/views/card/components/AttachmentUpload.tsx:33
#: src/views/card/components/AttachmentUpload.tsx:79
msgid "Failed to upload attachment. Please try again."
msgstr "Impossibile caricare l'allegato. Riprova."
#: src/views/home/components/Header.tsx:41
msgid "FAQ"
msgstr "FAQ"
@@ -970,7 +996,7 @@ msgstr "Feedback"
msgid "Feedback sent"
msgstr "Feedback inviato"
#: src/views/board/components/Filters.tsx:149
#: src/views/board/components/Filters.tsx:174
msgid "Filter"
msgstr "Filtra"
@@ -983,13 +1009,11 @@ msgid "For long-term sustainability, we recognise all good open source projects
msgstr "Per la sostenibilità a lungo termine, riconosciamo che tutti i buoni progetti open source necessitano di una fonte di entrate. La nostra proviene dall'offerta cloud a pagamento per gli spazi di lavoro con più utenti e per gli slug personalizzati degli spazi di lavoro. Non c'è alcun obbligo di utilizzarla, e puoi ospitare autonomamente il software sulla tua infrastruttura gratuitamente."
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/PricingTiers.tsx:30
#: src/views/pricing/components/PricingTiers.tsx:30
msgid "Free"
msgstr "Gratuito"
#: src/views/members/components/InviteMemberForm.tsx:326
#: src/views/members/components/InviteMemberForm.tsx:333
#: src/views/members/index.tsx:208
msgid "Free Plan"
msgstr "Piano gratuito"
@@ -1026,7 +1050,7 @@ msgstr "Inizia ora"
msgid "Get started by creating a new {0}"
msgstr "Inizia creando un nuovo {0}"
#: src/views/board/index.tsx:467
#: src/views/board/index.tsx:470
msgid "Get started by creating a new list"
msgstr "Inizia creando una nuova lista"
@@ -1181,11 +1205,11 @@ msgstr "Invito non valido"
msgid "Invite"
msgstr "Invita"
#: src/views/members/components/InviteMemberForm.tsx:202
#: src/views/members/components/InviteMemberForm.tsx:209
msgid "Invite link copied"
msgstr "Link di invito copiato"
#: src/views/members/components/InviteMemberForm.tsx:203
#: src/views/members/components/InviteMemberForm.tsx:210
msgid "Invite link copied to clipboard"
msgstr "Link di invito copiato negli appunti"
@@ -1198,11 +1222,11 @@ msgid "Invite links require a Team or Pro subscription. Please upgrade your work
msgstr "I link di invito richiedono un abbonamento Team o Pro. Per favore aggiorna il tuo spazio di lavoro."
#: src/views/card/components/MemberSelector.tsx:111
#: src/views/members/components/InviteMemberForm.tsx:365
#: src/views/members/components/InviteMemberForm.tsx:367
msgid "Invite member"
msgstr "Invita membro"
#: src/views/members/components/InviteMemberForm.tsx:329
#: src/views/members/components/InviteMemberForm.tsx:336
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
msgstr "L'invito di membri richiede un Piano Team. Sarai reindirizzato per aggiornare il tuo spazio di lavoro."
@@ -1240,9 +1264,9 @@ 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:101
#: src/views/board/components/NewCardForm.tsx:408
#: src/views/card/index.tsx:117
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
msgid "Labels"
msgstr "Etichette"
@@ -1259,7 +1283,7 @@ msgid "Language"
msgstr "Lingua"
#: src/components/NewWorkspaceForm.tsx:335
#: src/views/settings/components/UpgradeToProConfirmation.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:69
msgid "Launch offer"
msgstr "Offerta di lancio"
@@ -1292,18 +1316,22 @@ msgstr "Licenza"
msgid "Light"
msgstr "Chiaro"
#: src/views/public/board/index.tsx:65
#: src/views/public/board/index.tsx:66
msgid "Link copied"
msgstr "Link copiato"
#: src/views/card/index.tsx:109
#: src/views/card/index.tsx:111
msgid "List"
msgstr "Lista"
#: src/views/board/components/NewListForm.tsx:128
#: src/views/board/components/NewListForm.tsx:129
msgid "List name"
msgstr "Nome lista"
#: src/views/board/components/Filters.tsx:123
msgid "Lists"
msgstr "Liste"
#: src/views/auth/login/index.tsx:33
msgid "Login | kan.bn"
msgstr "Login | kan.bn"
@@ -1324,7 +1352,7 @@ msgstr "Amato dai team di tutto il mondo"
msgid "Low Priority"
msgstr "Bassa priorità"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "link magico"
@@ -1345,10 +1373,10 @@ msgstr "ha segnato l'elemento <0>{0}</0> della checklist come incompleto"
msgid "Medium Priority"
msgstr "Media priorità"
#: src/components/SideNavigation.tsx:80
#: src/views/board/components/Filters.tsx:93
#: src/views/board/components/NewCardForm.tsx:366
#: src/views/card/index.tsx:126
#: 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/views/members/index.tsx:178
msgid "Members"
msgstr "Membri"
@@ -1401,7 +1429,7 @@ msgstr "Nuovo {0}"
msgid "New API key"
msgstr "Nuova chiave API"
#: src/views/board/components/NewCardForm.tsx:292
#: src/views/board/components/NewCardForm.tsx:293
msgid "New card"
msgstr "Nuova carta"
@@ -1417,8 +1445,8 @@ msgstr "Nuova importazione"
msgid "New label"
msgstr "Nuova etichetta"
#: src/views/board/components/NewListForm.tsx:112
#: src/views/board/index.tsx:439
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
msgid "New list"
msgstr "Nuova lista"
@@ -1455,6 +1483,10 @@ msgstr "Prossimi passi"
msgid "No {0}"
msgstr "Nessun {0}"
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Nessun metodo di autenticazione è attualmente disponibile"
#: src/views/boards/components/ImportBoardsForm.tsx:242
msgid "No boards found"
msgstr "Nessuna bacheca trovata"
@@ -1463,7 +1495,11 @@ msgstr "Nessuna bacheca trovata"
msgid "No credit card required"
msgstr "Nessuna carta di credito richiesta"
#: src/views/board/index.tsx:464
#: 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
msgid "No lists"
msgstr "Nessuna lista"
@@ -1483,7 +1519,7 @@ msgstr "Inserimento"
msgid "Once you delete your account, there is no going back. This action cannot be undone."
msgstr "Una volta eliminato il tuo account, non si può tornare indietro. Questa azione non può essere annullata."
#: src/views/settings/WorkspaceSettings.tsx:99
#: src/views/settings/WorkspaceSettings.tsx:100
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."
@@ -1495,7 +1531,7 @@ msgstr "Open source"
msgid "Open Source Friends"
msgstr "Amici Open Source"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "o"
@@ -1577,15 +1613,15 @@ msgstr "Piattaforma"
msgid "Please confirm your new password"
msgstr "Conferma la tua nuova password"
#: src/components/AuthForm.tsx:391
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Inserisci un indirizzo email valido"
#: src/components/AuthForm.tsx:379
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Inserisci un nome valido"
#: src/components/AuthForm.tsx:404
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Inserisci una password valida"
@@ -1596,15 +1632,16 @@ 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:176
#: src/views/board/components/NewListForm.tsx:78
#: src/views/board/components/NewCardForm.tsx:177
#: 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:72
#: src/views/board/components/VisibilityButton.tsx:53
#: src/views/board/index.tsx:149
#: src/views/board/index.tsx:205
#: 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/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
#: src/views/card/components/ChecklistItemRow.tsx:88
#: src/views/card/components/ChecklistNameInput.tsx:46
@@ -1618,10 +1655,10 @@ msgstr "Seleziona un file da caricare."
#: 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:175
#: src/views/card/index.tsx:177
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:234
#: src/views/members/components/InviteMemberForm.tsx:241
#: src/views/settings/components/Avatar.tsx:78
#: src/views/settings/components/Avatar.tsx:219
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
@@ -1654,7 +1691,7 @@ msgstr "Prezzi"
#: src/views/pricing/components/Pricing.tsx:56
#: src/views/pricing/components/PricingTiers.tsx:53
#: src/views/settings/components/UpgradeToProConfirmation.tsx:91
#: src/views/settings/components/UpgradeToProConfirmation.tsx:93
msgid "Priority email support"
msgstr "Supporto email prioritario"
@@ -1662,7 +1699,7 @@ msgstr "Supporto email prioritario"
msgid "Privacy policy"
msgstr "Informativa sulla privacy"
#: src/views/board/components/VisibilityButton.tsx:73
#: src/views/board/components/VisibilityButton.tsx:74
msgid "Private"
msgstr "Privato"
@@ -1670,7 +1707,7 @@ msgstr "Privato"
msgid "Pro Plan"
msgstr "Piano Pro"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
msgid "Pro Plan ∞"
msgstr "Piano Pro ∞"
@@ -1682,7 +1719,7 @@ msgstr "Immagine del profilo aggiornata"
msgid "Profile picture"
msgstr "Immagine del profilo"
#: src/views/board/components/VisibilityButton.tsx:68
#: src/views/board/components/VisibilityButton.tsx:69
msgid "Public"
msgstr "Pubblico"
@@ -1835,7 +1872,7 @@ msgid "Senior"
msgstr "Senior"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:85
#: src/components/SideNavigation.tsx:106
msgid "Settings"
msgstr "Impostazioni"
@@ -1884,7 +1921,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:420
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Registrati con "
@@ -1909,6 +1946,11 @@ msgstr "Sviluppo software"
msgid "Star on Github"
msgstr "Metti una stella su Github"
#: src/views/members/components/InviteMemberForm.tsx:359
#: src/views/settings/components/UpgradeToProConfirmation.tsx:111
msgid "Start 14 day free trial"
msgstr "Inizia la prova gratuita di 14 giorni"
#: src/views/members/components/InviteMemberForm.tsx:122
msgid "Subscription Required"
msgstr "Abbonamento richiesto"
@@ -1918,7 +1960,7 @@ msgstr "Abbonamento richiesto"
msgid "Success"
msgstr "Operazione riuscita"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:55
#: src/views/settings/components/UpgradeToProConfirmation.tsx:57
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:"
@@ -1935,7 +1977,7 @@ msgstr "Sostieni lo sviluppo del progetto"
msgid "System"
msgstr "Sistema"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
#: src/views/members/index.tsx:207
msgid "Team Plan"
msgstr "Piano Team"
@@ -1946,8 +1988,8 @@ msgstr "Piano Team"
msgid "Teams"
msgstr "Team"
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:396
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
msgid "Template"
msgstr "Template"
@@ -1967,7 +2009,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:75
#: src/components/SideNavigation.tsx:96
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Template"
@@ -1997,7 +2039,7 @@ msgid "The open source <0/> alternative to Trello"
msgstr "L'alternativa open source <0/> a Trello"
#. placeholder {0}: isPublic ? "public" : "private"
#: src/views/board/components/VisibilityButton.tsx:46
#: src/views/board/components/VisibilityButton.tsx:47
msgid "The visibility of your board has been set to {0}."
msgstr "La visibilità della tua bacheca è stata impostata su {0}."
@@ -2021,11 +2063,11 @@ 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:154
#: src/views/public/board/index.tsx:156
msgid "This board is private or does not exist"
msgstr "Questa bacheca è privata o non esiste"
#: src/views/board/components/UpdateBoardSlugForm.tsx:135
#: src/views/board/components/UpdateBoardSlugForm.tsx:136
msgid "This board URL has already been taken"
msgstr "Questo URL della bacheca è già stato utilizzato"
@@ -2095,7 +2137,7 @@ msgstr "Impossibile aggiungere l'elemento della checklist"
msgid "Unable to add comment"
msgstr "Impossibile aggiungere il commento"
#: src/views/board/components/NewCardForm.tsx:173
#: src/views/board/components/NewCardForm.tsx:174
msgid "Unable to create card"
msgstr "Impossibile creare la scheda"
@@ -2103,7 +2145,7 @@ msgstr "Impossibile creare la scheda"
msgid "Unable to create checklist"
msgstr "Impossibile creare la checklist"
#: src/views/board/components/NewListForm.tsx:77
#: src/views/board/components/NewListForm.tsx:78
msgid "Unable to create list"
msgstr "Impossibile creare la lista"
@@ -2116,6 +2158,10 @@ msgstr "Impossibile creare il template"
msgid "Unable to create workspace"
msgstr "Impossibile creare lo spazio di lavoro"
#: src/views/card/components/AttachmentThumbnails.tsx:72
msgid "Unable to delete attachment"
msgstr "Impossibile eliminare l'allegato"
#: src/views/card/components/DeleteCardConfirmation.tsx:51
msgid "Unable to delete card"
msgstr "Impossibile eliminare la scheda"
@@ -2140,16 +2186,16 @@ msgstr "Impossibile rimuovere il membro"
msgid "Unable to send feedback"
msgstr "Impossibile inviare feedback"
#: src/views/board/components/UpdateBoardSlugForm.tsx:71
#: src/views/board/components/UpdateBoardSlugForm.tsx:72
msgid "Unable to update board URL"
msgstr "Impossibile aggiornare l'URL della bacheca"
#: src/views/board/components/VisibilityButton.tsx:52
#: src/views/board/components/VisibilityButton.tsx:53
msgid "Unable to update board visibility"
msgstr "Impossibile aggiornare la visibilità della bacheca"
#: src/views/board/index.tsx:204
#: src/views/card/index.tsx:174
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
msgid "Unable to update card"
msgstr "Impossibile aggiornare la scheda"
@@ -2169,7 +2215,7 @@ msgstr "Impossibile aggiornare il commento"
msgid "Unable to update labels"
msgstr "Impossibile aggiornare le etichette"
#: src/views/board/index.tsx:148
#: src/views/board/index.tsx:150
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Impossibile aggiornare la lista"
@@ -2217,7 +2263,7 @@ msgid "Unlimited lists"
msgstr "Liste illimitate"
#: src/components/NewWorkspaceForm.tsx:332
#: src/views/settings/components/UpgradeToProConfirmation.tsx:64
#: src/views/settings/components/UpgradeToProConfirmation.tsx:66
msgid "Unlimited members"
msgstr "Membri illimitati"
@@ -2225,7 +2271,12 @@ msgstr "Membri illimitati"
msgid "Unlimited workspaces"
msgstr "Aree di lavoro illimitate"
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
#: 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
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
@@ -2254,12 +2305,9 @@ msgstr "ha aggiornato il titolo"
msgid "updated the title to <0>{0}</0>"
msgstr "ha aggiornato il titolo in <0>{0}</0>"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:109
msgid "Upgrade"
msgstr "Aggiorna"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
#: src/views/settings/WorkspaceSettings.tsx:89
#: src/components/SideNavigation.tsx:202
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
msgstr "Passa a Pro"
@@ -2267,9 +2315,10 @@ msgstr "Passa a Pro"
msgid "Upgrade to Pro ($29/month)"
msgstr "Passa a Pro ($29/mese)"
#: src/views/members/components/InviteMemberForm.tsx:357
msgid "Upgrade to Team Plan"
msgstr "Passa al Piano Team"
#: src/views/card/components/AttachmentUpload.tsx:32
#: src/views/card/components/AttachmentUpload.tsx:78
msgid "Upload failed"
msgstr "Caricamento fallito"
#: src/views/boards/components/TemplateBoards.tsx:38
#: src/views/boards/components/TemplateBoards.tsx:55
@@ -2320,7 +2369,7 @@ msgstr "Visualizza e gestisci la tua fatturazione e abbonamento."
msgid "View docs"
msgstr "Visualizza documenti"
#: src/views/public/board/index.tsx:128
#: src/views/public/board/index.tsx:129
msgid "View only"
msgstr "Solo visualizzazione"
@@ -2328,11 +2377,11 @@ msgstr "Solo visualizzazione"
msgid "View our roadmap."
msgstr "Visualizza la nostra roadmap."
#: src/views/public/board/index.tsx:157
#: src/views/public/board/index.tsx:159
msgid "View workspace"
msgstr "Visualizza spazio di lavoro"
#: src/views/board/components/VisibilityButton.tsx:91
#: src/views/board/components/VisibilityButton.tsx:92
msgid "Visibility"
msgstr "Visibilità"
@@ -2340,6 +2389,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. "
@@ -2369,10 +2422,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:361
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:100
#: src/views/public/board/index.tsx:101
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Spazio di lavoro"
@@ -2478,7 +2531,11 @@ msgstr "Hai effettuato l'accesso con successo."
msgid "You have been signed up successfully."
msgstr "Ti sei registrato con successo."
#: src/views/members/components/InviteMemberForm.tsx:319
#: 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!"
@@ -2502,6 +2559,10 @@ msgstr "Le tue bacheche sono state importate."
msgid "Your display name has been updated."
msgstr "Il tuo nome visualizzato è stato aggiornato."
#: src/views/card/components/AttachmentUpload.tsx:26
msgid "Your file has been uploaded successfully."
msgstr "Il tuo file è stato caricato con successo."
#: src/views/settings/components/ChangePasswordConfirmation.tsx:70
msgid "Your password has been changed."
msgstr "La tua password è stata modificata."
@@ -2518,6 +2579,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

@@ -31,17 +31,17 @@ msgstr "{0}"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:442
#: src/views/board/components/NewCardForm.tsx:443
msgid "{0} labels"
msgstr "{0} labels"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:387
#: src/views/board/index.tsx:389
msgid "{0} not found"
msgstr "{0} niet gevonden"
@@ -63,6 +63,10 @@ msgstr "$8/maand"
msgid "1 user"
msgstr "1 gebruiker"
#: src/views/pricing/components/PricingTiers.tsx:136
msgid "14 day free trial"
msgstr "14 dagen gratis proefperiode"
#: src/views/home/index.tsx:71
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
msgstr "Een krachtige, flexibele kanban-app die je helpt werk te organiseren, voortgang bij te houden en resultaten te leveren—allemaal op één plek."
@@ -75,8 +79,8 @@ msgstr "Account"
msgid "Account deleted"
msgstr "Account verwijderd"
#: src/views/card/index.tsx:291
#: src/views/public/board/CardModal.tsx:143
#: src/views/card/index.tsx:343
#: src/views/public/board/CardModal.tsx:157
msgid "Activity"
msgstr "Activiteit"
@@ -105,7 +109,7 @@ msgstr "Voeg een item toe..."
msgid "Add checklist"
msgstr "Checklist toevoegen"
#: src/components/Editor.tsx:442
#: src/components/Editor.tsx:451
msgid "Add description... (type '/' to open commands or '@' to mention)"
msgstr "Beschrijving toevoegen... (typ '/' om commando's te openen of '@' om te vermelden)"
@@ -119,7 +123,7 @@ msgid "Add label"
msgstr "Label toevoegen"
#: src/views/card/components/MemberSelector.tsx:130
#: src/views/members/components/InviteMemberForm.tsx:250
#: src/views/members/components/InviteMemberForm.tsx:257
msgid "Add member"
msgstr "Lid toevoegen"
@@ -154,7 +158,7 @@ msgstr "heeft checklistitem <0>{0}</0> toegevoegd"
msgid "added label <0>{0}</0>"
msgstr "heeft label <0>{0}</0> toegevoegd"
#: src/views/members/components/InviteMemberForm.tsx:320
#: src/views/members/components/InviteMemberForm.tsx:327
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
msgstr "Het toevoegen van een nieuw lid kost een extra {price} ({billingType}) per plaats."
@@ -183,7 +187,7 @@ msgstr "Er is een fout opgetreden bij het verbreken van de verbinding met je Tre
msgid "An unexpected error occurred. Please try again later."
msgstr "Er is een onverwachte fout opgetreden. Probeer het later opnieuw."
#: src/views/members/components/InviteMemberForm.tsx:304
#: src/views/members/components/InviteMemberForm.tsx:311
msgid "Anyone with this link can join your workspace"
msgstr "Iedereen met deze link kan deelnemen aan je werkruimte"
@@ -266,6 +270,10 @@ msgstr "heeft <0>{0}</0> aan de kaart toegewezen"
msgid "Assignees"
msgstr "Toegewezen personen"
#: src/views/card/components/AttachmentUpload.tsx:25
msgid "Attachment uploaded"
msgstr "Bijlage geüpload"
#: src/views/boards/components/TemplateBoards.tsx:47
msgid "Awaiting Customer"
msgstr "Wachten op klant"
@@ -300,14 +308,14 @@ msgstr "Factureringsportaal"
msgid "Blog Post"
msgstr "Blogbericht"
#: src/views/board/index.tsx:361
#: src/views/card/index.tsx:216
#: src/views/public/board/index.tsx:100
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:229
#: src/views/public/board/index.tsx:101
msgid "Board"
msgstr "Bord"
#: src/components/NewWorkspaceForm.tsx:349
#: src/views/settings/components/UpgradeToProConfirmation.tsx:81
#: src/views/settings/components/UpgradeToProConfirmation.tsx:83
msgid "Board analytics"
msgstr "Bordanalyses"
@@ -319,7 +327,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:151
#: src/views/public/board/index.tsx:153
msgid "Board not found"
msgstr "Bord niet gevonden"
@@ -327,19 +335,19 @@ msgstr "Bord niet gevonden"
msgid "Board sharing & invites"
msgstr "Bord delen & uitnodigingen"
#: src/views/board/components/UpdateBoardSlugForm.tsx:45
#: src/views/board/components/UpdateBoardSlugForm.tsx:46
msgid "Board URL can only contain letters, numbers, and hyphens"
msgstr "Board URL kan alleen letters, cijfers en koppeltekens bevatten"
#: src/views/board/components/UpdateBoardSlugForm.tsx:43
#: src/views/board/components/UpdateBoardSlugForm.tsx:44
msgid "Board URL cannot exceed 60 characters"
msgstr "Board URL mag niet langer zijn dan 60 tekens"
#: src/views/public/board/index.tsx:67
#: src/views/public/board/index.tsx:68
msgid "Board URL copied to clipboard"
msgstr "Board URL gekopieerd naar klembord"
#: src/views/board/components/UpdateBoardSlugForm.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:42
msgid "Board URL must be at least 3 characters long"
msgstr "Board URL moet minimaal 3 tekens lang zijn"
@@ -348,11 +356,11 @@ msgstr "Board URL moet minimaal 3 tekens lang zijn"
msgid "Board visibility"
msgstr "Bordzichtbaarheid"
#: src/views/board/components/VisibilityButton.tsx:45
#: src/views/board/components/VisibilityButton.tsx:46
msgid "Board visibility updated"
msgstr "Zichtbaarheid van bord bijgewerkt"
#: src/components/SideNavigation.tsx:70
#: src/components/SideNavigation.tsx:91
msgid "Boards"
msgstr "Borden"
@@ -379,19 +387,20 @@ msgstr "Bugrapport"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
#: src/views/settings/components/UpgradeToProConfirmation.tsx:104
#: src/views/settings/components/UpgradeToProConfirmation.tsx:106
msgid "Cancel"
msgstr "Annuleren"
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "Card"
msgstr "Kaart"
#: src/views/card/index.tsx:262
#: src/views/card/index.tsx:263
#: src/views/card/index.tsx:299
msgid "Card not found"
msgstr "Kaart niet gevonden"
#: src/views/board/components/NewCardForm.tsx:309
#: src/views/board/components/NewCardForm.tsx:310
msgid "Card title"
msgstr "Kaarttitel"
@@ -424,7 +433,7 @@ msgstr "Naam checklist"
msgid "Checklists"
msgstr "Checklists"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/Filters.tsx:180
msgid "Clear filters"
msgstr "Filters wissen"
@@ -447,7 +456,7 @@ msgstr "Werk naadloos samen met je team."
#: src/components/NewWorkspaceForm.tsx:352
#: src/views/home/components/Features.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:84
#: src/views/settings/components/UpgradeToProConfirmation.tsx:86
msgid "Coming soon"
msgstr "Binnenkort beschikbaar"
@@ -485,6 +494,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"
@@ -515,12 +528,12 @@ msgstr "Neem contact op"
msgid "Content Creation"
msgstr "Content creatie"
#: src/components/AuthForm.tsx:420
#: 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}"
@@ -538,8 +551,8 @@ msgid "Create {0}"
msgstr "{0} maken"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:468
#: src/views/board/components/NewListForm.tsx:140
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Maak nog een"
@@ -547,7 +560,7 @@ msgstr "Maak nog een"
msgid "Create API key"
msgstr "API-sleutel aanmaken"
#: src/views/board/components/NewCardForm.tsx:478
#: src/views/board/components/NewCardForm.tsx:479
msgid "Create card"
msgstr "Maak kaart"
@@ -555,7 +568,7 @@ msgstr "Maak kaart"
msgid "Create checklist"
msgstr "Checklist maken"
#: src/views/members/components/InviteMemberForm.tsx:342
#: src/views/members/components/InviteMemberForm.tsx:349
msgid "Create invite link"
msgstr "Uitnodigingslink maken"
@@ -563,7 +576,7 @@ msgstr "Uitnodigingslink maken"
msgid "Create label"
msgstr "Maak label"
#: src/views/board/components/NewListForm.tsx:152
#: src/views/board/components/NewListForm.tsx:153
msgid "Create list"
msgstr "Lijst maken"
@@ -576,12 +589,12 @@ msgstr "Nieuwe {0} maken"
msgid "Create new key"
msgstr "Nieuwe sleutel aanmaken"
#: src/views/board/components/NewCardForm.tsx:404
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Maak nieuw label"
#: src/views/board/index.tsx:475
#: src/views/board/index.tsx:478
msgid "Create new list"
msgstr "Maak nieuwe lijst"
@@ -590,7 +603,7 @@ msgid "Create template"
msgstr "Sjabloon maken"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:148
#: src/components/WorkspaceMenu.tsx:149
msgid "Create workspace"
msgstr "Werkruimte maken"
@@ -632,7 +645,7 @@ msgid "Custom workspace link"
msgstr "Aangepaste werkruimtelink"
#: src/components/NewWorkspaceForm.tsx:342
#: src/views/settings/components/UpgradeToProConfirmation.tsx:74
#: src/views/settings/components/UpgradeToProConfirmation.tsx:76
msgid "Custom workspace URL"
msgstr "Aangepaste werkruimte-URL"
@@ -644,7 +657,7 @@ msgstr "Klantenservice"
msgid "Dark"
msgstr "Donker"
#: src/views/members/components/InviteMemberForm.tsx:341
#: src/views/members/components/InviteMemberForm.tsx:348
msgid "Deactivate invite link"
msgstr "Uitnodigingslink deactiveren"
@@ -684,8 +697,8 @@ msgid "Delete template"
msgstr "Sjabloon verwijderen"
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
#: src/views/settings/WorkspaceSettings.tsx:96
#: src/views/settings/WorkspaceSettings.tsx:107
#: src/views/settings/WorkspaceSettings.tsx:97
#: src/views/settings/WorkspaceSettings.tsx:108
msgid "Delete workspace"
msgstr "Werkruimte verwijderen"
@@ -739,6 +752,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"
@@ -765,8 +782,12 @@ msgstr "Heb je geen account? <0><1>Registreer je</1></0>"
msgid "Done"
msgstr "Klaar"
#: src/views/card/components/AttachmentThumbnails.tsx:141
msgid "Download failed"
msgstr "Download mislukt"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:115
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
msgstr "Board URL bewerken"
@@ -778,7 +799,7 @@ msgstr "Reactie bewerken"
msgid "Edit label"
msgstr "Label bewerken"
#: src/views/board/components/UpdateBoardSlugForm.tsx:161
#: src/views/board/components/UpdateBoardSlugForm.tsx:162
msgid "Edit workspace URL"
msgstr "Werkruimte-URL bewerken"
@@ -786,11 +807,11 @@ msgstr "Werkruimte-URL bewerken"
msgid "Editing"
msgstr "Bewerken"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "e-mail"
#: src/views/members/components/InviteMemberForm.tsx:265
#: src/views/members/components/InviteMemberForm.tsx:272
msgid "Email"
msgstr "E-mail"
@@ -806,11 +827,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:387
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Voer je e-mailadres in"
#: src/components/AuthForm.tsx:375
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Voer je naam in"
@@ -818,13 +839,13 @@ msgstr "Voer je naam in"
msgid "Enter your new password"
msgstr "Voer je nieuwe wachtwoord in"
#: src/components/AuthForm.tsx:400
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Voer je wachtwoord in"
#: src/views/boards/components/NewBoardForm.tsx:77
#: src/views/boards/components/NewBoardForm.tsx:92
#: src/views/members/components/InviteMemberForm.tsx:208
#: src/views/members/components/InviteMemberForm.tsx:215
msgid "Error"
msgstr "Fout"
@@ -881,7 +902,7 @@ msgstr "Fout bij bijwerken naam werkruimte"
msgid "Error updating workspace URL"
msgstr "Fout bij het bijwerken van werkruimte-URL"
#: src/views/members/components/InviteMemberForm.tsx:233
#: src/views/members/components/InviteMemberForm.tsx:240
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
msgid "Error upgrading subscription"
msgstr "Fout bij het upgraden van abonnement"
@@ -913,7 +934,7 @@ msgstr "Uitvoering"
msgid "Failed to accept invitation. Please try again later, or contact customer support."
msgstr "Uitnodiging accepteren mislukt. Probeer het later opnieuw of neem contact op met de klantenservice."
#: src/views/members/components/InviteMemberForm.tsx:209
#: src/views/members/components/InviteMemberForm.tsx:216
msgid "Failed to copy invite link"
msgstr "Kopiëren van uitnodigingslink mislukt"
@@ -927,6 +948,11 @@ msgstr "Bord maken mislukt"
msgid "Failed to login with {0}. Please try again."
msgstr "Inloggen met {0} is mislukt. Probeer het opnieuw."
#: src/views/card/components/AttachmentUpload.tsx:33
#: src/views/card/components/AttachmentUpload.tsx:79
msgid "Failed to upload attachment. Please try again."
msgstr "Bijlage uploaden mislukt. Probeer het opnieuw."
#: src/views/home/components/Header.tsx:41
msgid "FAQ"
msgstr "FAQ"
@@ -970,7 +996,7 @@ msgstr "Feedback"
msgid "Feedback sent"
msgstr "Feedback verzonden"
#: src/views/board/components/Filters.tsx:149
#: src/views/board/components/Filters.tsx:174
msgid "Filter"
msgstr "Filter"
@@ -983,13 +1009,11 @@ msgid "For long-term sustainability, we recognise all good open source projects
msgstr "Voor duurzaamheid op lange termijn erkennen we dat alle goede open source projecten een inkomstenbron nodig hebben. De onze komt van de betaalde cloudoplossing voor werkruimtes met meerdere gebruikers en voor aangepaste werkruimte-slugs. Er is geen verplichting om het te gebruiken, en je kunt de software gratis op je eigen infrastructuur hosten."
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/PricingTiers.tsx:30
#: src/views/pricing/components/PricingTiers.tsx:30
msgid "Free"
msgstr "Gratis"
#: src/views/members/components/InviteMemberForm.tsx:326
#: src/views/members/components/InviteMemberForm.tsx:333
#: src/views/members/index.tsx:208
msgid "Free Plan"
msgstr "Gratis plan"
@@ -1026,7 +1050,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:467
#: src/views/board/index.tsx:470
msgid "Get started by creating a new list"
msgstr "Begin door een nieuwe lijst te maken"
@@ -1181,11 +1205,11 @@ msgstr "Ongeldige uitnodiging"
msgid "Invite"
msgstr "Uitnodigen"
#: src/views/members/components/InviteMemberForm.tsx:202
#: src/views/members/components/InviteMemberForm.tsx:209
msgid "Invite link copied"
msgstr "Uitnodigingslink gekopieerd"
#: src/views/members/components/InviteMemberForm.tsx:203
#: src/views/members/components/InviteMemberForm.tsx:210
msgid "Invite link copied to clipboard"
msgstr "Uitnodigingslink gekopieerd naar klembord"
@@ -1198,11 +1222,11 @@ msgid "Invite links require a Team or Pro subscription. Please upgrade your work
msgstr "Uitnodigingslinks vereisen een Team- of Pro-abonnement. Upgrade je werkruimte."
#: src/views/card/components/MemberSelector.tsx:111
#: src/views/members/components/InviteMemberForm.tsx:365
#: src/views/members/components/InviteMemberForm.tsx:367
msgid "Invite member"
msgstr "Lid uitnodigen"
#: src/views/members/components/InviteMemberForm.tsx:329
#: src/views/members/components/InviteMemberForm.tsx:336
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
msgstr "Voor het uitnodigen van leden is een teamplan vereist. Je wordt doorgestuurd om je werkruimte te upgraden."
@@ -1240,9 +1264,9 @@ 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:101
#: src/views/board/components/NewCardForm.tsx:408
#: src/views/card/index.tsx:117
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
msgid "Labels"
msgstr "Labels"
@@ -1259,7 +1283,7 @@ msgid "Language"
msgstr "Taal"
#: src/components/NewWorkspaceForm.tsx:335
#: src/views/settings/components/UpgradeToProConfirmation.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:69
msgid "Launch offer"
msgstr "Introductieaanbieding"
@@ -1292,18 +1316,22 @@ msgstr "Licentie"
msgid "Light"
msgstr "Licht"
#: src/views/public/board/index.tsx:65
#: src/views/public/board/index.tsx:66
msgid "Link copied"
msgstr "Link gekopieerd"
#: src/views/card/index.tsx:109
#: src/views/card/index.tsx:111
msgid "List"
msgstr "Lijst"
#: src/views/board/components/NewListForm.tsx:128
#: src/views/board/components/NewListForm.tsx:129
msgid "List name"
msgstr "Lijstnaam"
#: src/views/board/components/Filters.tsx:123
msgid "Lists"
msgstr "Lijsten"
#: src/views/auth/login/index.tsx:33
msgid "Login | kan.bn"
msgstr "Login | kan.bn"
@@ -1324,7 +1352,7 @@ msgstr "Geliefd door teams wereldwijd"
msgid "Low Priority"
msgstr "Lage prioriteit"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "magische link"
@@ -1345,10 +1373,10 @@ msgstr "heeft checklistitem <0>{0}</0> als onvoltooid gemarkeerd"
msgid "Medium Priority"
msgstr "Gemiddelde prioriteit"
#: src/components/SideNavigation.tsx:80
#: src/views/board/components/Filters.tsx:93
#: src/views/board/components/NewCardForm.tsx:366
#: src/views/card/index.tsx:126
#: 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/views/members/index.tsx:178
msgid "Members"
msgstr "Leden"
@@ -1401,7 +1429,7 @@ msgstr "Nieuwe {0}"
msgid "New API key"
msgstr "Nieuwe API-sleutel"
#: src/views/board/components/NewCardForm.tsx:292
#: src/views/board/components/NewCardForm.tsx:293
msgid "New card"
msgstr "Nieuwe kaart"
@@ -1417,8 +1445,8 @@ msgstr "Nieuwe import"
msgid "New label"
msgstr "Nieuw label"
#: src/views/board/components/NewListForm.tsx:112
#: src/views/board/index.tsx:439
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
msgid "New list"
msgstr "Nieuwe lijst"
@@ -1455,6 +1483,10 @@ msgstr "Volgende stappen"
msgid "No {0}"
msgstr "Geen {0}"
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Er zijn momenteel geen authenticatiemethoden beschikbaar"
#: src/views/boards/components/ImportBoardsForm.tsx:242
msgid "No boards found"
msgstr "Geen borden gevonden"
@@ -1463,7 +1495,11 @@ msgstr "Geen borden gevonden"
msgid "No credit card required"
msgstr "Geen creditcard vereist"
#: src/views/board/index.tsx:464
#: 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
msgid "No lists"
msgstr "Geen lijsten"
@@ -1483,7 +1519,7 @@ msgstr "Inwerktraject"
msgid "Once you delete your account, there is no going back. This action cannot be undone."
msgstr "Zodra je je account verwijdert, is er geen weg terug. Deze actie kan niet ongedaan worden gemaakt."
#: src/views/settings/WorkspaceSettings.tsx:99
#: src/views/settings/WorkspaceSettings.tsx:100
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."
@@ -1495,7 +1531,7 @@ msgstr "Open source"
msgid "Open Source Friends"
msgstr "Open Source Vrienden"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "of"
@@ -1577,15 +1613,15 @@ msgstr "Platform"
msgid "Please confirm your new password"
msgstr "Bevestig je nieuwe wachtwoord"
#: src/components/AuthForm.tsx:391
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Voer een geldig e-mailadres in"
#: src/components/AuthForm.tsx:379
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Voer een geldige naam in"
#: src/components/AuthForm.tsx:404
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Voer een geldig wachtwoord in"
@@ -1596,15 +1632,16 @@ 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:176
#: src/views/board/components/NewListForm.tsx:78
#: src/views/board/components/NewCardForm.tsx:177
#: 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:72
#: src/views/board/components/VisibilityButton.tsx:53
#: src/views/board/index.tsx:149
#: src/views/board/index.tsx:205
#: 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/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
#: src/views/card/components/ChecklistItemRow.tsx:88
#: src/views/card/components/ChecklistNameInput.tsx:46
@@ -1618,10 +1655,10 @@ msgstr "Selecteer een bestand om te uploaden."
#: 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:175
#: src/views/card/index.tsx:177
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:234
#: src/views/members/components/InviteMemberForm.tsx:241
#: src/views/settings/components/Avatar.tsx:78
#: src/views/settings/components/Avatar.tsx:219
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
@@ -1654,7 +1691,7 @@ msgstr "Prijzen"
#: src/views/pricing/components/Pricing.tsx:56
#: src/views/pricing/components/PricingTiers.tsx:53
#: src/views/settings/components/UpgradeToProConfirmation.tsx:91
#: src/views/settings/components/UpgradeToProConfirmation.tsx:93
msgid "Priority email support"
msgstr "Prioriteit e-mailondersteuning"
@@ -1662,7 +1699,7 @@ msgstr "Prioriteit e-mailondersteuning"
msgid "Privacy policy"
msgstr "Privacybeleid"
#: src/views/board/components/VisibilityButton.tsx:73
#: src/views/board/components/VisibilityButton.tsx:74
msgid "Private"
msgstr "Privé"
@@ -1670,7 +1707,7 @@ msgstr "Privé"
msgid "Pro Plan"
msgstr "Pro Plan"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
msgid "Pro Plan ∞"
msgstr "Pro Plan ∞"
@@ -1682,7 +1719,7 @@ msgstr "Profielafbeelding bijgewerkt"
msgid "Profile picture"
msgstr "Profielfoto"
#: src/views/board/components/VisibilityButton.tsx:68
#: src/views/board/components/VisibilityButton.tsx:69
msgid "Public"
msgstr "Openbaar"
@@ -1835,7 +1872,7 @@ msgid "Senior"
msgstr "Senior"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:85
#: src/components/SideNavigation.tsx:106
msgid "Settings"
msgstr "Instellingen"
@@ -1884,7 +1921,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:420
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Registreren met "
@@ -1909,6 +1946,11 @@ msgstr "Softwareontwikkeling"
msgid "Star on Github"
msgstr "Star op Github"
#: src/views/members/components/InviteMemberForm.tsx:359
#: src/views/settings/components/UpgradeToProConfirmation.tsx:111
msgid "Start 14 day free trial"
msgstr "Start 14 dagen gratis proefperiode"
#: src/views/members/components/InviteMemberForm.tsx:122
msgid "Subscription Required"
msgstr "Abonnement vereist"
@@ -1918,7 +1960,7 @@ msgstr "Abonnement vereist"
msgid "Success"
msgstr "Geslaagd"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:55
#: src/views/settings/components/UpgradeToProConfirmation.tsx:57
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:"
@@ -1935,7 +1977,7 @@ msgstr "Ondersteun de ontwikkeling van het project"
msgid "System"
msgstr "Systeem"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
#: src/views/members/index.tsx:207
msgid "Team Plan"
msgstr "Teamplan"
@@ -1946,8 +1988,8 @@ msgstr "Teamplan"
msgid "Teams"
msgstr "Teams"
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:396
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
msgid "Template"
msgstr "Sjabloon"
@@ -1967,7 +2009,7 @@ msgstr "Sjabloonnaam mag niet langer zijn dan 100 tekens"
msgid "Template name is required"
msgstr "Sjabloonnaam is verplicht"
#: src/components/SideNavigation.tsx:75
#: src/components/SideNavigation.tsx:96
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Sjablonen"
@@ -1997,7 +2039,7 @@ msgid "The open source <0/> alternative to Trello"
msgstr "Het open source <0/> alternatief voor Trello"
#. placeholder {0}: isPublic ? "public" : "private"
#: src/views/board/components/VisibilityButton.tsx:46
#: src/views/board/components/VisibilityButton.tsx:47
msgid "The visibility of your board has been set to {0}."
msgstr "De zichtbaarheid van je bord is ingesteld op {0}."
@@ -2021,11 +2063,11 @@ 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:154
#: src/views/public/board/index.tsx:156
msgid "This board is private or does not exist"
msgstr "Dit bord is privé of bestaat niet"
#: src/views/board/components/UpdateBoardSlugForm.tsx:135
#: src/views/board/components/UpdateBoardSlugForm.tsx:136
msgid "This board URL has already been taken"
msgstr "Deze board URL is al in gebruik"
@@ -2095,7 +2137,7 @@ msgstr "Kan checklistitem niet toevoegen"
msgid "Unable to add comment"
msgstr "Kan geen reactie toevoegen"
#: src/views/board/components/NewCardForm.tsx:173
#: src/views/board/components/NewCardForm.tsx:174
msgid "Unable to create card"
msgstr "Kan kaart niet aanmaken"
@@ -2103,7 +2145,7 @@ msgstr "Kan kaart niet aanmaken"
msgid "Unable to create checklist"
msgstr "Kan checklist niet maken"
#: src/views/board/components/NewListForm.tsx:77
#: src/views/board/components/NewListForm.tsx:78
msgid "Unable to create list"
msgstr "Kan geen lijst maken"
@@ -2116,6 +2158,10 @@ msgstr "Kan sjabloon niet maken"
msgid "Unable to create workspace"
msgstr "Kan werkruimte niet aanmaken"
#: src/views/card/components/AttachmentThumbnails.tsx:72
msgid "Unable to delete attachment"
msgstr "Kan bijlage niet verwijderen"
#: src/views/card/components/DeleteCardConfirmation.tsx:51
msgid "Unable to delete card"
msgstr "Kan kaart niet verwijderen"
@@ -2140,16 +2186,16 @@ msgstr "Kan lid niet verwijderen"
msgid "Unable to send feedback"
msgstr "Feedback kon niet worden verzonden"
#: src/views/board/components/UpdateBoardSlugForm.tsx:71
#: src/views/board/components/UpdateBoardSlugForm.tsx:72
msgid "Unable to update board URL"
msgstr "Kan board URL niet bijwerken"
#: src/views/board/components/VisibilityButton.tsx:52
#: src/views/board/components/VisibilityButton.tsx:53
msgid "Unable to update board visibility"
msgstr "Kan bordzichtbaarheid niet bijwerken"
#: src/views/board/index.tsx:204
#: src/views/card/index.tsx:174
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
msgid "Unable to update card"
msgstr "Kan kaart niet bijwerken"
@@ -2169,7 +2215,7 @@ msgstr "Kan reactie niet bijwerken"
msgid "Unable to update labels"
msgstr "Kan labels niet bijwerken"
#: src/views/board/index.tsx:148
#: src/views/board/index.tsx:150
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Kan lijst niet bijwerken"
@@ -2217,7 +2263,7 @@ msgid "Unlimited lists"
msgstr "Onbeperkt aantal lijsten"
#: src/components/NewWorkspaceForm.tsx:332
#: src/views/settings/components/UpgradeToProConfirmation.tsx:64
#: src/views/settings/components/UpgradeToProConfirmation.tsx:66
msgid "Unlimited members"
msgstr "Onbeperkt aantal leden"
@@ -2225,7 +2271,12 @@ msgstr "Onbeperkt aantal leden"
msgid "Unlimited workspaces"
msgstr "Onbeperkte werkruimtes"
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
#: 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
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
@@ -2254,12 +2305,9 @@ msgstr "titel bijgewerkt"
msgid "updated the title to <0>{0}</0>"
msgstr "titel bijgewerkt naar <0>{0}</0>"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:109
msgid "Upgrade"
msgstr "Upgraden"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
#: src/views/settings/WorkspaceSettings.tsx:89
#: src/components/SideNavigation.tsx:202
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
msgstr "Upgraden naar Pro"
@@ -2267,9 +2315,10 @@ msgstr "Upgraden naar Pro"
msgid "Upgrade to Pro ($29/month)"
msgstr "Upgrade naar Pro ($29/maand)"
#: src/views/members/components/InviteMemberForm.tsx:357
msgid "Upgrade to Team Plan"
msgstr "Upgraden naar teamplan"
#: src/views/card/components/AttachmentUpload.tsx:32
#: src/views/card/components/AttachmentUpload.tsx:78
msgid "Upload failed"
msgstr "Uploaden mislukt"
#: src/views/boards/components/TemplateBoards.tsx:38
#: src/views/boards/components/TemplateBoards.tsx:55
@@ -2320,7 +2369,7 @@ msgstr "Bekijk en beheer je facturering en abonnement."
msgid "View docs"
msgstr "Bekijk documentatie"
#: src/views/public/board/index.tsx:128
#: src/views/public/board/index.tsx:129
msgid "View only"
msgstr "Alleen bekijken"
@@ -2328,11 +2377,11 @@ msgstr "Alleen bekijken"
msgid "View our roadmap."
msgstr "Bekijk onze roadmap."
#: src/views/public/board/index.tsx:157
#: src/views/public/board/index.tsx:159
msgid "View workspace"
msgstr "Werkruimte bekijken"
#: src/views/board/components/VisibilityButton.tsx:91
#: src/views/board/components/VisibilityButton.tsx:92
msgid "Visibility"
msgstr "Zichtbaarheid"
@@ -2340,6 +2389,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. "
@@ -2369,10 +2422,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:361
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:100
#: src/views/public/board/index.tsx:101
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Werkruimte"
@@ -2478,7 +2531,11 @@ msgstr "Je bent succesvol ingelogd."
msgid "You have been signed up successfully."
msgstr "Je bent succesvol geregistreerd."
#: src/views/members/components/InviteMemberForm.tsx:319
#: 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!"
@@ -2502,6 +2559,10 @@ msgstr "Je borden zijn geïmporteerd."
msgid "Your display name has been updated."
msgstr "Je weergavenaam is bijgewerkt."
#: src/views/card/components/AttachmentUpload.tsx:26
msgid "Your file has been uploaded successfully."
msgstr "Je bestand is succesvol geüpload."
#: src/views/settings/components/ChangePasswordConfirmation.tsx:70
msgid "Your password has been changed."
msgstr "Je wachtwoord is gewijzigd."
@@ -2518,6 +2579,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

@@ -36,17 +36,17 @@ msgstr "{0}"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:442
#: src/views/board/components/NewCardForm.tsx:443
msgid "{0} labels"
msgstr "{0} etykiety"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:387
#: src/views/board/index.tsx:389
msgid "{0} not found"
msgstr "{0} nie znaleziono"
@@ -68,6 +68,10 @@ msgstr "8 USD/miesiąc"
msgid "1 user"
msgstr "1 użytkownik"
#: src/views/pricing/components/PricingTiers.tsx:136
msgid "14 day free trial"
msgstr "14-dniowy bezpłatny okres próbny"
#: src/views/home/index.tsx:71
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
msgstr "Potężna, elastyczna aplikacja kanban, która pomaga organizować pracę, śledzić postępy i dostarczać wyniki — wszystko w jednym miejscu."
@@ -80,8 +84,8 @@ msgstr "Konto"
msgid "Account deleted"
msgstr "Konto zostało usunięte"
#: src/views/card/index.tsx:291
#: src/views/public/board/CardModal.tsx:143
#: src/views/card/index.tsx:343
#: src/views/public/board/CardModal.tsx:157
msgid "Activity"
msgstr "Aktywność"
@@ -110,7 +114,7 @@ msgstr "Dodaj element..."
msgid "Add checklist"
msgstr "Dodaj listę kontrolną"
#: src/components/Editor.tsx:442
#: src/components/Editor.tsx:451
msgid "Add description... (type '/' to open commands or '@' to mention)"
msgstr "Dodaj opis... (wpisz '/' aby otworzyć polecenia lub '@' aby wspomnieć)"
@@ -124,7 +128,7 @@ msgid "Add label"
msgstr "Dodaj etykietę"
#: src/views/card/components/MemberSelector.tsx:130
#: src/views/members/components/InviteMemberForm.tsx:250
#: src/views/members/components/InviteMemberForm.tsx:257
msgid "Add member"
msgstr "Dodaj członka"
@@ -159,7 +163,7 @@ msgstr "dodał(a) element listy kontrolnej <0>{0}</0>"
msgid "added label <0>{0}</0>"
msgstr "dodał(a) etykietę <0>{0}</0>"
#: src/views/members/components/InviteMemberForm.tsx:320
#: src/views/members/components/InviteMemberForm.tsx:327
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
msgstr "Dodanie nowego członka będzie kosztować dodatkowe {price} ({billingType}) za miejsce."
@@ -188,7 +192,7 @@ msgstr "Wystąpił błąd podczas odłączania konta Trello."
msgid "An unexpected error occurred. Please try again later."
msgstr "Wystąpił nieoczekiwany błąd. Spróbuj ponownie później."
#: src/views/members/components/InviteMemberForm.tsx:304
#: src/views/members/components/InviteMemberForm.tsx:311
msgid "Anyone with this link can join your workspace"
msgstr "Każdy z tym linkiem może dołączyć do twojego obszaru roboczego"
@@ -271,6 +275,10 @@ msgstr "przypisano <0>{0}</0> do karty"
msgid "Assignees"
msgstr "Osoby przypisane"
#: src/views/card/components/AttachmentUpload.tsx:25
msgid "Attachment uploaded"
msgstr "Załącznik został przesłany"
#: src/views/boards/components/TemplateBoards.tsx:47
msgid "Awaiting Customer"
msgstr "Oczekuje na klienta"
@@ -305,14 +313,14 @@ msgstr "Portal rozliczeniowy"
msgid "Blog Post"
msgstr "Post na blogu"
#: src/views/board/index.tsx:361
#: src/views/card/index.tsx:216
#: src/views/public/board/index.tsx:100
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:229
#: src/views/public/board/index.tsx:101
msgid "Board"
msgstr "Tablica"
#: src/components/NewWorkspaceForm.tsx:349
#: src/views/settings/components/UpgradeToProConfirmation.tsx:81
#: src/views/settings/components/UpgradeToProConfirmation.tsx:83
msgid "Board analytics"
msgstr "Analiza tablicy"
@@ -324,7 +332,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:151
#: src/views/public/board/index.tsx:153
msgid "Board not found"
msgstr "Tablica nie została znaleziona"
@@ -332,19 +340,19 @@ msgstr "Tablica nie została znaleziona"
msgid "Board sharing & invites"
msgstr "Udostępnianie tablicy i zaproszenia"
#: src/views/board/components/UpdateBoardSlugForm.tsx:45
#: src/views/board/components/UpdateBoardSlugForm.tsx:46
msgid "Board URL can only contain letters, numbers, and hyphens"
msgstr "Adres URL tablicy może zawierać tylko litery, cyfry i myślniki"
#: src/views/board/components/UpdateBoardSlugForm.tsx:43
#: src/views/board/components/UpdateBoardSlugForm.tsx:44
msgid "Board URL cannot exceed 60 characters"
msgstr "Adres URL tablicy nie może przekraczać 60 znaków"
#: src/views/public/board/index.tsx:67
#: src/views/public/board/index.tsx:68
msgid "Board URL copied to clipboard"
msgstr "Adres URL tablicy został skopiowany do schowka"
#: src/views/board/components/UpdateBoardSlugForm.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:42
msgid "Board URL must be at least 3 characters long"
msgstr "Adres URL tablicy musi mieć co najmniej 3 znaki"
@@ -353,11 +361,11 @@ msgstr "Adres URL tablicy musi mieć co najmniej 3 znaki"
msgid "Board visibility"
msgstr "Widoczność tablicy"
#: src/views/board/components/VisibilityButton.tsx:45
#: src/views/board/components/VisibilityButton.tsx:46
msgid "Board visibility updated"
msgstr "Widoczność tablicy została zaktualizowana"
#: src/components/SideNavigation.tsx:70
#: src/components/SideNavigation.tsx:91
msgid "Boards"
msgstr "Tablice"
@@ -384,19 +392,20 @@ msgstr "Zgłoszenie błędu"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
#: src/views/settings/components/UpgradeToProConfirmation.tsx:104
#: src/views/settings/components/UpgradeToProConfirmation.tsx:106
msgid "Cancel"
msgstr "Anuluj"
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "Card"
msgstr "Karta"
#: src/views/card/index.tsx:262
#: src/views/card/index.tsx:263
#: src/views/card/index.tsx:299
msgid "Card not found"
msgstr "Nie można było znaleźć karty"
#: src/views/board/components/NewCardForm.tsx:309
#: src/views/board/components/NewCardForm.tsx:310
msgid "Card title"
msgstr "Tytuł karty"
@@ -429,7 +438,7 @@ msgstr "Nazwa listy kontrolnej"
msgid "Checklists"
msgstr "Listy kontrolne"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/Filters.tsx:180
msgid "Clear filters"
msgstr "Wyczyść filtry"
@@ -452,7 +461,7 @@ msgstr "Współpracuj płynnie ze swoim zespołem."
#: src/components/NewWorkspaceForm.tsx:352
#: src/views/home/components/Features.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:84
#: src/views/settings/components/UpgradeToProConfirmation.tsx:86
msgid "Coming soon"
msgstr "Już wkrótce"
@@ -490,6 +499,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"
@@ -520,12 +533,12 @@ msgstr "Skontaktuj się z nami"
msgid "Content Creation"
msgstr "Tworzenie treści"
#: src/components/AuthForm.tsx:420
#: 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}"
@@ -543,8 +556,8 @@ msgid "Create {0}"
msgstr "Utwórz {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:468
#: src/views/board/components/NewListForm.tsx:140
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Utwórz kolejny"
@@ -552,7 +565,7 @@ msgstr "Utwórz kolejny"
msgid "Create API key"
msgstr "Utwórz klucz API"
#: src/views/board/components/NewCardForm.tsx:478
#: src/views/board/components/NewCardForm.tsx:479
msgid "Create card"
msgstr "Utwórz kartę"
@@ -560,7 +573,7 @@ msgstr "Utwórz kartę"
msgid "Create checklist"
msgstr "Utwórz listę kontrolną"
#: src/views/members/components/InviteMemberForm.tsx:342
#: src/views/members/components/InviteMemberForm.tsx:349
msgid "Create invite link"
msgstr "Utwórz link zaproszenia"
@@ -568,7 +581,7 @@ msgstr "Utwórz link zaproszenia"
msgid "Create label"
msgstr "Utwórz etykietę"
#: src/views/board/components/NewListForm.tsx:152
#: src/views/board/components/NewListForm.tsx:153
msgid "Create list"
msgstr "Utwórz listę"
@@ -581,12 +594,12 @@ msgstr "Utwórz nowy {0}"
msgid "Create new key"
msgstr "Utwórz nowy klucz"
#: src/views/board/components/NewCardForm.tsx:404
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Utwórz nową etykietę"
#: src/views/board/index.tsx:475
#: src/views/board/index.tsx:478
msgid "Create new list"
msgstr "Utwórz nową listę"
@@ -595,7 +608,7 @@ msgid "Create template"
msgstr "Utwórz szablon"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:148
#: src/components/WorkspaceMenu.tsx:149
msgid "Create workspace"
msgstr "Utwórz przestrzeń roboczą"
@@ -637,7 +650,7 @@ msgid "Custom workspace link"
msgstr "Niestandardowy link do przestrzeni roboczej"
#: src/components/NewWorkspaceForm.tsx:342
#: src/views/settings/components/UpgradeToProConfirmation.tsx:74
#: src/views/settings/components/UpgradeToProConfirmation.tsx:76
msgid "Custom workspace URL"
msgstr "Niestandardowy adres URL przestrzeni roboczej"
@@ -649,7 +662,7 @@ msgstr "Obsługa klienta"
msgid "Dark"
msgstr "Ciemny"
#: src/views/members/components/InviteMemberForm.tsx:341
#: src/views/members/components/InviteMemberForm.tsx:348
msgid "Deactivate invite link"
msgstr "Dezaktywuj link zaproszenia"
@@ -689,8 +702,8 @@ msgid "Delete template"
msgstr "Usuń szablon"
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
#: src/views/settings/WorkspaceSettings.tsx:96
#: src/views/settings/WorkspaceSettings.tsx:107
#: src/views/settings/WorkspaceSettings.tsx:97
#: src/views/settings/WorkspaceSettings.tsx:108
msgid "Delete workspace"
msgstr "Usuń przestrzeń roboczą"
@@ -744,6 +757,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"
@@ -770,8 +787,12 @@ msgstr "Nie masz konta? <0><1>Zarejestruj się</1></0>"
msgid "Done"
msgstr "Gotowe"
#: src/views/card/components/AttachmentThumbnails.tsx:141
msgid "Download failed"
msgstr "Pobieranie nie powiodło się"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:115
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
msgstr "Edytuj URL tablicy"
@@ -783,7 +804,7 @@ msgstr "Edytuj komentarz"
msgid "Edit label"
msgstr "Edytuj etykietę"
#: src/views/board/components/UpdateBoardSlugForm.tsx:161
#: src/views/board/components/UpdateBoardSlugForm.tsx:162
msgid "Edit workspace URL"
msgstr "Edytuj URL przestrzeni roboczej"
@@ -791,11 +812,11 @@ msgstr "Edytuj URL przestrzeni roboczej"
msgid "Editing"
msgstr "Edycja"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "email"
#: src/views/members/components/InviteMemberForm.tsx:265
#: src/views/members/components/InviteMemberForm.tsx:272
msgid "Email"
msgstr "Email"
@@ -811,11 +832,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:387
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Wprowadź swój adres e-mail"
#: src/components/AuthForm.tsx:375
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Wprowadź swoje imię"
@@ -823,13 +844,13 @@ msgstr "Wprowadź swoje imię"
msgid "Enter your new password"
msgstr "Wprowadź swoje nowe hasło"
#: src/components/AuthForm.tsx:400
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Wprowadź swoje hasło"
#: src/views/boards/components/NewBoardForm.tsx:77
#: src/views/boards/components/NewBoardForm.tsx:92
#: src/views/members/components/InviteMemberForm.tsx:208
#: src/views/members/components/InviteMemberForm.tsx:215
msgid "Error"
msgstr "Błąd"
@@ -886,7 +907,7 @@ msgstr "Błąd podczas aktualizacji nazwy przestrzeni roboczej"
msgid "Error updating workspace URL"
msgstr "Błąd podczas aktualizacji adresu URL przestrzeni roboczej"
#: src/views/members/components/InviteMemberForm.tsx:233
#: src/views/members/components/InviteMemberForm.tsx:240
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
msgid "Error upgrading subscription"
msgstr "Błąd podczas aktualizacji subskrypcji"
@@ -918,7 +939,7 @@ msgstr "Wykonanie"
msgid "Failed to accept invitation. Please try again later, or contact customer support."
msgstr "Nie udało się zaakceptować zaproszenia. Spróbuj ponownie później lub skontaktuj się z działem obsługi klienta."
#: src/views/members/components/InviteMemberForm.tsx:209
#: src/views/members/components/InviteMemberForm.tsx:216
msgid "Failed to copy invite link"
msgstr "Nie udało się skopiować linku zaproszenia"
@@ -932,6 +953,11 @@ msgstr "Nie udało się utworzyć tablicy"
msgid "Failed to login with {0}. Please try again."
msgstr "Nie udało się zalogować za pomocą {0}. Spróbuj ponownie."
#: src/views/card/components/AttachmentUpload.tsx:33
#: src/views/card/components/AttachmentUpload.tsx:79
msgid "Failed to upload attachment. Please try again."
msgstr "Nie udało się przesłać załącznika. Proszę spróbować ponownie."
#: src/views/home/components/Header.tsx:41
msgid "FAQ"
msgstr "FAQ"
@@ -975,7 +1001,7 @@ msgstr "Opinie"
msgid "Feedback sent"
msgstr "Opinia wysłana"
#: src/views/board/components/Filters.tsx:149
#: src/views/board/components/Filters.tsx:174
msgid "Filter"
msgstr "Filtr"
@@ -988,13 +1014,11 @@ msgid "For long-term sustainability, we recognise all good open source projects
msgstr "Dla długoterminowej trwałości uznajemy, że wszystkie dobre projekty open source potrzebują źródła dochodu. Nasze pochodzi z płatnej oferty w chmurze dla przestrzeni roboczych z wieloma użytkownikami oraz dla niestandardowych nazw przestrzeni roboczych. Nie ma obowiązku korzystania z niej, a oprogramowanie można hostować samodzielnie na własnej infrastrukturze bezpłatnie."
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/PricingTiers.tsx:30
#: src/views/pricing/components/PricingTiers.tsx:30
msgid "Free"
msgstr "Darmowy"
#: src/views/members/components/InviteMemberForm.tsx:326
#: src/views/members/components/InviteMemberForm.tsx:333
#: src/views/members/index.tsx:208
msgid "Free Plan"
msgstr "Darmowy plan"
@@ -1031,7 +1055,7 @@ msgstr "Rozpocznij"
msgid "Get started by creating a new {0}"
msgstr "Rozpocznij, tworząc nowy {0}"
#: src/views/board/index.tsx:467
#: src/views/board/index.tsx:470
msgid "Get started by creating a new list"
msgstr "Rozpocznij, tworząc nową listę"
@@ -1186,11 +1210,11 @@ msgstr "Nieprawidłowe zaproszenie"
msgid "Invite"
msgstr "Zaproś"
#: src/views/members/components/InviteMemberForm.tsx:202
#: src/views/members/components/InviteMemberForm.tsx:209
msgid "Invite link copied"
msgstr "Link zaproszenia skopiowany"
#: src/views/members/components/InviteMemberForm.tsx:203
#: src/views/members/components/InviteMemberForm.tsx:210
msgid "Invite link copied to clipboard"
msgstr "Link zaproszenia skopiowany do schowka"
@@ -1203,11 +1227,11 @@ msgid "Invite links require a Team or Pro subscription. Please upgrade your work
msgstr "Linki zaproszeń wymagają subskrypcji Team lub Pro. Proszę ulepszyć swoje środowisko pracy."
#: src/views/card/components/MemberSelector.tsx:111
#: src/views/members/components/InviteMemberForm.tsx:365
#: src/views/members/components/InviteMemberForm.tsx:367
msgid "Invite member"
msgstr "Zaproś członka"
#: src/views/members/components/InviteMemberForm.tsx:329
#: src/views/members/components/InviteMemberForm.tsx:336
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
msgstr "Zapraszanie członków wymaga planu Team. Zostaniesz przekierowany, aby ulepszyć swoje środowisko pracy."
@@ -1245,9 +1269,9 @@ 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:101
#: src/views/board/components/NewCardForm.tsx:408
#: src/views/card/index.tsx:117
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
msgid "Labels"
msgstr "Etykiety"
@@ -1264,7 +1288,7 @@ msgid "Language"
msgstr "Język"
#: src/components/NewWorkspaceForm.tsx:335
#: src/views/settings/components/UpgradeToProConfirmation.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:69
msgid "Launch offer"
msgstr "Oferta startowa"
@@ -1297,18 +1321,22 @@ msgstr "Licencja"
msgid "Light"
msgstr "Jasny"
#: src/views/public/board/index.tsx:65
#: src/views/public/board/index.tsx:66
msgid "Link copied"
msgstr "Link skopiowany"
#: src/views/card/index.tsx:109
#: src/views/card/index.tsx:111
msgid "List"
msgstr "Lista"
#: src/views/board/components/NewListForm.tsx:128
#: src/views/board/components/NewListForm.tsx:129
msgid "List name"
msgstr "Nazwa listy"
#: src/views/board/components/Filters.tsx:123
msgid "Lists"
msgstr "Listy"
#: src/views/auth/login/index.tsx:33
msgid "Login | kan.bn"
msgstr "Zaloguj się | kan.bn"
@@ -1329,7 +1357,7 @@ msgstr "Uwielbiane przez zespoły na całym świecie"
msgid "Low Priority"
msgstr "Niski priorytet"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "magiczny link"
@@ -1350,10 +1378,10 @@ msgstr "oznaczył element listy kontrolnej <0>{0}</0> jako nieukończony"
msgid "Medium Priority"
msgstr "Średni priorytet"
#: src/components/SideNavigation.tsx:80
#: src/views/board/components/Filters.tsx:93
#: src/views/board/components/NewCardForm.tsx:366
#: src/views/card/index.tsx:126
#: 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/views/members/index.tsx:178
msgid "Members"
msgstr "Członkowie"
@@ -1406,7 +1434,7 @@ msgstr "Nowy {0}"
msgid "New API key"
msgstr "Nowy klucz API"
#: src/views/board/components/NewCardForm.tsx:292
#: src/views/board/components/NewCardForm.tsx:293
msgid "New card"
msgstr "Nowa karta"
@@ -1422,8 +1450,8 @@ msgstr "Nowy import"
msgid "New label"
msgstr "Nowa etykieta"
#: src/views/board/components/NewListForm.tsx:112
#: src/views/board/index.tsx:439
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
msgid "New list"
msgstr "Nowa lista"
@@ -1460,6 +1488,10 @@ msgstr "Następne kroki"
msgid "No {0}"
msgstr "Brak {0}"
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "Obecnie brak dostępnych metod uwierzytelniania"
#: src/views/boards/components/ImportBoardsForm.tsx:242
msgid "No boards found"
msgstr "Nie znaleziono tablic"
@@ -1468,7 +1500,11 @@ msgstr "Nie znaleziono tablic"
msgid "No credit card required"
msgstr "Nie wymagana karta kredytowa"
#: src/views/board/index.tsx:464
#: 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
msgid "No lists"
msgstr "Brak list"
@@ -1488,7 +1524,7 @@ msgstr "Wprowadzenie"
msgid "Once you delete your account, there is no going back. This action cannot be undone."
msgstr "Po usunięciu konta nie będzie możliwości jego przywrócenia. Tej operacji nie można cofnąć."
#: src/views/settings/WorkspaceSettings.tsx:99
#: src/views/settings/WorkspaceSettings.tsx:100
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ąć."
@@ -1500,7 +1536,7 @@ msgstr "Otwartoźródłowe"
msgid "Open Source Friends"
msgstr "Przyjaciele Open Source"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "lub"
@@ -1582,15 +1618,15 @@ msgstr "Platforma"
msgid "Please confirm your new password"
msgstr "Proszę potwierdzić nowe hasło"
#: src/components/AuthForm.tsx:391
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Proszę wprowadzić prawidłowy adres e-mail"
#: src/components/AuthForm.tsx:379
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Proszę wprowadzić poprawne imię"
#: src/components/AuthForm.tsx:404
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Proszę wprowadzić poprawne hasło"
@@ -1601,15 +1637,16 @@ 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:176
#: src/views/board/components/NewListForm.tsx:78
#: src/views/board/components/NewCardForm.tsx:177
#: 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:72
#: src/views/board/components/VisibilityButton.tsx:53
#: src/views/board/index.tsx:149
#: src/views/board/index.tsx:205
#: 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/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
#: src/views/card/components/ChecklistItemRow.tsx:88
#: src/views/card/components/ChecklistNameInput.tsx:46
@@ -1623,10 +1660,10 @@ msgstr "Proszę wybrać plik do przesłania."
#: 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:175
#: src/views/card/index.tsx:177
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:234
#: src/views/members/components/InviteMemberForm.tsx:241
#: src/views/settings/components/Avatar.tsx:78
#: src/views/settings/components/Avatar.tsx:219
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
@@ -1659,7 +1696,7 @@ msgstr "Cennik"
#: src/views/pricing/components/Pricing.tsx:56
#: src/views/pricing/components/PricingTiers.tsx:53
#: src/views/settings/components/UpgradeToProConfirmation.tsx:91
#: src/views/settings/components/UpgradeToProConfirmation.tsx:93
msgid "Priority email support"
msgstr "Priorytetowe wsparcie e-mail"
@@ -1667,7 +1704,7 @@ msgstr "Priorytetowe wsparcie e-mail"
msgid "Privacy policy"
msgstr "Polityka prywatności"
#: src/views/board/components/VisibilityButton.tsx:73
#: src/views/board/components/VisibilityButton.tsx:74
msgid "Private"
msgstr "Prywatne"
@@ -1675,7 +1712,7 @@ msgstr "Prywatne"
msgid "Pro Plan"
msgstr "Plan Pro"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
msgid "Pro Plan ∞"
msgstr "Plan Pro ∞"
@@ -1687,7 +1724,7 @@ msgstr "Zaktualizowano zdjęcie profilowe"
msgid "Profile picture"
msgstr "Zdjęcie profilowe"
#: src/views/board/components/VisibilityButton.tsx:68
#: src/views/board/components/VisibilityButton.tsx:69
msgid "Public"
msgstr "Publiczne"
@@ -1840,7 +1877,7 @@ msgid "Senior"
msgstr "Starszy"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:85
#: src/components/SideNavigation.tsx:106
msgid "Settings"
msgstr "Ustawienia"
@@ -1889,7 +1926,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:420
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Zarejestruj się za pomocą "
@@ -1914,6 +1951,11 @@ msgstr "Rozwój oprogramowania"
msgid "Star on Github"
msgstr "Oznacz gwiazdką na Githubie"
#: src/views/members/components/InviteMemberForm.tsx:359
#: src/views/settings/components/UpgradeToProConfirmation.tsx:111
msgid "Start 14 day free trial"
msgstr "Rozpocznij 14-dniowy bezpłatny okres próbny"
#: src/views/members/components/InviteMemberForm.tsx:122
msgid "Subscription Required"
msgstr "Wymagana subskrypcja"
@@ -1923,7 +1965,7 @@ msgstr "Wymagana subskrypcja"
msgid "Success"
msgstr "Sukces"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:55
#: src/views/settings/components/UpgradeToProConfirmation.tsx:57
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:"
@@ -1940,7 +1982,7 @@ msgstr "Wspieraj rozwój projektu"
msgid "System"
msgstr "System"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
#: src/views/members/index.tsx:207
msgid "Team Plan"
msgstr "Plan zespołowy"
@@ -1951,8 +1993,8 @@ msgstr "Plan zespołowy"
msgid "Teams"
msgstr "Zespoły"
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:396
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
msgid "Template"
msgstr "Szablon"
@@ -1972,7 +2014,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:75
#: src/components/SideNavigation.tsx:96
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Szablony"
@@ -2002,7 +2044,7 @@ msgid "The open source <0/> alternative to Trello"
msgstr "Otwarta alternatywa <0/> dla Trello"
#. placeholder {0}: isPublic ? "public" : "private"
#: src/views/board/components/VisibilityButton.tsx:46
#: src/views/board/components/VisibilityButton.tsx:47
msgid "The visibility of your board has been set to {0}."
msgstr "Widoczność Twojej tablicy została ustawiona na {0}."
@@ -2026,11 +2068,11 @@ 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:154
#: src/views/public/board/index.tsx:156
msgid "This board is private or does not exist"
msgstr "Ta tablica jest prywatna lub nie istnieje"
#: src/views/board/components/UpdateBoardSlugForm.tsx:135
#: src/views/board/components/UpdateBoardSlugForm.tsx:136
msgid "This board URL has already been taken"
msgstr "Ten adres URL tablicy jest już zajęty"
@@ -2100,7 +2142,7 @@ 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:173
#: src/views/board/components/NewCardForm.tsx:174
msgid "Unable to create card"
msgstr "Nie można utworzyć karty"
@@ -2108,7 +2150,7 @@ msgstr "Nie można utworzyć karty"
msgid "Unable to create checklist"
msgstr "Nie można utworzyć listy kontrolnej"
#: src/views/board/components/NewListForm.tsx:77
#: src/views/board/components/NewListForm.tsx:78
msgid "Unable to create list"
msgstr "Nie można utworzyć listy"
@@ -2121,6 +2163,10 @@ msgstr "Nie można utworzyć szablonu"
msgid "Unable to create workspace"
msgstr "Nie można utworzyć przestrzeni roboczej"
#: src/views/card/components/AttachmentThumbnails.tsx:72
msgid "Unable to delete attachment"
msgstr "Nie można usunąć załącznika"
#: src/views/card/components/DeleteCardConfirmation.tsx:51
msgid "Unable to delete card"
msgstr "Nie można usunąć karty"
@@ -2145,16 +2191,16 @@ msgstr "Nie można usunąć członka"
msgid "Unable to send feedback"
msgstr "Nie można wysłać opinii"
#: src/views/board/components/UpdateBoardSlugForm.tsx:71
#: src/views/board/components/UpdateBoardSlugForm.tsx:72
msgid "Unable to update board URL"
msgstr "Nie można zaktualizować adresu URL tablicy"
#: src/views/board/components/VisibilityButton.tsx:52
#: src/views/board/components/VisibilityButton.tsx:53
msgid "Unable to update board visibility"
msgstr "Nie można zaktualizować widoczności tablicy"
#: src/views/board/index.tsx:204
#: src/views/card/index.tsx:174
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
msgid "Unable to update card"
msgstr "Nie można zaktualizować karty"
@@ -2174,7 +2220,7 @@ msgstr "Nie można zaktualizować komentarza"
msgid "Unable to update labels"
msgstr "Nie można zaktualizować etykiet"
#: src/views/board/index.tsx:148
#: src/views/board/index.tsx:150
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Nie można zaktualizować listy"
@@ -2222,7 +2268,7 @@ msgid "Unlimited lists"
msgstr "Nieograniczona liczba list"
#: src/components/NewWorkspaceForm.tsx:332
#: src/views/settings/components/UpgradeToProConfirmation.tsx:64
#: src/views/settings/components/UpgradeToProConfirmation.tsx:66
msgid "Unlimited members"
msgstr "Nieograniczona liczba członków"
@@ -2230,7 +2276,12 @@ msgstr "Nieograniczona liczba członków"
msgid "Unlimited workspaces"
msgstr "Nieograniczona liczba przestrzeni roboczych"
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
#: 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
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
@@ -2259,12 +2310,9 @@ msgstr "zaktualizowano tytuł"
msgid "updated the title to <0>{0}</0>"
msgstr "zaktualizowano tytuł na <0>{0}</0>"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:109
msgid "Upgrade"
msgstr "Ulepsz"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
#: src/views/settings/WorkspaceSettings.tsx:89
#: src/components/SideNavigation.tsx:202
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
msgstr "Ulepsz do planu Pro"
@@ -2272,9 +2320,10 @@ msgstr "Ulepsz do planu Pro"
msgid "Upgrade to Pro ($29/month)"
msgstr "Ulepsz do planu Pro (29 USD/miesiąc)"
#: src/views/members/components/InviteMemberForm.tsx:357
msgid "Upgrade to Team Plan"
msgstr "Ulepsz do planu zespołowego"
#: src/views/card/components/AttachmentUpload.tsx:32
#: src/views/card/components/AttachmentUpload.tsx:78
msgid "Upload failed"
msgstr "Przesyłanie nie powiodło się"
#: src/views/boards/components/TemplateBoards.tsx:38
#: src/views/boards/components/TemplateBoards.tsx:55
@@ -2325,7 +2374,7 @@ msgstr "Zobacz i zarządzaj swoimi płatnościami i subskrypcją."
msgid "View docs"
msgstr "Zobacz dokumentację"
#: src/views/public/board/index.tsx:128
#: src/views/public/board/index.tsx:129
msgid "View only"
msgstr "Tylko podgląd"
@@ -2333,11 +2382,11 @@ msgstr "Tylko podgląd"
msgid "View our roadmap."
msgstr "Zobacz nasz plan ulepszeń."
#: src/views/public/board/index.tsx:157
#: src/views/public/board/index.tsx:159
msgid "View workspace"
msgstr "Zobacz obszar roboczy"
#: src/views/board/components/VisibilityButton.tsx:91
#: src/views/board/components/VisibilityButton.tsx:92
msgid "Visibility"
msgstr "Widoczność"
@@ -2345,6 +2394,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. "
@@ -2374,10 +2427,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:361
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:100
#: src/views/public/board/index.tsx:101
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Obszar roboczy"
@@ -2483,7 +2536,11 @@ msgstr "Zostałeś pomyślnie zalogowany."
msgid "You have been signed up successfully."
msgstr "Zostałeś pomyślnie zarejestrowany."
#: src/views/members/components/InviteMemberForm.tsx:319
#: 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!"
@@ -2507,6 +2564,10 @@ msgstr "Twoje tablice zostały zaimportowane."
msgid "Your display name has been updated."
msgstr "Twoja nazwa wyświetlana została zaktualizowana."
#: src/views/card/components/AttachmentUpload.tsx:26
msgid "Your file has been uploaded successfully."
msgstr "Twój plik został pomyślnie przesłany."
#: src/views/settings/components/ChangePasswordConfirmation.tsx:70
msgid "Your password has been changed."
msgstr "Twoje hasło zostało zmienione."
@@ -2523,6 +2584,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

View File

@@ -31,17 +31,17 @@ msgstr "{0}"
#. placeholder {1}: board?.name ?? t`Board`
#. placeholder {1}: workspace.name ?? t`Workspace`
#: src/views/boards/index.tsx:22
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "{0} | {1}"
msgstr "{0} | {1}"
#. placeholder {0}: labelPublicIds.length
#: src/views/board/components/NewCardForm.tsx:442
#: src/views/board/components/NewCardForm.tsx:443
msgid "{0} labels"
msgstr "{0} метки"
#. placeholder {0}: isTemplate ? "Template" : "Board"
#: src/views/board/index.tsx:387
#: src/views/board/index.tsx:389
msgid "{0} not found"
msgstr "{0} не найден"
@@ -63,6 +63,10 @@ msgstr "$8/месяц"
msgid "1 user"
msgstr "1 пользователь"
#: src/views/pricing/components/PricingTiers.tsx:136
msgid "14 day free trial"
msgstr "14-дневная бесплатная пробная версия"
#: src/views/home/index.tsx:71
msgid "A powerful, flexible kanban app that helps you organise work, track progress, and deliver results—all in one place."
msgstr "Мощное и гибкое приложение для канбана, которое помогает организовать работу, отслеживать прогресс и достигать результатов — всё в одном месте."
@@ -75,8 +79,8 @@ msgstr "Аккаунт"
msgid "Account deleted"
msgstr "Аккаунт удалён"
#: src/views/card/index.tsx:291
#: src/views/public/board/CardModal.tsx:143
#: src/views/card/index.tsx:343
#: src/views/public/board/CardModal.tsx:157
msgid "Activity"
msgstr "Активность"
@@ -105,7 +109,7 @@ msgstr "Добавить элемент..."
msgid "Add checklist"
msgstr "Добавить чек-лист"
#: src/components/Editor.tsx:442
#: src/components/Editor.tsx:451
msgid "Add description... (type '/' to open commands or '@' to mention)"
msgstr "Добавить описание... (введите '/' для открытия команд или '@' для упоминания)"
@@ -119,7 +123,7 @@ msgid "Add label"
msgstr "Добавить метку"
#: src/views/card/components/MemberSelector.tsx:130
#: src/views/members/components/InviteMemberForm.tsx:250
#: src/views/members/components/InviteMemberForm.tsx:257
msgid "Add member"
msgstr "Добавить участника"
@@ -154,7 +158,7 @@ msgstr "добавлен элемент контрольного списка <0
msgid "added label <0>{0}</0>"
msgstr "добавлена метка <0>{0}</0>"
#: src/views/members/components/InviteMemberForm.tsx:320
#: src/views/members/components/InviteMemberForm.tsx:327
msgid "Adding a new member will cost an additional {price} ({billingType}) per seat."
msgstr "Добавление нового участника будет стоить дополнительно {price} ({billingType}) за место."
@@ -183,7 +187,7 @@ msgstr "Произошла ошибка при отключении вашего
msgid "An unexpected error occurred. Please try again later."
msgstr "Произошла непредвиденная ошибка. Пожалуйста, попробуйте позже."
#: src/views/members/components/InviteMemberForm.tsx:304
#: src/views/members/components/InviteMemberForm.tsx:311
msgid "Anyone with this link can join your workspace"
msgstr "Любой, у кого есть эта ссылка, может присоединиться к вашему рабочему пространству"
@@ -266,6 +270,10 @@ msgstr "назначено <0>{0}</0> к карточке"
msgid "Assignees"
msgstr "Исполнители"
#: src/views/card/components/AttachmentUpload.tsx:25
msgid "Attachment uploaded"
msgstr "Вложение загружено"
#: src/views/boards/components/TemplateBoards.tsx:47
msgid "Awaiting Customer"
msgstr "Ожидание клиента"
@@ -300,14 +308,14 @@ msgstr "Портал выставления счетов"
msgid "Blog Post"
msgstr "Пост в блоге"
#: src/views/board/index.tsx:361
#: src/views/card/index.tsx:216
#: src/views/public/board/index.tsx:100
#: src/views/board/index.tsx:363
#: src/views/card/index.tsx:229
#: src/views/public/board/index.tsx:101
msgid "Board"
msgstr "Доска"
#: src/components/NewWorkspaceForm.tsx:349
#: src/views/settings/components/UpgradeToProConfirmation.tsx:81
#: src/views/settings/components/UpgradeToProConfirmation.tsx:83
msgid "Board analytics"
msgstr "Аналитика доски"
@@ -319,7 +327,7 @@ msgstr "Название доски не может превышать 100 си
msgid "Board name is required"
msgstr "Название доски обязательно"
#: src/views/public/board/index.tsx:151
#: src/views/public/board/index.tsx:153
msgid "Board not found"
msgstr "Доска не найдена"
@@ -327,19 +335,19 @@ msgstr "Доска не найдена"
msgid "Board sharing & invites"
msgstr "Общий доступ к доске и приглашения"
#: src/views/board/components/UpdateBoardSlugForm.tsx:45
#: src/views/board/components/UpdateBoardSlugForm.tsx:46
msgid "Board URL can only contain letters, numbers, and hyphens"
msgstr "URL доски может содержать только буквы, цифры и дефисы"
#: src/views/board/components/UpdateBoardSlugForm.tsx:43
#: src/views/board/components/UpdateBoardSlugForm.tsx:44
msgid "Board URL cannot exceed 60 characters"
msgstr "URL доски не может превышать 60 символов"
#: src/views/public/board/index.tsx:67
#: src/views/public/board/index.tsx:68
msgid "Board URL copied to clipboard"
msgstr "URL доски скопирован в буфер обмена"
#: src/views/board/components/UpdateBoardSlugForm.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:42
msgid "Board URL must be at least 3 characters long"
msgstr "URL доски должен содержать не менее 3 символов"
@@ -348,11 +356,11 @@ msgstr "URL доски должен содержать не менее 3 сим
msgid "Board visibility"
msgstr "Видимость доски"
#: src/views/board/components/VisibilityButton.tsx:45
#: src/views/board/components/VisibilityButton.tsx:46
msgid "Board visibility updated"
msgstr "Видимость доски обновлена"
#: src/components/SideNavigation.tsx:70
#: src/components/SideNavigation.tsx:91
msgid "Boards"
msgstr "Доски"
@@ -379,19 +387,20 @@ msgstr "Отчет об ошибке"
#: src/views/settings/components/ChangePasswordConfirmation.tsx:168
#: src/views/settings/components/DeleteAccountConfirmation.tsx:88
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:98
#: src/views/settings/components/UpgradeToProConfirmation.tsx:104
#: src/views/settings/components/UpgradeToProConfirmation.tsx:106
msgid "Cancel"
msgstr "Отмена"
#: src/views/card/index.tsx:216
#: src/views/card/index.tsx:229
msgid "Card"
msgstr "Карточка"
#: src/views/card/index.tsx:262
#: src/views/card/index.tsx:263
#: src/views/card/index.tsx:299
msgid "Card not found"
msgstr "Карточка не найдена"
#: src/views/board/components/NewCardForm.tsx:309
#: src/views/board/components/NewCardForm.tsx:310
msgid "Card title"
msgstr "Название карточки"
@@ -424,7 +433,7 @@ msgstr "Название контрольного списка"
msgid "Checklists"
msgstr "Контрольные списки"
#: src/views/board/components/Filters.tsx:155
#: src/views/board/components/Filters.tsx:180
msgid "Clear filters"
msgstr "Очистить фильтры"
@@ -447,7 +456,7 @@ msgstr "Сотрудничайте с вашей командой без пре
#: src/components/NewWorkspaceForm.tsx:352
#: src/views/home/components/Features.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:84
#: src/views/settings/components/UpgradeToProConfirmation.tsx:86
msgid "Coming soon"
msgstr "Скоро будет"
@@ -485,6 +494,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 "Подтвердите ваш новый пароль"
@@ -515,12 +528,12 @@ msgstr "Свяжитесь с нами"
msgid "Content Creation"
msgstr "Создание контента"
#: src/components/AuthForm.tsx:420
#: 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}"
@@ -538,8 +551,8 @@ msgid "Create {0}"
msgstr "Создать {0}"
#: src/components/LabelForm.tsx:213
#: src/views/board/components/NewCardForm.tsx:468
#: src/views/board/components/NewListForm.tsx:140
#: src/views/board/components/NewCardForm.tsx:469
#: src/views/board/components/NewListForm.tsx:141
msgid "Create another"
msgstr "Создать ещё одну"
@@ -547,7 +560,7 @@ msgstr "Создать ещё одну"
msgid "Create API key"
msgstr "Создать API-ключ"
#: src/views/board/components/NewCardForm.tsx:478
#: src/views/board/components/NewCardForm.tsx:479
msgid "Create card"
msgstr "Создать карточку"
@@ -555,7 +568,7 @@ msgstr "Создать карточку"
msgid "Create checklist"
msgstr "Создать чек-лист"
#: src/views/members/components/InviteMemberForm.tsx:342
#: src/views/members/components/InviteMemberForm.tsx:349
msgid "Create invite link"
msgstr "Создать ссылку-приглашение"
@@ -563,7 +576,7 @@ msgstr "Создать ссылку-приглашение"
msgid "Create label"
msgstr "Создать метку"
#: src/views/board/components/NewListForm.tsx:152
#: src/views/board/components/NewListForm.tsx:153
msgid "Create list"
msgstr "Создать список"
@@ -576,12 +589,12 @@ msgstr "Создать новый {0}"
msgid "Create new key"
msgstr "Создать новый ключ"
#: src/views/board/components/NewCardForm.tsx:404
#: src/views/board/components/NewCardForm.tsx:405
#: src/views/card/components/LabelSelector.tsx:97
msgid "Create new label"
msgstr "Создать новую метку"
#: src/views/board/index.tsx:475
#: src/views/board/index.tsx:478
msgid "Create new list"
msgstr "Создать новый список"
@@ -590,7 +603,7 @@ msgid "Create template"
msgstr "Создать шаблон"
#: src/components/NewWorkspaceForm.tsx:387
#: src/components/WorkspaceMenu.tsx:148
#: src/components/WorkspaceMenu.tsx:149
msgid "Create workspace"
msgstr "Создать рабочее пространство"
@@ -632,7 +645,7 @@ msgid "Custom workspace link"
msgstr "Пользовательская ссылка рабочего пространства"
#: src/components/NewWorkspaceForm.tsx:342
#: src/views/settings/components/UpgradeToProConfirmation.tsx:74
#: src/views/settings/components/UpgradeToProConfirmation.tsx:76
msgid "Custom workspace URL"
msgstr "Пользовательский URL рабочего пространства"
@@ -644,7 +657,7 @@ msgstr "Служба поддержки"
msgid "Dark"
msgstr "Тёмный"
#: src/views/members/components/InviteMemberForm.tsx:341
#: src/views/members/components/InviteMemberForm.tsx:348
msgid "Deactivate invite link"
msgstr "Деактивировать ссылку-приглашение"
@@ -684,8 +697,8 @@ msgid "Delete template"
msgstr "Удалить шаблон"
#: src/views/settings/components/DeleteWorkspaceConfirmation.tsx:106
#: src/views/settings/WorkspaceSettings.tsx:96
#: src/views/settings/WorkspaceSettings.tsx:107
#: src/views/settings/WorkspaceSettings.tsx:97
#: src/views/settings/WorkspaceSettings.tsx:108
msgid "Delete workspace"
msgstr "Удалить рабочее пространство"
@@ -739,6 +752,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 "документы"
@@ -765,8 +782,12 @@ msgstr "Нет аккаунта? <0><1>Зарегистрируйтесь</1></0
msgid "Done"
msgstr "Готово"
#: src/views/card/components/AttachmentThumbnails.tsx:141
msgid "Download failed"
msgstr "Не удалось загрузить"
#: src/views/board/components/BoardDropdown.tsx:41
#: src/views/board/components/UpdateBoardSlugForm.tsx:115
#: src/views/board/components/UpdateBoardSlugForm.tsx:116
msgid "Edit board URL"
msgstr "Изменить URL доски"
@@ -778,7 +799,7 @@ msgstr "Редактировать комментарий"
msgid "Edit label"
msgstr "Редактировать метку"
#: src/views/board/components/UpdateBoardSlugForm.tsx:161
#: src/views/board/components/UpdateBoardSlugForm.tsx:162
msgid "Edit workspace URL"
msgstr "Редактировать URL рабочей области"
@@ -786,11 +807,11 @@ msgstr "Редактировать URL рабочей области"
msgid "Editing"
msgstr "Редактирование"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "email"
msgstr "email"
#: src/views/members/components/InviteMemberForm.tsx:265
#: src/views/members/components/InviteMemberForm.tsx:272
msgid "Email"
msgstr "Email"
@@ -806,11 +827,11 @@ msgstr "Введите ваш текущий пароль"
msgid "Enter your current password and choose a new secure password."
msgstr "Введите ваш текущий пароль и выберите новый безопасный пароль."
#: src/components/AuthForm.tsx:387
#: src/components/AuthForm.tsx:402
msgid "Enter your email address"
msgstr "Введите ваш адрес электронной почты"
#: src/components/AuthForm.tsx:375
#: src/components/AuthForm.tsx:388
msgid "Enter your name"
msgstr "Введите ваше имя"
@@ -818,13 +839,13 @@ msgstr "Введите ваше имя"
msgid "Enter your new password"
msgstr "Введите ваш новый пароль"
#: src/components/AuthForm.tsx:400
#: src/components/AuthForm.tsx:416
msgid "Enter your password"
msgstr "Введите ваш пароль"
#: src/views/boards/components/NewBoardForm.tsx:77
#: src/views/boards/components/NewBoardForm.tsx:92
#: src/views/members/components/InviteMemberForm.tsx:208
#: src/views/members/components/InviteMemberForm.tsx:215
msgid "Error"
msgstr "Ошибка"
@@ -881,7 +902,7 @@ msgstr "Ошибка при обновлении имени рабочей об
msgid "Error updating workspace URL"
msgstr "Ошибка при обновлении URL рабочей области"
#: src/views/members/components/InviteMemberForm.tsx:233
#: src/views/members/components/InviteMemberForm.tsx:240
#: src/views/settings/components/UpgradeToProConfirmation.tsx:41
msgid "Error upgrading subscription"
msgstr "Ошибка при обновлении подписки"
@@ -913,7 +934,7 @@ msgstr "Выполнение"
msgid "Failed to accept invitation. Please try again later, or contact customer support."
msgstr "Не удалось принять приглашение. Пожалуйста, попробуйте позже или свяжитесь со службой поддержки."
#: src/views/members/components/InviteMemberForm.tsx:209
#: src/views/members/components/InviteMemberForm.tsx:216
msgid "Failed to copy invite link"
msgstr "Не удалось скопировать ссылку приглашения"
@@ -927,6 +948,11 @@ msgstr "Не удалось создать доску"
msgid "Failed to login with {0}. Please try again."
msgstr "Не удалось войти с {0}. Пожалуйста, попробуйте снова."
#: src/views/card/components/AttachmentUpload.tsx:33
#: src/views/card/components/AttachmentUpload.tsx:79
msgid "Failed to upload attachment. Please try again."
msgstr "Не удалось загрузить вложение. Пожалуйста, попробуйте еще раз."
#: src/views/home/components/Header.tsx:41
msgid "FAQ"
msgstr "FAQ"
@@ -970,7 +996,7 @@ msgstr "Обратная связь"
msgid "Feedback sent"
msgstr "Обратная связь отправлена"
#: src/views/board/components/Filters.tsx:149
#: src/views/board/components/Filters.tsx:174
msgid "Filter"
msgstr "Фильтр"
@@ -983,13 +1009,11 @@ msgid "For long-term sustainability, we recognise all good open source projects
msgstr "Для долгосрочной устойчивости мы признаем, что всем хорошим проектам с открытым исходным кодом необходим источник дохода. Наш доход поступает от платного облачного предложения для рабочих пространств с несколькими пользователями и для пользовательских идентификаторов рабочих пространств. Нет обязательства использовать его, и вы можете самостоятельно разместить программное обеспечение на своей инфраструктуре бесплатно."
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/Pricing.tsx:32
#: src/views/pricing/components/PricingTiers.tsx:30
#: src/views/pricing/components/PricingTiers.tsx:30
msgid "Free"
msgstr "Бесплатно"
#: src/views/members/components/InviteMemberForm.tsx:326
#: src/views/members/components/InviteMemberForm.tsx:333
#: src/views/members/index.tsx:208
msgid "Free Plan"
msgstr "Бесплатный план"
@@ -1026,7 +1050,7 @@ msgstr "Начать"
msgid "Get started by creating a new {0}"
msgstr "Начните с создания нового {0}"
#: src/views/board/index.tsx:467
#: src/views/board/index.tsx:470
msgid "Get started by creating a new list"
msgstr "Начните с создания нового списка"
@@ -1181,11 +1205,11 @@ msgstr "Недействительное приглашение"
msgid "Invite"
msgstr "Пригласить"
#: src/views/members/components/InviteMemberForm.tsx:202
#: src/views/members/components/InviteMemberForm.tsx:209
msgid "Invite link copied"
msgstr "Ссылка приглашения скопирована"
#: src/views/members/components/InviteMemberForm.tsx:203
#: src/views/members/components/InviteMemberForm.tsx:210
msgid "Invite link copied to clipboard"
msgstr "Ссылка приглашения скопирована в буфер обмена"
@@ -1198,11 +1222,11 @@ msgid "Invite links require a Team or Pro subscription. Please upgrade your work
msgstr "Ссылки для приглашений требуют подписки Team или Pro. Пожалуйста, обновите ваше рабочее пространство."
#: src/views/card/components/MemberSelector.tsx:111
#: src/views/members/components/InviteMemberForm.tsx:365
#: src/views/members/components/InviteMemberForm.tsx:367
msgid "Invite member"
msgstr "Пригласить участника"
#: src/views/members/components/InviteMemberForm.tsx:329
#: src/views/members/components/InviteMemberForm.tsx:336
msgid "Inviting members requires a Team Plan. You'll be redirected to upgrade your workspace."
msgstr "Для приглашения участников необходим командный план. Вы будете перенаправлены для обновления вашего рабочего пространства."
@@ -1240,9 +1264,9 @@ msgstr "Канбан переосмыслен"
msgid "Keep in mind that this action is irreversible."
msgstr "Имейте в виду, что это действие необратимо."
#: src/views/board/components/Filters.tsx:101
#: src/views/board/components/NewCardForm.tsx:408
#: src/views/card/index.tsx:117
#: src/views/board/components/Filters.tsx:115
#: src/views/board/components/NewCardForm.tsx:409
#: src/views/card/index.tsx:119
msgid "Labels"
msgstr "Метки"
@@ -1259,7 +1283,7 @@ msgid "Language"
msgstr "Язык"
#: src/components/NewWorkspaceForm.tsx:335
#: src/views/settings/components/UpgradeToProConfirmation.tsx:67
#: src/views/settings/components/UpgradeToProConfirmation.tsx:69
msgid "Launch offer"
msgstr "Стартовое предложение"
@@ -1292,18 +1316,22 @@ msgstr "Лицензия"
msgid "Light"
msgstr "Светлый"
#: src/views/public/board/index.tsx:65
#: src/views/public/board/index.tsx:66
msgid "Link copied"
msgstr "Ссылка скопирована"
#: src/views/card/index.tsx:109
#: src/views/card/index.tsx:111
msgid "List"
msgstr "Список"
#: src/views/board/components/NewListForm.tsx:128
#: src/views/board/components/NewListForm.tsx:129
msgid "List name"
msgstr "Название списка"
#: src/views/board/components/Filters.tsx:123
msgid "Lists"
msgstr "Списки"
#: src/views/auth/login/index.tsx:33
msgid "Login | kan.bn"
msgstr "Вход | kan.bn"
@@ -1324,7 +1352,7 @@ msgstr "Любим командами по всему миру"
msgid "Low Priority"
msgstr "Низкий приоритет"
#: src/components/AuthForm.tsx:421
#: src/components/AuthForm.tsx:441
msgid "magic link"
msgstr "магическая ссылка"
@@ -1345,10 +1373,10 @@ msgstr "отметил элемент контрольного списка <0>{
msgid "Medium Priority"
msgstr "Средний приоритет"
#: src/components/SideNavigation.tsx:80
#: src/views/board/components/Filters.tsx:93
#: src/views/board/components/NewCardForm.tsx:366
#: src/views/card/index.tsx:126
#: 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/views/members/index.tsx:178
msgid "Members"
msgstr "Участники"
@@ -1401,7 +1429,7 @@ msgstr "Новый {0}"
msgid "New API key"
msgstr "Новый API-ключ"
#: src/views/board/components/NewCardForm.tsx:292
#: src/views/board/components/NewCardForm.tsx:293
msgid "New card"
msgstr "Новая карточка"
@@ -1417,8 +1445,8 @@ msgstr "Новый импорт"
msgid "New label"
msgstr "Новая метка"
#: src/views/board/components/NewListForm.tsx:112
#: src/views/board/index.tsx:439
#: src/views/board/components/NewListForm.tsx:113
#: src/views/board/index.tsx:442
msgid "New list"
msgstr "Новый список"
@@ -1455,6 +1483,10 @@ msgstr "Следующие шаги"
msgid "No {0}"
msgstr "Нет {0}"
#: src/components/AuthForm.tsx:369
msgid "No authentication methods are currently available"
msgstr "В настоящее время нет доступных методов аутентификации"
#: src/views/boards/components/ImportBoardsForm.tsx:242
msgid "No boards found"
msgstr "Доски не найдены"
@@ -1463,7 +1495,11 @@ msgstr "Доски не найдены"
msgid "No credit card required"
msgstr "Кредитная карта не требуется"
#: src/views/board/index.tsx:464
#: src/views/card/components/AttachmentThumbnails.tsx:142
msgid "No download URL available for this attachment."
msgstr "URL для загрузки этого вложения недоступен."
#: src/views/board/index.tsx:467
msgid "No lists"
msgstr "Нет списков"
@@ -1483,7 +1519,7 @@ msgstr "Введение"
msgid "Once you delete your account, there is no going back. This action cannot be undone."
msgstr "После удаления аккаунта возврат будет невозможен. Это действие необратимо."
#: src/views/settings/WorkspaceSettings.tsx:99
#: src/views/settings/WorkspaceSettings.tsx:100
msgid "Once you delete your workspace, there is no going back. This action cannot be undone."
msgstr "После удаления рабочего пространства возврат будет невозможен. Это действие необратимо."
@@ -1495,7 +1531,7 @@ msgstr "С открытым исходным кодом"
msgid "Open Source Friends"
msgstr "Друзья с открытым исходным кодом"
#: src/components/AuthForm.tsx:365
#: src/components/AuthForm.tsx:378
msgid "or"
msgstr "или"
@@ -1577,15 +1613,15 @@ msgstr "Платформа"
msgid "Please confirm your new password"
msgstr "Пожалуйста, подтвердите новый пароль"
#: src/components/AuthForm.tsx:391
#: src/components/AuthForm.tsx:406
msgid "Please enter a valid email address"
msgstr "Пожалуйста, введите действительный адрес электронной почты"
#: src/components/AuthForm.tsx:379
#: src/components/AuthForm.tsx:392
msgid "Please enter a valid name"
msgstr "Пожалуйста, введите действительное имя"
#: src/components/AuthForm.tsx:404
#: src/components/AuthForm.tsx:421
msgid "Please enter a valid password"
msgstr "Пожалуйста, введите действительный пароль"
@@ -1596,15 +1632,16 @@ msgstr "Пожалуйста, выберите файл для загрузки.
#: src/components/DeleteLabelConfirmation.tsx:26
#: src/components/FeedbackModal.tsx:41
#: src/components/NewWorkspaceForm.tsx:158
#: src/views/board/components/NewCardForm.tsx:176
#: src/views/board/components/NewListForm.tsx:78
#: src/views/board/components/NewCardForm.tsx:177
#: 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:72
#: src/views/board/components/VisibilityButton.tsx:53
#: src/views/board/index.tsx:149
#: src/views/board/index.tsx:205
#: 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/boards/components/ImportBoardsForm.tsx:205
#: src/views/card/components/AttachmentThumbnails.tsx:73
#: src/views/card/components/ChecklistItemRow.tsx:60
#: src/views/card/components/ChecklistItemRow.tsx:88
#: src/views/card/components/ChecklistNameInput.tsx:46
@@ -1618,10 +1655,10 @@ msgstr "Пожалуйста, выберите файл для загрузки.
#: 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:175
#: src/views/card/index.tsx:177
#: src/views/members/components/DeleteMemberConfirmation.tsx:28
#: src/views/members/components/InviteMemberForm.tsx:104
#: src/views/members/components/InviteMemberForm.tsx:234
#: src/views/members/components/InviteMemberForm.tsx:241
#: src/views/settings/components/Avatar.tsx:78
#: src/views/settings/components/Avatar.tsx:219
#: src/views/settings/components/DeleteAccountConfirmation.tsx:40
@@ -1654,7 +1691,7 @@ msgstr "Цены"
#: src/views/pricing/components/Pricing.tsx:56
#: src/views/pricing/components/PricingTiers.tsx:53
#: src/views/settings/components/UpgradeToProConfirmation.tsx:91
#: src/views/settings/components/UpgradeToProConfirmation.tsx:93
msgid "Priority email support"
msgstr "Приоритетная поддержка по электронной почте"
@@ -1662,7 +1699,7 @@ msgstr "Приоритетная поддержка по электронной
msgid "Privacy policy"
msgstr "Политика конфиденциальности"
#: src/views/board/components/VisibilityButton.tsx:73
#: src/views/board/components/VisibilityButton.tsx:74
msgid "Private"
msgstr "Личное"
@@ -1670,7 +1707,7 @@ msgstr "Личное"
msgid "Pro Plan"
msgstr "Профессиональный план"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
msgid "Pro Plan ∞"
msgstr "Профессиональный план ∞"
@@ -1682,7 +1719,7 @@ msgstr "Изображение профиля обновлено"
msgid "Profile picture"
msgstr "Фотография профиля"
#: src/views/board/components/VisibilityButton.tsx:68
#: src/views/board/components/VisibilityButton.tsx:69
msgid "Public"
msgstr "Публичное"
@@ -1835,7 +1872,7 @@ msgid "Senior"
msgstr "Старший"
#: src/components/SettingsLayout.tsx:82
#: src/components/SideNavigation.tsx:85
#: src/components/SideNavigation.tsx:106
msgid "Settings"
msgstr "Настройки"
@@ -1884,7 +1921,7 @@ msgstr "Регистрация отключена"
msgid "Sign up is currently disabled. Please try again later."
msgstr "Регистрация в настоящее время отключена. Пожалуйста, попробуйте позже."
#: src/components/AuthForm.tsx:420
#: src/components/AuthForm.tsx:440
msgid "Sign up with "
msgstr "Зарегистрироваться с "
@@ -1909,6 +1946,11 @@ msgstr "Разработка программного обеспечения"
msgid "Star on Github"
msgstr "Поставить звезду на Github"
#: src/views/members/components/InviteMemberForm.tsx:359
#: src/views/settings/components/UpgradeToProConfirmation.tsx:111
msgid "Start 14 day free trial"
msgstr "Начать 14-дневную бесплатную пробную версию"
#: src/views/members/components/InviteMemberForm.tsx:122
msgid "Subscription Required"
msgstr "Требуется подписка"
@@ -1918,7 +1960,7 @@ msgstr "Требуется подписка"
msgid "Success"
msgstr "Успех"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:55
#: src/views/settings/components/UpgradeToProConfirmation.tsx:57
msgid "Supercharge your workspace for just $29/month. Here's what you'll get:"
msgstr "Улучшите свою рабочую область всего за $29/месяц. Вот что вы получите:"
@@ -1935,7 +1977,7 @@ msgstr "Поддержите развитие проекта"
msgid "System"
msgstr "Система"
#: src/views/members/components/InviteMemberForm.tsx:315
#: src/views/members/components/InviteMemberForm.tsx:322
#: src/views/members/index.tsx:207
msgid "Team Plan"
msgstr "Командный план"
@@ -1946,8 +1988,8 @@ msgstr "Командный план"
msgid "Teams"
msgstr "Команды"
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:396
#: src/views/board/index.tsx:363
#: src/views/board/index.tsx:398
msgid "Template"
msgstr "Шаблон"
@@ -1967,7 +2009,7 @@ msgstr "Имя шаблона не может превышать 100 симво
msgid "Template name is required"
msgstr "Имя шаблона обязательно"
#: src/components/SideNavigation.tsx:75
#: src/components/SideNavigation.tsx:96
#: src/views/home/components/Features.tsx:115
msgid "Templates"
msgstr "Шаблоны"
@@ -1997,7 +2039,7 @@ msgid "The open source <0/> alternative to Trello"
msgstr "Альтернатива Trello с открытым исходным кодом <0/>"
#. placeholder {0}: isPublic ? "public" : "private"
#: src/views/board/components/VisibilityButton.tsx:46
#: src/views/board/components/VisibilityButton.tsx:47
msgid "The visibility of your board has been set to {0}."
msgstr "Видимость вашей доски установлена на {0}."
@@ -2021,11 +2063,11 @@ 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:154
#: src/views/public/board/index.tsx:156
msgid "This board is private or does not exist"
msgstr "Эта доска является приватной или не существует."
#: src/views/board/components/UpdateBoardSlugForm.tsx:135
#: src/views/board/components/UpdateBoardSlugForm.tsx:136
msgid "This board URL has already been taken"
msgstr "Этот URL доски уже занят."
@@ -2095,7 +2137,7 @@ msgstr "Не удалось добавить элемент контрольно
msgid "Unable to add comment"
msgstr "Не удалось добавить комментарий."
#: src/views/board/components/NewCardForm.tsx:173
#: src/views/board/components/NewCardForm.tsx:174
msgid "Unable to create card"
msgstr "Не удалось создать карточку."
@@ -2103,7 +2145,7 @@ msgstr "Не удалось создать карточку."
msgid "Unable to create checklist"
msgstr "Не удалось создать контрольный список."
#: src/views/board/components/NewListForm.tsx:77
#: src/views/board/components/NewListForm.tsx:78
msgid "Unable to create list"
msgstr "Не удалось создать список."
@@ -2116,6 +2158,10 @@ msgstr "Не удалось создать шаблон"
msgid "Unable to create workspace"
msgstr "Не удалось создать рабочее пространство."
#: src/views/card/components/AttachmentThumbnails.tsx:72
msgid "Unable to delete attachment"
msgstr "Не удалось удалить вложение"
#: src/views/card/components/DeleteCardConfirmation.tsx:51
msgid "Unable to delete card"
msgstr "Не удалось удалить карточку."
@@ -2140,16 +2186,16 @@ msgstr "Не удалось удалить участника"
msgid "Unable to send feedback"
msgstr "Не удалось отправить отзыв"
#: src/views/board/components/UpdateBoardSlugForm.tsx:71
#: src/views/board/components/UpdateBoardSlugForm.tsx:72
msgid "Unable to update board URL"
msgstr "Не удалось обновить URL доски"
#: src/views/board/components/VisibilityButton.tsx:52
#: src/views/board/components/VisibilityButton.tsx:53
msgid "Unable to update board visibility"
msgstr "Не удалось обновить видимость доски"
#: src/views/board/index.tsx:204
#: src/views/card/index.tsx:174
#: src/views/board/index.tsx:206
#: src/views/card/index.tsx:176
msgid "Unable to update card"
msgstr "Не удалось обновить карточку"
@@ -2169,7 +2215,7 @@ msgstr "Не удалось обновить комментарий"
msgid "Unable to update labels"
msgstr "Не удалось обновить метки"
#: src/views/board/index.tsx:148
#: src/views/board/index.tsx:150
#: src/views/card/components/ListSelector.tsx:51
msgid "Unable to update list"
msgstr "Не удалось обновить список"
@@ -2217,7 +2263,7 @@ msgid "Unlimited lists"
msgstr "Неограниченное количество списков"
#: src/components/NewWorkspaceForm.tsx:332
#: src/views/settings/components/UpgradeToProConfirmation.tsx:64
#: src/views/settings/components/UpgradeToProConfirmation.tsx:66
msgid "Unlimited members"
msgstr "Неограниченное количество участников"
@@ -2225,7 +2271,12 @@ msgstr "Неограниченное количество участников"
msgid "Unlimited workspaces"
msgstr "Неограниченное количество рабочих пространств"
#: src/views/board/components/UpdateBoardSlugForm.tsx:174
#: 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
#: src/views/settings/components/UpdateWorkspaceNameForm.tsx:83
@@ -2254,12 +2305,9 @@ msgstr "обновлен заголовок"
msgid "updated the title to <0>{0}</0>"
msgstr "обновлен заголовок на <0>{0}</0>"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:109
msgid "Upgrade"
msgstr "Обновить"
#: src/views/settings/components/UpgradeToProConfirmation.tsx:52
#: src/views/settings/WorkspaceSettings.tsx:89
#: src/components/SideNavigation.tsx:202
#: src/views/settings/components/UpgradeToProConfirmation.tsx:53
#: src/views/settings/WorkspaceSettings.tsx:90
msgid "Upgrade to Pro"
msgstr "Обновить до Pro"
@@ -2267,9 +2315,10 @@ msgstr "Обновить до Pro"
msgid "Upgrade to Pro ($29/month)"
msgstr "Обновить до Pro ($29/месяц)"
#: src/views/members/components/InviteMemberForm.tsx:357
msgid "Upgrade to Team Plan"
msgstr "Обновить до командного плана"
#: src/views/card/components/AttachmentUpload.tsx:32
#: src/views/card/components/AttachmentUpload.tsx:78
msgid "Upload failed"
msgstr "Не удалось загрузить"
#: src/views/boards/components/TemplateBoards.tsx:38
#: src/views/boards/components/TemplateBoards.tsx:55
@@ -2320,7 +2369,7 @@ msgstr "Просмотр и управление вашими счетами и
msgid "View docs"
msgstr "Просмотреть документацию"
#: src/views/public/board/index.tsx:128
#: src/views/public/board/index.tsx:129
msgid "View only"
msgstr "Только просмотр"
@@ -2328,11 +2377,11 @@ msgstr "Только просмотр"
msgid "View our roadmap."
msgstr "Просмотреть нашу дорожную карту."
#: src/views/public/board/index.tsx:157
#: src/views/public/board/index.tsx:159
msgid "View workspace"
msgstr "Просмотреть рабочее пространство"
#: src/views/board/components/VisibilityButton.tsx:91
#: src/views/board/components/VisibilityButton.tsx:92
msgid "Visibility"
msgstr "Видимость"
@@ -2340,6 +2389,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 "Мы только начинаем. "
@@ -2369,10 +2422,10 @@ msgid "Why make an open source Trello?"
msgstr "Зачем создавать Trello с открытым исходным кодом?"
#: src/components/SettingsLayout.tsx:39
#: src/views/board/index.tsx:361
#: src/views/board/index.tsx:363
#: src/views/boards/index.tsx:22
#: src/views/members/index.tsx:173
#: src/views/public/board/index.tsx:100
#: src/views/public/board/index.tsx:101
#: src/views/public/boards/index.tsx:66
msgid "Workspace"
msgstr "Рабочее пространство"
@@ -2478,7 +2531,11 @@ msgstr "Вы успешно вошли в систему."
msgid "You have been signed up successfully."
msgstr "Вы успешно зарегистрировались."
#: src/views/members/components/InviteMemberForm.tsx:319
#: 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 "В вашем профессиональном плане неограниченное количество мест. За новых участников дополнительная плата не взимается!"
@@ -2502,6 +2559,10 @@ msgstr "Ваши доски были импортированы."
msgid "Your display name has been updated."
msgstr "Ваше отображаемое имя было обновлено."
#: src/views/card/components/AttachmentUpload.tsx:26
msgid "Your file has been uploaded successfully."
msgstr "Ваш файл успешно загружен."
#: src/views/settings/components/ChangePasswordConfirmation.tsx:70
msgid "Your password has been changed."
msgstr "Ваш пароль был изменён."
@@ -2518,6 +2579,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

@@ -0,0 +1,47 @@
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "GET") {
return res.status(405).json({ message: "Method not allowed" });
}
const { url, filename } = req.query;
if (!url || typeof url !== "string") {
return res.status(400).json({
message: "url parameter is required",
});
}
try {
const downloadFilename = typeof filename === "string"
? encodeURIComponent(filename)
: "attachment";
const upstream = await fetch(url);
if (!upstream.ok) {
return res.status(upstream.status).json({
message: "Failed to fetch attachment",
});
}
const contentType =
upstream.headers.get("Content-Type") ?? "application/octet-stream";
res.setHeader("Content-Type", contentType);
res.setHeader(
"Content-Disposition",
`attachment; filename="${downloadFilename}"; filename*=UTF-8''${downloadFilename}`,
);
const buffer = await upstream.arrayBuffer();
return res.send(Buffer.from(buffer));
} catch (error) {
console.error("Error downloading attachment:", error);
return res.status(500).json({ message: "Failed to download attachment" });
}
}

View File

@@ -94,6 +94,9 @@ export default async function handler(
quantity: 1,
},
],
subscription_data: {
trial_period_days: 14,
},
success_url: `${env("NEXT_PUBLIC_BASE_URL")}${successUrl}`,
cancel_url: `${env("NEXT_PUBLIC_BASE_URL")}${cancelUrl}`,
client_reference_id: workspacePublicId,

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

@@ -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

@@ -59,7 +59,18 @@ export const ModalProvider: React.FC<Props> = ({ children }) => {
entityLabel,
closeOnClickOutside,
};
setModalStack((prev) => [...prev, newModal]);
setModalStack((prev) => {
const last = prev[prev.length - 1];
if (
last &&
last.contentType === newModal.contentType &&
last.entityId === newModal.entityId &&
last.entityLabel === newModal.entityLabel
) {
return prev; // prevent stacking duplicate modal on top
}
return [...prev, newModal];
});
},
[],
);

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

@@ -1,3 +1,4 @@
import { HiOutlinePaperClip } from "react-icons/hi";
import { HiBars3BottomLeft, HiChatBubbleLeft } from "react-icons/hi2";
import Avatar from "~/components/Avatar";
@@ -13,6 +14,7 @@ const Card = ({
checklists,
description,
comments,
attachments,
}: {
title: string;
labels: { name: string; colourCode: string | null }[];
@@ -33,6 +35,7 @@ const Card = ({
}[];
description: string | null;
comments: { publicId: string }[];
attachments?: { publicId: string }[];
}) => {
const completedItems = checklists.reduce((acc, checklist) => {
return acc + checklist.items.filter((item) => item.completed).length;
@@ -47,6 +50,7 @@ const Card = ({
const hasDescription =
description && description.replace(/<[^>]*>/g, "").trim().length > 0;
const hasAttachments = attachments && attachments.length > 0;
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">
@@ -55,7 +59,8 @@ const Card = ({
members.length ||
checklists.length > 0 ||
hasDescription ||
comments.length > 0 ? (
comments.length > 0 ||
hasAttachments ? (
<div className="mt-2 flex flex-col justify-end">
<div className="space-x-0.5">
{labels.map((label) => (
@@ -77,6 +82,11 @@ const Card = ({
<HiChatBubbleLeft className="h-4 w-4" />
</div>
)}
{hasAttachments && (
<div className="flex items-center gap-1 text-light-700 dark:text-dark-800">
<HiOutlinePaperClip className="h-4 w-4" />
</div>
)}
</div>
<div className="flex items-center justify-end gap-1">
{checklists.length > 0 && (

View File

@@ -12,6 +12,7 @@ interface QueryParams {
boardPublicId: string;
members: string[];
labels: string[];
lists: string[];
}
export function DeleteListConfirmation({

View File

@@ -2,6 +2,7 @@ import { useRouter } from "next/router";
import { t } from "@lingui/core/macro";
import {
HiMiniXMark,
HiOutlineSquare3Stack3D,
HiOutlineTag,
HiOutlineUserCircle,
} from "react-icons/hi2";
@@ -32,15 +33,22 @@ interface Label {
colourCode: string | null;
}
interface List {
publicId: string;
name: string;
}
const Filters = ({
position = "right",
labels,
members,
lists,
isLoading,
}: {
position?: "left" | "right";
labels: Label[];
members: Member[];
lists: List[];
isLoading: boolean;
}) => {
const router = useRouter();
@@ -52,7 +60,7 @@ const Filters = ({
try {
await router.push({
pathname: router.pathname,
query: { ...router.query, members: [], labels: [] },
query: { ...router.query, members: [], labels: [], lists: [] },
});
} catch (error) {
console.error(error);
@@ -85,6 +93,12 @@ const Filters = ({
leftIcon: <LabelIcon colourCode={label.colourCode} />,
}));
const formattedLists = lists.map((list) => ({
key: list.publicId,
value: list.name,
selected: !!router.query.lists?.includes(list.publicId),
}));
const groups = [
...(formattedMembers.length
? [
@@ -102,6 +116,16 @@ const Filters = ({
icon: <HiOutlineTag size={16} />,
items: formattedLabels,
},
...(formattedLists.length
? [
{
key: "lists",
label: t`Lists`,
icon: <HiOutlineSquare3Stack3D size={16} />,
items: formattedLists,
},
]
: []),
];
const handleSelect = async (
@@ -131,6 +155,7 @@ const Filters = ({
const numOfFilters = [
...formatToArray(router.query.members),
...formatToArray(router.query.labels),
...formatToArray(router.query.lists),
].length;
return (

View File

@@ -33,6 +33,7 @@ interface QueryParams {
boardPublicId: string;
members: string[];
labels: string[];
lists: string[];
}
interface NewCardFormProps {

View File

@@ -21,6 +21,7 @@ interface QueryParams {
boardPublicId: string;
members: string[];
labels: string[];
lists: string[];
}
export function NewListForm({

View File

@@ -17,6 +17,7 @@ interface QueryParams {
boardPublicId: string;
members: string[];
labels: string[];
lists: string[];
}
export function UpdateBoardSlugForm({

View File

@@ -11,6 +11,7 @@ interface QueryParams {
boardPublicId: string;
members: string[];
labels: string[];
lists: string[];
}
const VisibilityButton = ({

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]
@@ -80,11 +91,13 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
boardPublicId: string;
members: string[];
labels: string[];
lists: string[];
type: "regular" | "template";
} = {
boardPublicId: boardId ?? "",
members: formatToArray(router.query.members),
labels: formatToArray(router.query.labels),
lists: formatToArray(router.query.lists),
type: isTemplate ? "template" : "regular",
};
@@ -418,26 +431,29 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
members={boardData.workspace.members.filter(
(member) => member.user !== null,
)}
lists={boardData.allLists}
position="left"
isLoading={!boardData}
/>
)}
</>
)}
<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}
@@ -550,6 +566,7 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
card.description ?? null
}
comments={card.comments ?? []}
attachments={card.attachments}
/>
</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

@@ -0,0 +1,462 @@
import Image from "next/image";
import { Dialog, Transition } from "@headlessui/react";
import { t } from "@lingui/core/macro";
import { Fragment, useEffect, useState } from "react";
import {
HiArrowDownTray,
HiChevronLeft,
HiChevronRight,
HiDocumentText,
HiOutlineTrash,
HiXMark,
} from "react-icons/hi2";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
interface Attachment {
publicId: string;
contentType: string;
url: string | null;
originalFilename: string | null;
s3Key: string;
size?: number | null;
}
export function AttachmentThumbnails({
attachments,
cardPublicId,
isReadOnly = false,
}: {
attachments?: Attachment[];
cardPublicId: string;
isReadOnly?: boolean;
}) {
const { showPopup } = usePopup();
const utils = api.useUtils();
const imageAttachments =
attachments?.filter(
(attachment) =>
attachment.contentType.startsWith("image/") && attachment.url,
) ?? [];
const nonImageAttachments =
attachments?.filter(
(attachment) =>
!attachment.contentType.startsWith("image/") && attachment.url,
) ?? [];
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
const deleteAttachment = api.attachment.delete.useMutation({
onMutate: async (args) => {
if (isReadOnly) return;
await utils.card.byId.cancel({ cardPublicId });
const currentState = utils.card.byId.getData({ cardPublicId });
utils.card.byId.setData({ cardPublicId }, (oldCard) => {
if (!oldCard) return oldCard;
const updatedAttachments = oldCard.attachments.filter(
(attachment) => attachment.publicId !== args.attachmentPublicId,
);
return { ...oldCard, attachments: updatedAttachments };
});
return { previousState: currentState };
},
onError: (_error, _args, context) => {
if (isReadOnly) return;
utils.card.byId.setData({ cardPublicId }, context?.previousState);
showPopup({
header: t`Unable to delete attachment`,
message: t`Please try again later, or contact customer support.`,
icon: "error",
});
},
onSuccess: () => {
if (isReadOnly) return;
// Close viewer if the deleted image was being viewed
setSelectedIndex(null);
},
onSettled: async () => {
if (isReadOnly) return;
await utils.card.byId.invalidate({ cardPublicId });
},
});
// Keyboard navigation
useEffect(() => {
if (selectedIndex === null) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
setSelectedIndex(null);
} else if (e.key === "ArrowLeft") {
setSelectedIndex((prev) => {
if (prev === null) return null;
return prev === 0 ? imageAttachments.length - 1 : prev - 1;
});
} else if (e.key === "ArrowRight") {
setSelectedIndex((prev) => {
if (prev === null) return null;
return prev === imageAttachments.length - 1 ? 0 : prev + 1;
});
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedIndex, imageAttachments.length]);
if (imageAttachments.length === 0 && nonImageAttachments.length === 0) {
return null;
}
const openViewer = (index: number) => {
setSelectedIndex(index);
};
const closeViewer = () => {
setSelectedIndex(null);
};
const goToPrevious = () => {
if (selectedIndex === null) return;
const newIndex =
selectedIndex === 0 ? imageAttachments.length - 1 : selectedIndex - 1;
setSelectedIndex(newIndex);
};
const goToNext = () => {
if (selectedIndex === null) return;
const newIndex =
selectedIndex === imageAttachments.length - 1 ? 0 : selectedIndex + 1;
setSelectedIndex(newIndex);
};
const handleDownload = (attachment: Attachment) => {
if (!attachment.url) {
showPopup({
header: t`Download failed`,
message: t`No download URL available for this attachment.`,
icon: "error",
});
return;
}
const downloadUrl = `/api/download/attatchment?url=${encodeURIComponent(attachment.url)}&filename=${encodeURIComponent(attachment.originalFilename ?? "attachment")}`;
const link = document.createElement("a");
link.href = downloadUrl;
link.style.display = "none";
document.body.appendChild(link);
link.click();
};
const selectedAttachment =
selectedIndex !== null ? imageAttachments[selectedIndex] : null;
return (
<>
<div className="mb-3 flex flex-wrap gap-2 pt-1">
{imageAttachments.map((attachment, index) => {
if (!attachment.url) return null;
return (
<AttachmentThumbnail
key={attachment.publicId}
attachment={{
publicId: attachment.publicId,
url: attachment.url,
originalFilename: attachment.originalFilename ?? "",
contentType: attachment.contentType,
}}
onClick={() => openViewer(index)}
isImage={true}
/>
);
})}
</div>
{nonImageAttachments.length > 0 && (
<div className="mb-3 flex flex-col gap-2">
{nonImageAttachments.map((attachment) => {
if (!attachment.url) return null;
return (
<FileListItem
key={attachment.publicId}
attachment={attachment}
onDownload={() => handleDownload(attachment)}
onDelete={
isReadOnly
? undefined
: () => {
deleteAttachment.mutate({
attachmentPublicId: attachment.publicId,
});
}
}
/>
);
})}
</div>
)}
<Transition.Root show={selectedIndex !== null} as={Fragment}>
<Dialog
as="div"
className="relative z-50"
onClose={() => {
// Dialog closing is handled by the background overlay click
}}
static
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div
className="fixed inset-0 bg-light-50 transition-opacity dark:bg-dark-50"
onClick={(e) => {
// Only close if clicking directly on the background, not on buttons
if (e.target === e.currentTarget) {
closeViewer();
}
}}
/>
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
{selectedIndex !== null && selectedAttachment && (
<div
className="fixed left-2 top-2 z-20 flex gap-1"
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
>
{!isReadOnly && (
<button
onMouseDown={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
deleteAttachment.mutate({
attachmentPublicId: selectedAttachment.publicId,
});
}}
className="rounded-full bg-light-50 p-1.5 text-light-1000 transition-colors hover:bg-light-100 focus:outline-none dark:bg-dark-50 dark:text-dark-1000 dark:hover:bg-dark-100"
aria-label="Delete image"
disabled={deleteAttachment.isPending}
>
<HiOutlineTrash className="h-4 w-4" />
</button>
)}
<button
onMouseDown={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleDownload(selectedAttachment);
}}
className="rounded-full bg-light-50 p-1.5 text-light-1000 transition-colors hover:bg-light-100 focus:outline-none dark:bg-dark-50 dark:text-dark-1000 dark:hover:bg-dark-100"
aria-label="Download image"
>
<HiArrowDownTray className="h-4 w-4" />
</button>
</div>
)}
<div className="fixed right-2 top-2 z-20 flex gap-1">
{imageAttachments.length > 1 && selectedIndex !== null && (
<button
onClick={(e) => {
e.stopPropagation();
goToPrevious();
}}
className="rounded-full bg-light-50 p-1.5 text-light-1000 transition-colors hover:bg-light-100 focus:outline-none dark:bg-dark-50 dark:text-dark-1000 dark:hover:bg-dark-100"
aria-label="Previous image"
>
<HiChevronLeft className="h-4 w-4" />
</button>
)}
{imageAttachments.length > 1 && selectedIndex !== null && (
<button
onClick={(e) => {
e.stopPropagation();
goToNext();
}}
className="rounded-full bg-light-50 p-1.5 text-light-1000 transition-colors hover:bg-light-100 focus:outline-none dark:bg-dark-50 dark:text-dark-1000 dark:hover:bg-dark-100"
aria-label="Next image"
>
<HiChevronRight className="h-4 w-4" />
</button>
)}
{selectedIndex !== null && (
<button
onClick={(e) => {
e.stopPropagation();
closeViewer();
}}
className="rounded-full bg-light-50 p-1.5 text-light-1000 transition-colors hover:bg-light-100 focus:outline-none dark:bg-dark-50 dark:text-dark-1000 dark:hover:bg-dark-100"
aria-label="Close"
>
<HiXMark className="h-4 w-4" />
</button>
)}
</div>
<div className="flex min-h-full items-center justify-center p-4">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel
className="relative w-full max-w-7xl"
onClick={(e) => e.stopPropagation()}
>
{selectedAttachment?.url && (
<div className="relative">
<div className="relative mx-auto max-h-[90vh] w-full">
<Image
src={selectedAttachment.url}
alt={
selectedAttachment.originalFilename ?? "Attachment"
}
width={1920}
height={1080}
className="mx-auto max-h-[90vh] w-auto object-contain"
unoptimized
/>
</div>
{imageAttachments.length > 1 && (
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 rounded-full bg-black/50 px-3 py-1.5 text-[10px] text-white">
{selectedIndex !== null && selectedIndex + 1} /{" "}
{imageAttachments.length}
</div>
)}
</div>
)}
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
</>
);
}
function AttachmentThumbnail({
attachment,
onClick,
isImage,
}: {
attachment: {
publicId: string;
url: string;
originalFilename: string;
contentType: string;
};
onClick: () => void;
isImage: boolean;
}) {
return (
<button
onClick={onClick}
className="relative h-16 w-16 overflow-hidden rounded-xl border border-light-300 transition-transform hover:scale-105 dark:border-dark-300"
aria-label={`View ${attachment.originalFilename}`}
>
{isImage ? (
<Image
src={attachment.url}
alt={attachment.originalFilename}
fill
className="object-cover"
sizes="64px"
/>
) : (
<div className="flex h-full w-full items-center justify-center bg-light-100 dark:bg-dark-100">
<HiDocumentText className="h-6 w-6 text-light-700 dark:text-dark-700" />
</div>
)}
</button>
);
}
function formatFileSize(bytes: number | null | undefined): string {
if (!bytes || bytes === 0 || isNaN(bytes)) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i] ?? "B"}`;
}
function FileListItem({
attachment,
onDownload,
onDelete,
}: {
attachment: Attachment;
onDownload: () => void;
onDelete?: () => void;
}) {
return (
<div className="group flex w-full items-center gap-3 rounded-lg border border-light-300 bg-light-50 px-3 py-2 dark:border-dark-200 dark:bg-dark-100">
<div className="flex-shrink-0">
<HiDocumentText className="h-5 w-5 text-light-700 dark:text-dark-700" />
</div>
<div className="min-w-0 flex-1 truncate text-sm text-light-1000 dark:text-dark-1000">
{attachment.originalFilename ?? "File"}
</div>
<div className="flex items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100">
<div className="text-xs text-light-500 dark:text-dark-900">
{attachment.size != null &&
!isNaN(attachment.size) &&
`${formatFileSize(attachment.size)}`}
</div>
<div className="flex items-center gap-1">
<button
onClick={(e) => {
e.stopPropagation();
onDownload();
}}
className="flex-shrink-0 rounded-full bg-light-100 p-1.5 text-light-1000 transition-colors hover:bg-light-200 focus:outline-none dark:bg-dark-100 dark:text-dark-950 dark:hover:bg-dark-300"
aria-label={`Download ${attachment.originalFilename}`}
>
<HiArrowDownTray className="h-4 w-4" />
</button>
{onDelete && (
<button
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
className="flex-shrink-0 rounded-full bg-light-100 p-1.5 text-light-1000 transition-colors hover:bg-light-200 focus:outline-none dark:bg-dark-100 dark:text-dark-950 dark:hover:bg-dark-300"
aria-label={`Delete ${attachment.originalFilename}`}
>
<HiXMark className="h-4 w-4" />
</button>
)}
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,174 @@
import { t } from "@lingui/core/macro";
import { useRef, useState } from "react";
import { HiOutlinePaperClip } from "react-icons/hi";
import { HiCheckBadge } from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
import Button from "~/components/Button";
import { useModal } from "~/providers/modal";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
export function AttachmentUpload({ cardPublicId }: { cardPublicId: string }) {
const { openModal } = useModal();
const { showPopup } = usePopup();
const utils = api.useUtils();
const [uploading, setUploading] = useState(false);
const [isDragging, setIsDragging] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);
const generateUploadUrl = api.attachment.generateUploadUrl.useMutation();
const confirmAttachment = api.attachment.confirm.useMutation({
onSuccess: async () => {
await utils.card.byId.invalidate({ cardPublicId });
showPopup({
header: t`Attachment uploaded`,
message: t`Your file has been uploaded successfully.`,
icon: "success",
});
},
onError: () => {
showPopup({
header: t`Upload failed`,
message: t`Failed to upload attachment. Please try again.`,
icon: "error",
});
},
onSettled: () => {
setUploading(false);
},
});
const uploadFile = async (file: File) => {
setUploading(true);
try {
// Generate presigned URL
const { url, key } = await generateUploadUrl.mutateAsync({
cardPublicId,
filename: file.name,
contentType: file.type,
size: file.size,
});
// Upload file to S3
const uploadResponse = await fetch(url, {
method: "PUT",
body: file,
headers: {
"Content-Type": file.type,
},
});
if (!uploadResponse.ok) {
throw new Error("Upload failed");
}
// Confirm attachment in database
await confirmAttachment.mutateAsync({
cardPublicId,
s3Key: key,
filename: file.name,
originalFilename: file.name,
contentType: file.type,
size: file.size,
});
} catch {
showPopup({
header: t`Upload failed`,
message: t`Failed to upload attachment. Please try again.`,
icon: "error",
});
setUploading(false);
}
};
const handleFileSelect = async (
event: React.ChangeEvent<HTMLInputElement>,
) => {
const file = event.target.files?.[0];
if (!file) return;
// Reset input
event.target.value = "";
await uploadFile(file);
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
if (!uploading) {
setIsDragging(true);
}
};
const handleDragLeave = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
};
const handleDrop = async (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
if (uploading) return;
const files = Array.from(e.dataTransfer.files);
if (files.length === 0) return;
// Upload the first file (or could upload all files)
await uploadFile(files[0] ?? new File([], ""));
};
return (
<div className="mb-6">
<input
ref={inputRef}
type="file"
id="attachment-upload"
className="hidden"
onChange={handleFileSelect}
disabled={uploading}
/>
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={twMerge(
"rounded-lg border-2 border-dashed transition-colors",
isDragging
? "border-light-300 bg-light-100 dark:border-dark-300 dark:bg-dark-100"
: "border-transparent",
)}
>
<div className="flex items-center justify-between p-2">
<Button
type="button"
variant="ghost"
iconLeft={
<HiCheckBadge className="h-4 w-4 text-light-950 dark:text-dark-950" />
}
iconOnly
size="sm"
onClick={() => openModal("ADD_CHECKLIST")}
/>
<Button
type="button"
variant="ghost"
iconLeft={
<HiOutlinePaperClip className="h-4 w-4 text-light-950 dark:text-dark-950" />
}
isLoading={uploading}
disabled={uploading}
iconOnly
size="sm"
onClick={() => inputRef.current?.click()}
/>
</div>
</div>
</div>
);
}

View File

@@ -28,7 +28,7 @@ export default function BoardDropdown() {
},
]}
>
<HiEllipsisHorizontal className="h-6 w-6 text-dark-900" />
<HiEllipsisHorizontal className="h-5 w-5 text-dark-900" />
</Dropdown>
);
}

View File

@@ -20,6 +20,8 @@ import { api } from "~/utils/api";
import { formatMemberDisplayName, getAvatarUrl } from "~/utils/helpers";
import { DeleteLabelConfirmation } from "../../components/DeleteLabelConfirmation";
import ActivityList from "./components/ActivityList";
import { AttachmentThumbnails } from "./components/AttachmentThumbnails";
import { AttachmentUpload } from "./components/AttachmentUpload";
import Checklists from "./components/Checklists";
import { DeleteCardConfirmation } from "./components/DeleteCardConfirmation";
import { DeleteChecklistConfirmation } from "./components/DeleteChecklistConfirmation";
@@ -105,7 +107,7 @@ export function CardRightPanel({ isTemplate }: { isTemplate?: boolean }) {
return (
<div className="h-full w-[360px] border-l-[1px] border-light-300 bg-light-50 p-8 text-light-900 dark:border-dark-300 dark:bg-dark-50 dark:text-dark-900">
<div className="mb-4 flex w-full flex-row">
<div className="mb-4 flex w-full flex-row pt-[18px]">
<p className="my-2 mb-2 w-[100px] text-sm font-medium">{t`List`}</p>
<ListSelector
cardPublicId={cardId ?? ""}
@@ -208,6 +210,17 @@ export default function CardPage({ isTemplate }: { isTemplate?: boolean }) {
}
}, [card, getModalState, clearModalState]);
// Auto-resize title textarea
useEffect(() => {
const titleTextarea = document.getElementById(
"title",
) as HTMLTextAreaElement;
if (titleTextarea) {
titleTextarea.style.height = "auto";
titleTextarea.style.height = `${titleTextarea.scrollHeight}px`;
}
}, [card]);
if (!cardId) return <></>;
return (
@@ -215,47 +228,71 @@ export default function CardPage({ isTemplate }: { isTemplate?: boolean }) {
<PageHead
title={t`${card?.title ?? t`Card`} | ${board?.name ?? t`Board`}`}
/>
<div className="flex h-full flex-1 flex-row overflow-hidden">
<div className="flex h-full flex-1 flex-col overflow-hidden">
{/* Full-width top strip with board link and dropdown */}
<div className="flex w-full items-center justify-between border-b-[1px] border-light-300 bg-light-50 px-8 py-2 dark:border-dark-300 dark:bg-dark-50">
{!card && isLoading && (
<div className="flex space-x-2">
<div className="h-[1.5rem] w-[150px] animate-pulse rounded-[5px] bg-light-300 dark:bg-dark-300" />
</div>
)}
{card && (
<>
<div className="flex items-center gap-1">
<Link
className="whitespace-nowrapleading-[1.5rem] text-sm font-bold text-light-900 dark:text-dark-950"
href={`${isTemplate ? "/templates" : "/boards"}`}
>
{workspace.name}
</Link>
<IoChevronForwardSharp className="h-[10px] w-[10px] text-light-900 dark:text-dark-900" />
<Link
className="whitespace-nowrap text-sm font-bold leading-[1.5rem] text-light-900 dark:text-dark-950"
href={`${isTemplate ? "/templates" : "/boards"}/${board?.publicId}`}
>
{board?.name}
</Link>
</div>
<div className="flex items-center gap-2">
<Dropdown />
</div>
</>
)}
{!card && !isLoading && (
<p className="block p-0 py-0 font-bold leading-[1.5rem] tracking-tight text-light-900 dark:text-dark-900 sm:text-[1rem]">
{t`Card not found`}
</p>
)}
</div>
<div className="scrollbar-thumb-rounded-[4px] scrollbar-track-rounded-[4px] w-full flex-1 overflow-y-auto scrollbar scrollbar-track-light-200 scrollbar-thumb-light-400 hover:scrollbar-thumb-light-400 dark:scrollbar-track-dark-100 dark:scrollbar-thumb-dark-300 dark:hover:scrollbar-thumb-dark-300">
<div className="p-auto mx-auto flex h-full w-full max-w-[800px] flex-col">
<div className="p-6 md:p-8">
<div className="mb-8 flex w-full items-center justify-between md:mt-6">
<div className="mb-8 md:mt-4">
{!card && isLoading && (
<div className="flex space-x-2">
<div className="h-[2.3rem] w-[150px] animate-pulse rounded-[5px] bg-light-300 dark:bg-dark-300" />
<div className="h-[2.3rem] w-[300px] animate-pulse rounded-[5px] bg-light-300 dark:bg-dark-300" />
</div>
)}
{card && (
<>
<Link
className="whitespace-nowrap font-bold leading-[2.3rem] tracking-tight text-light-900 dark:text-dark-900 sm:text-[1.2rem]"
href={`${isTemplate ? "/templates" : "/boards"}/${board?.publicId}`}
>
{board?.name}
</Link>
<IoChevronForwardSharp
size={18}
className="mx-2 text-light-900 dark:text-dark-900"
/>
<form
onSubmit={handleSubmit(onSubmit)}
className="w-full space-y-6"
>
<div>
<input
type="text"
id="title"
{...register("title")}
onBlur={handleSubmit(onSubmit)}
className="block w-full border-0 bg-transparent p-0 py-0 font-bold tracking-tight text-neutral-900 focus:ring-0 dark:text-dark-1000 sm:text-[1.2rem]"
/>
</div>
</form>
<div className="flex">
<Dropdown />
<form
onSubmit={handleSubmit(onSubmit)}
className="w-full space-y-6"
>
<div>
<textarea
id="title"
{...register("title")}
onBlur={handleSubmit(onSubmit)}
rows={1}
className="block w-full resize-none overflow-hidden border-0 bg-transparent p-0 py-0 font-bold leading-relaxed text-neutral-900 focus:ring-0 dark:text-dark-1000 sm:text-[1.2rem]"
onInput={(e) => {
const target = e.target as HTMLTextAreaElement;
target.style.height = "auto";
target.style.height = `${target.scrollHeight}px`;
}}
/>
</div>
</>
</form>
)}
{!card && !isLoading && (
<p className="block p-0 py-0 font-bold leading-[2.3rem] tracking-tight text-neutral-900 dark:text-dark-1000 sm:text-[1.2rem]">
@@ -286,6 +323,21 @@ export default function CardPage({ isTemplate }: { isTemplate?: boolean }) {
activeChecklistForm={activeChecklistForm}
setActiveChecklistForm={setActiveChecklistForm}
/>
{!isTemplate && (
<>
{card?.attachments.length > 0 && (
<div className="mt-6">
<AttachmentThumbnails
attachments={card.attachments}
cardPublicId={cardId ?? ""}
/>
</div>
)}
<div className="mt-6">
<AttachmentUpload cardPublicId={cardId} />
</div>
</>
)}
<div className="border-t-[1px] border-light-300 pt-12 dark:border-dark-300">
<h2 className="text-md pb-4 font-medium text-light-1000 dark:text-dark-1000">
{t`Activity`}

View File

@@ -176,6 +176,13 @@ export function InviteMemberForm({
};
const handleInviteLinkToggle = async () => {
if (
env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
!hasTeamSubscription &&
!hasProSubscription
)
return handleUpgrade();
setIsLoadingInviteLink(true);
if (isShareInviteLinkEnabled && workspace.publicId) {
@@ -342,11 +349,6 @@ export function InviteMemberForm({
: t`Create invite link`
}
isChecked={isShareInviteLinkEnabled}
disabled={
env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
!hasTeamSubscription &&
!hasProSubscription
}
onChange={handleInviteLinkToggle}
/>
<div>
@@ -354,7 +356,7 @@ export function InviteMemberForm({
!hasTeamSubscription &&
!hasProSubscription ? (
<Button type="button" onClick={handleUpgrade}>
{t`Upgrade to Team Plan`}
{t`Start 14 day free trial`}
</Button>
) : (
<Button

View File

@@ -181,7 +181,7 @@ export default function MembersPage() {
<div className="flex items-center gap-3">
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" && (
<>
{!proSubscription && (
{!proSubscription && !teamSubscription && (
<Link
href="/settings/workspace?upgrade=pro"
className="hidden items-center rounded-full border border-emerald-300 bg-emerald-50 px-3 py-1 text-center text-xs text-emerald-400 dark:border-emerald-700 dark:bg-emerald-950 dark:text-emerald-400 lg:flex"

View File

@@ -131,11 +131,11 @@ const Pricing = ({
>
{tier.name}
</h3>
{tier.highlighted && frequency?.value === "annually" ? (
{tier.highlighted && (
<p className="rounded-full bg-light-50 px-2.5 py-1 text-[12px] font-semibold text-dark-500 dark:bg-dark-1000 dark:text-dark-50">
-20%
{t`14 day free trial`}
</p>
) : null}
)}
</div>
<p
className={twMerge(

View File

@@ -9,6 +9,7 @@ import LabelIcon from "~/components/LabelIcon";
import { useModal } from "~/providers/modal";
import { api } from "~/utils/api";
import ActivityList from "~/views/card/components/ActivityList";
import { AttachmentThumbnails } from "~/views/card/components/AttachmentThumbnails";
import Checklists from "~/views/card/components/Checklists";
export function CardModal({
@@ -66,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);
}}
>
@@ -126,11 +128,24 @@ export function CardModal({
<Editor
content={data.description}
readOnly
workspaceMembers={data?.list.board.workspace.members ?? []}
workspaceMembers={
data?.list.board.workspace.members ?? []
}
/>
</div>
</div>
)}
{data?.attachments &&
data.attachments.length > 0 &&
cardPublicId && (
<div className="mb-10 max-w-2xl">
<AttachmentThumbnails
attachments={data.attachments}
cardPublicId={cardPublicId}
isReadOnly
/>
</div>
)}
{data?.checklists && data.checklists.length > 0 && (
<Checklists
checklists={data.checklists}

View File

@@ -44,6 +44,7 @@ export default function PublicBoardView() {
workspaceSlug: workspaceSlug ?? "",
members: formatToArray(router.query.members),
labels: formatToArray(router.query.labels),
lists: formatToArray(router.query.lists),
},
{
enabled: router.isReady && !!boardSlug,
@@ -75,7 +76,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(() => {
@@ -130,6 +132,7 @@ export default function PublicBoardView() {
<Filters
labels={data.labels ?? []}
members={[]}
lists={data.allLists ?? []}
isLoading={isLoading}
/>
</div>
@@ -170,26 +173,36 @@ 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 ?? []}
/>
</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}
/>
</Link>
);
})}
</div>
</div>
))}

View File

@@ -80,7 +80,8 @@ export default function WorkspaceSettings() {
/>
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
!hasActiveSubscription(subscriptions, "pro") && (
!hasActiveSubscription(subscriptions, "pro") &&
!hasActiveSubscription(subscriptions, "team") && (
<div className="my-8">
<Button
onClick={() => openModal("UPGRADE_TO_PRO")}

View File

@@ -48,9 +48,11 @@ export function UpgradeToProConfirmation({
return (
<div className="p-5">
<div className="flex w-full flex-col justify-between pb-4">
<h2 className="text-md pb-4 font-bold text-neutral-900 dark:text-dark-1000">
{t`Upgrade to Pro`}
</h2>
<div className="pb-4">
<h2 className="text-md font-bold text-neutral-900 dark:text-dark-1000">
{t`Upgrade to Pro`}
</h2>
</div>
<p className="mb-4 text-sm font-medium text-light-900 dark:text-dark-900">
{t`Supercharge your workspace for just $29/month. Here's what you'll get:`}
</p>
@@ -106,7 +108,7 @@ export function UpgradeToProConfirmation({
<Button
onClick={handleUpgrade}
iconRight={<HiBolt />}
>{t`Upgrade`}</Button>
>{t`Start 14 day free trial`}</Button>
</div>
</div>
);

View File

@@ -35,8 +35,11 @@ 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}
# 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}
@@ -46,6 +49,7 @@ services:
- S3_FORCE_PATH_STYLE=${S3_FORCE_PATH_STYLE}
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
- NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME=${NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME}
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
# Auth config

View File

@@ -36,6 +36,7 @@ services:
- S3_FORCE_PATH_STYLE=${S3_FORCE_PATH_STYLE}
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
- NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME=${NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME}
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
# White label

View File

@@ -35,6 +35,8 @@
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.802.0",
"@aws-sdk/s3-request-presigner": "^3.812.0",
"@kan/auth": "workspace:*",
"@kan/db": "workspace:*",
"@kan/email": "workspace:^",

View File

@@ -1,3 +1,4 @@
import { attachmentRouter } from "./routers/attachment";
import { boardRouter } from "./routers/board";
import { cardRouter } from "./routers/card";
import { checklistRouter } from "./routers/checklist";
@@ -12,6 +13,7 @@ import { workspaceRouter } from "./routers/workspace";
import { createTRPCRouter } from "./trpc";
export const appRouter = createTRPCRouter({
attachment: attachmentRouter,
board: boardRouter,
card: cardRouter,
checklist: checklistRouter,

View File

@@ -0,0 +1,205 @@
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import * as cardRepo from "@kan/db/repository/card.repo";
import * as cardActivityRepo from "@kan/db/repository/cardActivity.repo";
import * as cardAttachmentRepo from "@kan/db/repository/cardAttachment.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { generateUID } from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import { generateUploadUrl } from "../utils/s3";
export const attachmentRouter = createTRPCRouter({
generateUploadUrl: protectedProcedure
.meta({
openapi: {
summary: "Generate presigned URL for attachment upload",
method: "POST",
path: "/cards/{cardPublicId}/attachments/upload-url",
description:
"Generates a presigned URL for uploading an attachment to S3",
tags: ["Attachments"],
protect: true,
},
})
.input(
z.object({
cardPublicId: z.string().min(12),
filename: z.string().min(1).max(255),
contentType: z.string(),
size: z
.number()
.positive()
.max(50 * 1024 * 1024), // 50MB max
}),
)
.output(z.object({ url: z.string(), key: z.string() }))
.mutation(async ({ ctx, input }) => {
const userId = ctx.user?.id;
if (!userId)
throw new TRPCError({
message: `User not authenticated`,
code: "UNAUTHORIZED",
});
const card = await cardRepo.getWorkspaceAndCardIdByCardPublicId(
ctx.db,
input.cardPublicId,
);
if (!card)
throw new TRPCError({
message: `Card with public ID ${input.cardPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, card.workspaceId);
// Get workspace publicId
const workspace = await workspaceRepo.getById(ctx.db, card.workspaceId);
if (!workspace)
throw new TRPCError({
message: `Workspace not found`,
code: "NOT_FOUND",
});
const bucket = process.env.NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME;
if (!bucket)
throw new TRPCError({
message: `Attachments bucket not configured`,
code: "INTERNAL_SERVER_ERROR",
});
// Sanitize filename
const sanitizedFilename = input.filename
.replace(/[^a-zA-Z0-9._-]/g, "_")
.substring(0, 200);
const s3Key = `${workspace.publicId}/${input.cardPublicId}/${generateUID()}-${sanitizedFilename}`;
const url = await generateUploadUrl(
bucket,
s3Key,
input.contentType,
3600, // 1 hour
);
return { url, key: s3Key };
}),
confirm: protectedProcedure
.meta({
openapi: {
summary: "Confirm attachment upload and save to database",
method: "POST",
path: "/cards/{cardPublicId}/attachments/confirm",
description:
"Confirms an attachment upload and saves the record to the database",
tags: ["Attachments"],
protect: true,
},
})
.input(
z.object({
cardPublicId: z.string().min(12),
s3Key: z.string(),
filename: z.string(),
originalFilename: z.string(),
contentType: z.string(),
size: z.number().positive(),
}),
)
.output(z.custom<Awaited<ReturnType<typeof cardAttachmentRepo.create>>>())
.mutation(async ({ ctx, input }) => {
const userId = ctx.user?.id;
if (!userId)
throw new TRPCError({
message: `User not authenticated`,
code: "UNAUTHORIZED",
});
const card = await cardRepo.getWorkspaceAndCardIdByCardPublicId(
ctx.db,
input.cardPublicId,
);
if (!card)
throw new TRPCError({
message: `Card with public ID ${input.cardPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, card.workspaceId);
const attachment = await cardAttachmentRepo.create(ctx.db, {
cardId: card.id,
filename: input.filename,
originalFilename: input.originalFilename,
contentType: input.contentType,
size: input.size,
s3Key: input.s3Key,
createdBy: userId,
});
await cardActivityRepo.create(ctx.db, {
type: "card.updated.attachment.added",
cardId: card.id,
createdBy: userId,
});
return attachment;
}),
delete: protectedProcedure
.meta({
openapi: {
summary: "Delete an attachment",
method: "DELETE",
path: "/attachments/{attachmentPublicId}",
description: "Soft deletes an attachment",
tags: ["Attachments"],
protect: true,
},
})
.input(z.object({ attachmentPublicId: z.string().min(12) }))
.output(z.object({ success: z.boolean() }))
.mutation(async ({ ctx, input }) => {
const userId = ctx.user?.id;
if (!userId)
throw new TRPCError({
message: `User not authenticated`,
code: "UNAUTHORIZED",
});
const attachment = await cardAttachmentRepo.getByPublicId(
ctx.db,
input.attachmentPublicId,
);
if (!attachment || attachment.deletedAt)
throw new TRPCError({
message: `Attachment with public ID ${input.attachmentPublicId} not found`,
code: "NOT_FOUND",
});
const workspaceId = attachment.card.list.board.workspaceId;
await assertUserInWorkspace(ctx.db, userId, workspaceId);
await cardAttachmentRepo.softDelete(ctx.db, {
attachmentId: attachment.id,
deletedAt: new Date(),
});
await cardActivityRepo.create(ctx.db, {
type: "card.updated.attachment.removed",
cardId: attachment.cardId,
createdBy: userId,
});
return { success: true };
}),
});

View File

@@ -78,6 +78,7 @@ export const boardRouter = createTRPCRouter({
boardPublicId: z.string().min(12),
members: z.array(z.string().min(12)).optional(),
labels: z.array(z.string().min(12)).optional(),
lists: z.array(z.string().min(12)).optional(),
type: z.enum(["regular", "template"]).optional(),
}),
)
@@ -110,6 +111,7 @@ export const boardRouter = createTRPCRouter({
{
members: input.members ?? [],
labels: input.labels ?? [],
lists: input.lists ?? [],
type: input.type,
},
);
@@ -142,6 +144,7 @@ export const boardRouter = createTRPCRouter({
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
members: z.array(z.string().min(12)).optional(),
labels: z.array(z.string().min(12)).optional(),
lists: z.array(z.string().min(12)).optional(),
}),
)
.output(z.custom<Awaited<ReturnType<typeof boardRepo.getBySlug>>>())
@@ -164,6 +167,7 @@ export const boardRouter = createTRPCRouter({
{
members: input.members ?? [],
labels: input.labels ?? [],
lists: input.lists ?? [],
},
);
@@ -234,6 +238,7 @@ export const boardRouter = createTRPCRouter({
{
members: [],
labels: [],
lists: [],
type: sourceBoardInfo.type,
},
);

View File

@@ -10,6 +10,7 @@ import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import { generateDownloadUrl } from "../utils/s3";
export const cardRouter = createTRPCRouter({
create: protectedProcedure
@@ -574,7 +575,21 @@ export const cardRouter = createTRPCRouter({
.input(z.object({ cardPublicId: z.string().min(12) }))
.output(
z.custom<
Awaited<ReturnType<typeof cardRepo.getWithListAndMembersByPublicId>>
Omit<
NonNullable<
Awaited<ReturnType<typeof cardRepo.getWithListAndMembersByPublicId>>
>,
"attachments"
> & {
attachments: {
publicId: string;
contentType: string;
s3Key: string;
originalFilename: string | null;
size?: number | null;
url: string | null;
}[];
}
>(),
)
.query(async ({ ctx, input }) => {
@@ -612,7 +627,46 @@ export const cardRouter = createTRPCRouter({
code: "NOT_FOUND",
});
return result;
// Generate URLs for all attachments
const bucket = process.env.NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME;
if (result.attachments && Array.isArray(result.attachments)) {
const attachments = result.attachments as {
publicId: string;
contentType: string;
s3Key: string;
originalFilename: string | null;
size?: number | null;
}[];
const attachmentsWithUrls = await Promise.all(
attachments.map(async (attachment) => {
const base = {
publicId: attachment.publicId,
contentType: attachment.contentType,
s3Key: attachment.s3Key,
originalFilename: attachment.originalFilename,
size: attachment.size,
};
if (!bucket || !attachment.s3Key) {
return { ...base, url: null };
}
try {
const url = await generateDownloadUrl(
bucket,
attachment.s3Key,
86400, // 24 hours expiration
);
return { ...base, url };
} catch {
// If URL generation fails, return attachment with url: null
return { ...base, url: null };
}
}),
);
return { ...result, attachments: attachmentsWithUrls };
}
return { ...result, attachments: [] };
}),
update: protectedProcedure
.meta({

View File

@@ -4,6 +4,7 @@ import { z } from "zod";
import * as boardRepo from "@kan/db/repository/board.repo";
import * as cardRepo from "@kan/db/repository/card.repo";
import * as cardActivityRepo from "@kan/db/repository/cardActivity.repo";
import * as checklistRepo from "@kan/db/repository/checklist.repo";
import * as importRepo from "@kan/db/repository/import.repo";
import * as integrationsRepo from "@kan/db/repository/integration.repo";
import * as labelRepo from "@kan/db/repository/label.repo";
@@ -22,6 +23,7 @@ export interface TrelloBoard {
labels: TrelloLabel[];
lists: TrelloList[];
cards: TrelloCard[];
checklists: TrelloChecklist[];
}
interface TrelloLabel {
@@ -34,12 +36,34 @@ interface TrelloList {
name: string;
}
interface TrelloChecklist {
id: string;
idCard: string;
name: string;
checkItems: TrelloCheckItem[];
}
interface TrelloCheckItem {
id: string;
name: string;
state: "complete" | "incomplete";
pos: number;
}
interface TrelloCard {
id: string;
name: string;
desc: string;
idList: string;
labels: TrelloLabel[];
idChecklists: string[];
checkItemStates: TrelloCheckItemState[];
}
interface TrelloCheckItemState {
idChecklist: string;
idCheckItem: string;
state: "complete" | "incomplete";
}
export const importRouter = createTRPCRouter({
@@ -170,7 +194,7 @@ export const importRouter = createTRPCRouter({
for (const boardId of input.boardIds) {
const response = await fetch(
`${urls.trello}/boards/${boardId}?key=${apiKey}&token=${integration.accessToken}&lists=open&cards=open&labels=all`,
`${urls.trello}/boards/${boardId}?key=${apiKey}&token=${integration.accessToken}&lists=open&cards=open&labels=all&checklists=all&checkItemStates=all`,
);
const data = (await response.json()) as TrelloBoard;
@@ -194,6 +218,18 @@ export const importRouter = createTRPCRouter({
sourceId: label.id,
name: label.name,
})),
checklists: data.checklists
.filter((checklist) => checklist.idCard === _card.id)
.map((_checklist) => ({
sourceId: _checklist.id,
name: _checklist.name,
items: _checklist.checkItems.map((_item) => ({
sourceId: _item.id,
title: _item.name,
completed: _item.state === "complete",
index: _item.pos,
})),
})),
})),
})),
};
@@ -289,6 +325,97 @@ export const importRouter = createTRPCRouter({
await cardActivityRepo.bulkCreate(ctx.db, activities);
}
const checklistsToCreate: {
cardId: number;
name: string;
createdBy: string;
index: number;
sourceId: string;
items: {
sourceId: string;
title: string;
completed: boolean;
index: number;
}[];
}[] = [];
for (const card of list.cards) {
const _card = createdCards.find(
(c) => c.sourceId === card.sourceId,
);
if (!_card || !card.checklists.length) continue;
for (
let checklistIndex = 0;
checklistIndex < card.checklists.length;
checklistIndex++
) {
const checklist = card.checklists[checklistIndex];
if (!checklist) continue;
checklistsToCreate.push({
cardId: _card.id,
name: checklist.name,
createdBy: userId,
index: checklistIndex,
sourceId: checklist.sourceId,
items: checklist.items.map((item) => ({
sourceId: item.sourceId,
title: item.title,
completed: item.completed,
index: item.index,
})),
});
}
}
if (checklistsToCreate.length > 0) {
const newChecklists = await checklistRepo.bulkCreate(
ctx.db,
checklistsToCreate.map((checklist) => ({
cardId: checklist.cardId,
name: checklist.name,
createdBy: checklist.createdBy,
index: checklist.index,
})),
);
const itemsToCreate: {
checklistId: number;
title: string;
createdBy: string;
index: number;
completed: boolean;
}[] = [];
for (let i = 0; i < checklistsToCreate.length; i++) {
const checklistData = checklistsToCreate[i];
const newChecklist = newChecklists[i];
if (!newChecklist || !checklistData?.items.length) continue;
// NOTE: Sorting here to prevent checklist items being out of order
const sortedItems = [...checklistData.items].sort(
(a, b) => a.index - b.index,
);
for (const item of sortedItems) {
itemsToCreate.push({
checklistId: newChecklist.id,
title: item.title,
createdBy: userId,
index: item.index,
completed: item.completed,
});
}
}
if (itemsToCreate.length > 0) {
await checklistRepo.bulkCreateItems(ctx.db, itemsToCreate);
}
}
if (createdLabels.length && createdCards.length) {
const cardLabelRelations: {
cardId: number;

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

@@ -0,0 +1,53 @@
import {
GetObjectCommand,
PutObjectCommand,
S3Client,
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
export function createS3Client() {
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 ?? "",
},
});
}
export async function generateUploadUrl(
bucket: string,
key: string,
contentType: string,
expiresIn = 3600,
) {
const client = createS3Client();
return getSignedUrl(
client,
new PutObjectCommand({
Bucket: bucket,
Key: key,
ContentType: contentType,
// Don't set ACL for private files
}),
{ expiresIn },
);
}
export async function generateDownloadUrl(
bucket: string,
key: string,
expiresIn = 3600,
) {
const client = createS3Client();
return getSignedUrl(
client,
new GetObjectCommand({
Bucket: bucket,
Key: key,
}),
{ expiresIn },
);
}

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<
@@ -129,6 +133,11 @@ export const initAuth = (db: dbClient) => {
user: schema.users,
},
}),
session: {
expiresIn: 60 * 60 * 24 * 30, // 30 days
updateAge: 60 * 60 * 24 * 2, // Update session expiry every 48 hours if user is active
freshAge: 0,
},
emailAndPassword: {
enabled: env("NEXT_PUBLIC_ALLOW_CREDENTIALS")?.toLowerCase() === "true",
disableSignUp:
@@ -170,12 +179,44 @@ export const initAuth = (db: dbClient) => {
priceId: process.env.STRIPE_TEAM_PLAN_MONTHLY_PRICE_ID!,
annualDiscountPriceId:
process.env.STRIPE_TEAM_PLAN_YEARLY_PRICE_ID!,
freeTrial: {
days: 14,
onTrialStart: async (subscription) => {
await triggerWorkflow(db, "trial-start", subscription);
},
onTrialEnd: async ({ subscription }) => {
await triggerWorkflow(db, "trial-end", subscription);
},
onTrialExpired: async (subscription) => {
await triggerWorkflow(
db,
"trial-expired",
subscription,
);
},
},
},
{
name: "pro",
priceId: process.env.STRIPE_PRO_PLAN_MONTHLY_PRICE_ID!,
annualDiscountPriceId:
process.env.STRIPE_PRO_PLAN_YEARLY_PRICE_ID!,
freeTrial: {
days: 14,
onTrialStart: async (subscription) => {
await triggerWorkflow(db, "trial-start", subscription);
},
onTrialEnd: async ({ subscription }) => {
await triggerWorkflow(db, "trial-end", subscription);
},
onTrialExpired: async (subscription) => {
await triggerWorkflow(
db,
"trial-expired",
subscription,
);
},
},
},
],
authorizeReference: async (data) => {
@@ -307,17 +348,7 @@ export const initAuth = (db: dbClient) => {
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!)
@@ -350,6 +381,9 @@ export const initAuth = (db: dbClient) => {
ACL: "public-read",
}),
);
avatarKey = key;
await userRepo.update(db, user.id, {
image: key,
});
@@ -357,6 +391,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,
);
}
}
},
},
},
@@ -394,3 +480,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,29 @@
ALTER TYPE "public"."card_activity_type" ADD VALUE 'card.updated.attachment.added' BEFORE 'card.archived';--> statement-breakpoint
ALTER TYPE "public"."card_activity_type" ADD VALUE 'card.updated.attachment.removed' BEFORE 'card.archived';--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "card_attachment" (
"id" bigserial PRIMARY KEY NOT NULL,
"publicId" varchar(12) NOT NULL,
"cardId" bigint NOT NULL,
"filename" varchar(255) NOT NULL,
"originalFilename" varchar(255) NOT NULL,
"contentType" varchar(100) NOT NULL,
"size" bigint NOT NULL,
"s3Key" varchar(500) NOT NULL,
"createdBy" uuid,
"createdAt" timestamp DEFAULT now() NOT NULL,
"deletedAt" timestamp,
CONSTRAINT "card_attachment_publicId_unique" UNIQUE("publicId")
);
--> statement-breakpoint
ALTER TABLE "card_attachment" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "card_attachment" ADD CONSTRAINT "card_attachment_cardId_card_id_fk" FOREIGN KEY ("cardId") REFERENCES "public"."card"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "card_attachment" ADD CONSTRAINT "card_attachment_createdBy_user_id_fk" FOREIGN KEY ("createdBy") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

File diff suppressed because it is too large Load Diff

View File

@@ -134,6 +134,13 @@
"when": 1760044396109,
"tag": "20251009211316_AddBoardSourceIdToCardActivity",
"breakpoints": true
},
{
"idx": 19,
"version": "7",
"when": 1762807463569,
"tag": "20251110204423_AddCardAttachments",
"breakpoints": true
}
]
}

View File

@@ -5,6 +5,7 @@ import type { BoardVisibilityStatus } from "@kan/db/schema";
import {
boards,
cardActivities,
cardAttachments,
cards,
cardsToLabels,
cardToWorkspaceMembers,
@@ -70,6 +71,7 @@ export const getByPublicId = async (
filters: {
members: string[];
labels: string[];
lists: string[];
type: "regular" | "template" | undefined;
},
) => {
@@ -195,6 +197,13 @@ export const getByPublicId = async (
},
},
},
attachments: {
columns: {
publicId: true,
},
where: isNull(cardAttachments.deletedAt),
orderBy: asc(cardAttachments.createdAt),
},
checklists: {
columns: {
publicId: true,
@@ -231,6 +240,19 @@ export const getByPublicId = async (
orderBy: [asc(cards.index)],
},
},
where: and(
isNull(lists.deletedAt),
filters.lists.length > 0
? inArray(lists.publicId, filters.lists)
: undefined,
),
orderBy: [asc(lists.index)],
},
allLists: {
columns: {
publicId: true,
name: true,
},
where: isNull(lists.deletedAt),
orderBy: [asc(lists.index)],
},
@@ -268,6 +290,7 @@ export const getBySlug = async (
filters: {
members: string[];
labels: string[];
lists: string[];
},
) => {
let cardIds: string[] = [];
@@ -343,6 +366,13 @@ export const getBySlug = async (
},
},
},
attachments: {
columns: {
publicId: true,
},
where: isNull(cardAttachments.deletedAt),
orderBy: asc(cardAttachments.createdAt),
},
comments: {
columns: {
publicId: true,
@@ -379,6 +409,19 @@ export const getBySlug = async (
orderBy: [asc(cards.index)],
},
},
where: and(
isNull(lists.deletedAt),
filters.lists.length > 0
? inArray(lists.publicId, filters.lists)
: undefined,
),
orderBy: [asc(lists.index)],
},
allLists: {
columns: {
publicId: true,
name: true,
},
where: isNull(lists.deletedAt),
orderBy: [asc(lists.index)],
},

View File

@@ -3,6 +3,7 @@ import { and, asc, desc, eq, gt, inArray, isNull, sql } from "drizzle-orm";
import type { dbClient } from "@kan/db/client";
import {
cardActivities,
cardAttachments,
cards,
cardsToLabels,
cardToWorkspaceMembers,
@@ -409,6 +410,17 @@ export const getWithListAndMembersByPublicId = async (
},
},
},
attachments: {
columns: {
publicId: true,
contentType: true,
s3Key: true,
originalFilename: true,
size: true,
},
where: isNull(cardAttachments.deletedAt),
orderBy: asc(cardAttachments.createdAt),
},
checklists: {
columns: {
publicId: true,

View File

@@ -0,0 +1,99 @@
import { and, eq, isNull } from "drizzle-orm";
import type { dbClient } from "@kan/db/client";
import { cardAttachments } from "@kan/db/schema";
import { generateUID } from "@kan/shared/utils";
export const create = async (
db: dbClient,
attachmentInput: {
cardId: number;
filename: string;
originalFilename: string;
contentType: string;
size: number;
s3Key: string;
createdBy: string;
},
) => {
const [result] = await db
.insert(cardAttachments)
.values({
publicId: generateUID(),
cardId: attachmentInput.cardId,
filename: attachmentInput.filename,
originalFilename: attachmentInput.originalFilename,
contentType: attachmentInput.contentType,
size: attachmentInput.size,
s3Key: attachmentInput.s3Key,
createdBy: attachmentInput.createdBy,
})
.returning({
id: cardAttachments.id,
publicId: cardAttachments.publicId,
filename: cardAttachments.filename,
originalFilename: cardAttachments.originalFilename,
contentType: cardAttachments.contentType,
size: cardAttachments.size,
s3Key: cardAttachments.s3Key,
createdBy: cardAttachments.createdBy,
createdAt: cardAttachments.createdAt,
});
return result;
};
export const getByPublicId = (db: dbClient, publicId: string) => {
return db.query.cardAttachments.findFirst({
where: eq(cardAttachments.publicId, publicId),
with: {
card: {
columns: {
id: true,
publicId: true,
},
with: {
list: {
columns: {
id: true,
},
with: {
board: {
columns: {
id: true,
workspaceId: true,
},
},
},
},
},
},
},
});
};
export const getAllByCardId = (db: dbClient, cardId: number) => {
return db.query.cardAttachments.findMany({
where: and(
eq(cardAttachments.cardId, cardId),
isNull(cardAttachments.deletedAt),
),
orderBy: (attachments, { desc }) => [desc(attachments.createdAt)],
});
};
export const softDelete = async (
db: dbClient,
args: {
attachmentId: number;
deletedAt: Date;
},
) => {
const [result] = await db
.update(cardAttachments)
.set({ deletedAt: args.deletedAt })
.where(eq(cardAttachments.id, args.attachmentId))
.returning({ id: cardAttachments.id });
return result;
};

View File

@@ -46,6 +46,7 @@ export const createItem = async (
checklistId: number;
title: string;
createdBy: string;
completed?: boolean;
},
) => {
return db.transaction(async (tx) => {
@@ -65,7 +66,7 @@ export const createItem = async (
createdBy: checklistItemInput.createdBy,
checklistId: checklistItemInput.checklistId,
index: lastItem ? lastItem.index + 1 : 0,
completed: false,
completed: checklistItemInput.completed ?? false,
})
.returning({
id: checklistItems.id,
@@ -214,3 +215,129 @@ export const updateChecklistById = async (
.returning({ publicId: checklists.publicId, name: checklists.name });
return result;
};
export const bulkCreate = async (
db: dbClient,
checklistInput: {
cardId: number;
name: string;
createdBy: string;
index: number;
}[],
) => {
if (checklistInput.length === 0) return [];
return db.transaction(async (tx) => {
const byCard = groupByKey(checklistInput, "cardId");
const allValuesToInsert: {
publicId: string;
cardId: number;
name: string;
createdBy: string;
index: number;
}[] = [];
for (const [cardId, items] of byCard.entries()) {
const last = await tx.query.checklists.findFirst({
columns: { index: true },
where: and(eq(checklists.cardId, cardId), isNull(checklists.deletedAt)),
orderBy: [desc(checklists.index)],
});
let nextIndex = last ? last.index + 1 : 0;
const sorted = [...items].sort((a, b) => a.index - b.index);
for (const item of sorted) {
allValuesToInsert.push({
publicId: generateUID(),
...item,
index: nextIndex++,
});
}
}
const inserted = await tx
.insert(checklists)
.values(allValuesToInsert)
.returning({ id: checklists.id, publicId: checklists.publicId });
return inserted;
});
};
export const bulkCreateItems = async (
db: dbClient,
checklistItemInput: {
checklistId: number;
title: string;
createdBy: string;
index: number;
completed: boolean;
}[],
) => {
if (checklistItemInput.length === 0) return [];
return db.transaction(async (tx) => {
const byChecklist = groupByKey(checklistItemInput, "checklistId");
const allValuesToInsert: {
publicId: string;
checklistId: number;
title: string;
createdBy: string;
index: number;
completed: boolean;
}[] = [];
for (const [checklistId, items] of byChecklist.entries()) {
const last = await tx.query.checklistItems.findFirst({
columns: { index: true },
where: and(
eq(checklistItems.checklistId, checklistId),
isNull(checklistItems.deletedAt),
),
orderBy: [desc(checklistItems.index)],
});
let nextIndex = last ? last.index + 1 : 0;
const sorted = [...items].sort((a, b) => a.index - b.index);
for (const item of sorted) {
allValuesToInsert.push({
publicId: generateUID(),
...item,
index: nextIndex++,
});
}
}
const inserted = await tx
.insert(checklistItems)
.values(allValuesToInsert)
.returning({
id: checklistItems.id,
publicId: checklistItems.publicId,
title: checklistItems.title,
completed: checklistItems.completed,
});
return inserted;
});
};
const groupByKey = <T extends Record<string, unknown>>(
items: T[],
keyField: keyof T,
): Map<number, T[]> => {
const grouped = new Map<number, T[]>();
for (const item of items) {
const key = item[keyField] as number;
const arr = grouped.get(key) ?? [];
arr.push(item);
grouped.set(key, arr);
}
return grouped;
};

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

@@ -73,6 +73,7 @@ export const boardsRelations = relations(boards, ({ one, many }) => ({
relationName: "boardCreatedByUser",
}),
lists: many(lists),
allLists: many(lists),
labels: many(labels),
deletedBy: one(users, {
fields: [boards.deletedBy],

View File

@@ -42,6 +42,8 @@ export const activityTypes = [
"card.updated.checklist.item.completed",
"card.updated.checklist.item.uncompleted",
"card.updated.checklist.item.deleted",
"card.updated.attachment.added",
"card.updated.attachment.removed",
"card.archived",
] as const;
@@ -96,6 +98,7 @@ export const cardsRelations = relations(cards, ({ one, many }) => ({
comments: many(comments),
activities: many(cardActivities),
checklists: many(checklists),
attachments: many(cardAttachments),
}));
export const cardActivities = pgTable("card_activity", {
@@ -273,3 +276,37 @@ export const commentsRelations = relations(comments, ({ one }) => ({
relationName: "commentsDeletedByUser",
}),
}));
export const cardAttachments = pgTable("card_attachment", {
id: bigserial("id", { mode: "number" }).primaryKey(),
publicId: varchar("publicId", { length: 12 }).notNull().unique(),
cardId: bigint("cardId", { mode: "number" })
.notNull()
.references(() => cards.id, { onDelete: "cascade" }),
filename: varchar("filename", { length: 255 }).notNull(),
originalFilename: varchar("originalFilename", { length: 255 }).notNull(),
contentType: varchar("contentType", { length: 100 }).notNull(),
size: bigint("size", { mode: "number" }).notNull(),
s3Key: varchar("s3Key", { length: 500 }).notNull(),
createdBy: uuid("createdBy").references(() => users.id, {
onDelete: "set null",
}),
createdAt: timestamp("createdAt").defaultNow().notNull(),
deletedAt: timestamp("deletedAt"),
}).enableRLS();
export const cardAttachmentsRelations = relations(
cardAttachments,
({ one }) => ({
card: one(cards, {
fields: [cardAttachments.cardId],
references: [cards.id],
relationName: "cardAttachmentsCard",
}),
createdBy: one(users, {
fields: [cardAttachments.createdBy],
references: [users.id],
relationName: "cardAttachmentsCreatedByUser",
}),
}),
);

View File

@@ -23,7 +23,7 @@
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
"@plunk/node": "^3.0.3",
"@novu/api": "^3.11.0",
"@react-email/components": "^0.0.25",
"nodemailer": "^7.0.3",
"react-email": "^3.0.1"

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";
@@ -45,15 +44,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,8 @@
},
"prettier": "@kan/prettier-config",
"dependencies": {
"nanoid": "^5.0.9"
"jose": "^6.1.2",
"nanoid": "^5.0.9",
"next-runtime-env": "^1.7.2"
}
}

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,4 @@
export * from "./generateUID";
export * from "./generateSlug";
export * from "./subscriptions";
export * from "./email";

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 };

84
pnpm-lock.yaml generated
View File

@@ -115,6 +115,9 @@ importers:
'@lingui/react':
specifier: ^5.3.2
version: 5.4.1(@lingui/babel-plugin-lingui-macro@5.4.1(typescript@5.9.2))(react@18.3.1)
'@novu/api':
specifier: ^3.11.0
version: 3.11.0
'@t3-oss/env-nextjs':
specifier: ^0.11.1
version: 0.11.1(typescript@5.9.2)(zod@3.25.76)
@@ -166,6 +169,9 @@ importers:
geist:
specifier: ^1.3.1
version: 1.4.2(next@15.5.2(@babel/core@7.28.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
jose:
specifier: ^6.1.2
version: 6.1.2
next:
specifier: ^15.3.4
version: 15.5.2(@babel/core@7.28.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -223,6 +229,9 @@ importers:
tippy.js:
specifier: ^6.3.7
version: 6.3.7
tiptap-markdown:
specifier: ^0.8
version: 0.8.10(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
zod:
specifier: 'catalog:'
version: 3.25.76
@@ -290,6 +299,12 @@ importers:
packages/api:
dependencies:
'@aws-sdk/client-s3':
specifier: ^3.802.0
version: 3.879.0
'@aws-sdk/s3-request-presigner':
specifier: ^3.812.0
version: 3.879.0
'@kan/auth':
specifier: workspace:*
version: link:../auth
@@ -431,9 +446,9 @@ importers:
packages/email:
dependencies:
'@plunk/node':
specifier: ^3.0.3
version: 3.0.3
'@novu/api':
specifier: ^3.11.0
version: 3.11.0
'@react-email/components':
specifier: ^0.0.25
version: 0.0.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -468,9 +483,15 @@ importers:
packages/shared:
dependencies:
jose:
specifier: ^6.1.2
version: 6.1.2
nanoid:
specifier: ^5.0.9
version: 5.1.5
next-runtime-env:
specifier: ^1.7.2
version: 1.8.0
devDependencies:
'@kan/eslint-config':
specifier: workspace:*
@@ -2674,6 +2695,9 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@novu/api@3.11.0':
resolution: {integrity: sha512-8u0mB5VThL7MhdxoN0UoA4CS9eu2k3Xa6iulauMhCHENuJNxUNuirJWq5t8jDoH4bFTQTfMN0VRkC0qodKR2qA==}
'@octokit/auth-token@3.0.4':
resolution: {integrity: sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==}
engines: {node: '>= 14'}
@@ -2753,9 +2777,6 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@plunk/node@3.0.3':
resolution: {integrity: sha512-f3NT418/EdLjebQ4arAwR64cnz7d5H7i1CeKh8+B0cVBrwzHvF+Q+QkhutF+FAp49yuI/BYc71qQH+RFc2fxxQ==}
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
@@ -3575,9 +3596,15 @@ packages:
'@types/katex@0.16.7':
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
'@types/linkify-it@3.0.5':
resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==}
'@types/linkify-it@5.0.0':
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
'@types/markdown-it@13.0.9':
resolution: {integrity: sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==}
'@types/markdown-it@14.1.2':
resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
@@ -3587,6 +3614,9 @@ packages:
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
'@types/mdurl@1.0.5':
resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==}
'@types/mdurl@2.0.0':
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
@@ -5536,6 +5566,9 @@ packages:
jose@5.10.0:
resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==}
jose@6.1.2:
resolution: {integrity: sha512-MpcPtHLE5EmztuFIqB0vzHAWJPpmN1E6L4oo+kze56LIs3MyXIj9ZHMDxqOvkP38gBR7K1v3jqd4WU2+nrfONQ==}
joycon@3.1.1:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
@@ -5730,6 +5763,9 @@ packages:
make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
markdown-it-task-lists@2.1.1:
resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==}
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
@@ -7411,6 +7447,11 @@ packages:
tippy.js@6.3.7:
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
tiptap-markdown@0.8.10:
resolution: {integrity: sha512-iDVkR2BjAqkTDtFX0h94yVvE2AihCXlF0Q7RIXSJPRSR5I0PA1TMuAg6FHFpmqTn4tPxJ0by0CK7PUMlnFLGEQ==}
peerDependencies:
'@tiptap/core': ^2.0.3
title-case@2.1.1:
resolution: {integrity: sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==}
@@ -10200,6 +10241,10 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1
'@novu/api@3.11.0':
dependencies:
zod: 3.25.76
'@octokit/auth-token@3.0.4': {}
'@octokit/core@4.2.4':
@@ -10319,10 +10364,6 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@plunk/node@3.0.3':
dependencies:
tslib: 2.8.1
'@popperjs/core@2.11.8': {}
'@posthog/core@1.0.2': {}
@@ -11321,8 +11362,15 @@ snapshots:
'@types/katex@0.16.7': {}
'@types/linkify-it@3.0.5': {}
'@types/linkify-it@5.0.0': {}
'@types/markdown-it@13.0.9':
dependencies:
'@types/linkify-it': 3.0.5
'@types/mdurl': 1.0.5
'@types/markdown-it@14.1.2':
dependencies:
'@types/linkify-it': 5.0.0
@@ -11336,6 +11384,8 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
'@types/mdurl@1.0.5': {}
'@types/mdurl@2.0.0': {}
'@types/ms@2.1.0': {}
@@ -13614,6 +13664,8 @@ snapshots:
jose@5.10.0: {}
jose@6.1.2: {}
joycon@3.1.1: {}
js-beautify@1.15.4:
@@ -13777,6 +13829,8 @@ snapshots:
make-error@1.3.6: {}
markdown-it-task-lists@2.1.1: {}
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
@@ -16085,6 +16139,14 @@ snapshots:
dependencies:
'@popperjs/core': 2.11.8
tiptap-markdown@0.8.10(@tiptap/core@2.26.1(@tiptap/pm@2.26.1)):
dependencies:
'@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
'@types/markdown-it': 13.0.9
markdown-it: 14.1.0
markdown-it-task-lists: 2.1.1
prosemirror-markdown: 1.13.2
title-case@2.1.1:
dependencies:
no-case: 2.3.2
@@ -16466,7 +16528,7 @@ snapshots:
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.15.0
acorn-import-phases: 1.0.4(acorn@8.15.0)
browserslist: 4.25.4
browserslist: 4.27.0
chrome-trace-event: 1.0.4
enhanced-resolve: 5.18.3
es-module-lexer: 1.7.0

View File

@@ -99,6 +99,7 @@
"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",
@@ -108,6 +109,7 @@
"NEXT_PUBLIC_STORAGE_DOMAIN",
"NEXT_PUBLIC_STORAGE_URL",
"NEXT_PUBLIC_AVATAR_BUCKET_NAME",
"NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME",
"NEXT_PUBLIC_ALLOW_CREDENTIALS",
"NEXT_PUBLIC_DISABLE_SIGN_UP",
"NEXT_PUBLIC_USE_STANDALONE_OUTPUT",
@@ -119,8 +121,8 @@
"PORT",
"BETTER_AUTH_SECRET",
"BETTER_AUTH_TRUSTED_ORIGINS",
"PLUNK_API_KEY",
"PLUNK_API_URL"
"NOVU_API_KEY",
"EMAIL_UNSUBSCRIBE_SECRET"
],
"globalPassThroughEnv": [
"NODE_ENV",