Compare commits

..

14 Commits

Author SHA1 Message Date
Henry
8863d43793 feat: set up partner webhook handler 2026-05-12 22:36:28 +01:00
Henry
9837ddd8af feat: add repo funcs 2026-05-12 21:56:28 +01:00
Henry
e34518e654 feat: add partnerLicenseKey and partnerTier to subscription schema 2026-05-12 21:34:01 +01:00
Henry
4459fbb4b2 fix: pin pnpm version in dockerfile to prevent corepack fetching latest 2026-05-11 15:49:34 +01:00
hjball
507c553778 chore: update translations 2026-05-11 12:45:01 +00:00
Henry
3d9e859b31 refactor: small tweaks to password reset flow 2026-05-11 13:43:38 +01:00
hjball
54dcd08d9a chore: compile translations 2026-05-11 12:42:35 +00:00
hjball
468aeef8b6 chore: update translations 2026-05-11 12:42:31 +00:00
Morfixx
71205b67dc fix: unable to change password when using magic link (#484)
* fix: fixed password not resetting & added password prompt with save guards

* fix: migrated to using setPassword
2026-05-11 13:40:59 +01:00
Midas
607672718c Add cardPublicId to card webhook payloads (#490)
* Add cardPublicId to card webhook payloads

* fix: include card publicId in webhook payloads

Add data.card.publicId to webhook payloads while keeping data.card.id for compatibility, and update card/webhook call sites and tests so webhook consumers can reliably use public IDs.
2026-05-11 13:22:21 +01:00
Henry
dd262ee50f fix: allow reordering without supplying an index when updating cards (#492) 2026-05-11 13:18:07 +01:00
Henry
82c8eef0c2 Update README.md 2026-05-05 21:50:50 +01:00
hjball
ea116268ff chore: update translations 2026-05-05 20:49:45 +00:00
Henry
74384d841e Update README.md 2026-05-05 21:48:29 +01:00
41 changed files with 5824 additions and 345 deletions

View File

@@ -227,6 +227,12 @@ We welcome contributions! Please read our [contribution guidelines](CONTRIBUTING
<img src="https://contrib.rocks/image?repo=kanbn/kan" />
</a>
## Sponsors ❤️
[<img height="100" alt="image" src="https://github.com/user-attachments/assets/e331c71f-ac86-46a6-bceb-ce276de094b0" />](https://www.testmuai.com)
Proudly sponsored by [TestMu AI](https://www.testmuai.com) - an AI-native testing cloud platform built for modern engineering teams. Covering everything from autonomous test creation and fast execution to testing AI agents like chatbots and voice assistants. If you're serious about testing, go check them out.
## License 📝
Kan is licensed under the [AGPLv3 license](LICENSE).

View File

@@ -11,6 +11,7 @@ RUN apk update && \
apk add --no-cache libc6-compat && \
rm -rf /var/cache/apk/* /tmp/* || true && \
corepack enable && \
corepack prepare pnpm@9.14.2 --activate && \
npm install turbo@2.3.1 --global && \
pnpm config set store-dir ~/.pnpm-store

View File

@@ -614,9 +614,10 @@ checksums:
RRn9jf/origin/1/0: d6f5120f072a821219608624f361384e
RRn9jf/translation: 210b6ff8727f976182ec3f29ea3c7667
yz7wBu/message: 2c2e22f8424a1031de89063bd0022e16
yz7wBu/origin/0/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
yz7wBu/origin/1/0: afee530613110e248ffde6b56872ebed
yz7wBu/origin/2/0: dcafca531d4dce1faef7788feabfb048
yz7wBu/origin/0/0: 278a40eb45b1219f424703d0abc1d273
yz7wBu/origin/1/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
yz7wBu/origin/2/0: afee530613110e248ffde6b56872ebed
yz7wBu/origin/3/0: dcafca531d4dce1faef7788feabfb048
yz7wBu/translation: 2c2e22f8424a1031de89063bd0022e16
TcXcmk/message: a2da6c2339301e7c3ddf068c4ff9f7e8
TcXcmk/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
@@ -734,9 +735,9 @@ checksums:
uvH2xS/message: ecc2617544a6d385d18678577fa112fd
uvH2xS/origin/0/0: 97bd206a05414e2a571020d64e166093
uvH2xS/translation: ecc2617544a6d385d18678577fa112fd
7mrkyO/translation: d3853b58e9b736eefb0f9bf400daf4d7
7mrkyO/message: d3853b58e9b736eefb0f9bf400daf4d7
7mrkyO/origin/0/0: 1b05beba6bdd908015fa8e798717f024
7mrkyO/translation: d3853b58e9b736eefb0f9bf400daf4d7
9PaIxv/message: da95932e7a1465a5d21aa3b855a46dc2
9PaIxv/origin/0/0: c0b59968b2b234167c488a05cc38710b
9PaIxv/translation: da95932e7a1465a5d21aa3b855a46dc2
@@ -1121,6 +1122,9 @@ checksums:
rarRW%2F/origin/0/0: 8628ddb6f4ded25c0f17e2cbda8207d3
rarRW%2F/origin/1/0: 8628ddb6f4ded25c0f17e2cbda8207d3
rarRW%2F/translation: 91553b606a6ebed3534cd2e5635ce004
sZixXc/translation: db4e3e372abb1ac510c62b14d544f0f8
sZixXc/message: db4e3e372abb1ac510c62b14d544f0f8
sZixXc/origin/0/0: 0627e0040ca4939c9a57349e98c51797
sVBn4K/message: 0d7979a25d290123ed8b8906bf468f1a
sVBn4K/origin/0/0: d465da67c0a870d198eb7f313569c137
sVBn4K/translation: 0d7979a25d290123ed8b8906bf468f1a
@@ -1380,9 +1384,9 @@ checksums:
2Py5S%2F/message: fe1183a7439299210d8ab7379b8f4d01
2Py5S%2F/origin/0/0: 83911e3eacbad4583e2b1647a784d154
2Py5S%2F/translation: fe1183a7439299210d8ab7379b8f4d01
VOwhhz/translation: 613f43526ea128b2749f70878f10008c
VOwhhz/message: 613f43526ea128b2749f70878f10008c
VOwhhz/origin/0/0: 1b05beba6bdd908015fa8e798717f024
VOwhhz/translation: 613f43526ea128b2749f70878f10008c
iwr7AP/message: 27c24b56895defecd10b0171b39bc93f
iwr7AP/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
iwr7AP/translation: 27c24b56895defecd10b0171b39bc93f
@@ -1817,6 +1821,9 @@ checksums:
Uworke/message: 213d63da450f35dabb3ab0e35e29feed
Uworke/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
Uworke/translation: 213d63da450f35dabb3ab0e35e29feed
nG7mus/translation: 03326832c645f4ddb873a6e7eab8a639
nG7mus/message: 03326832c645f4ddb873a6e7eab8a639
nG7mus/origin/0/0: 278a40eb45b1219f424703d0abc1d273
IrZaAn/message: 1fcebe9ddb46f722a57f195efddc695d
IrZaAn/origin/0/0: 0627e0040ca4939c9a57349e98c51797
IrZaAn/translation: 1fcebe9ddb46f722a57f195efddc695d
@@ -1829,6 +1836,9 @@ checksums:
vwGkYB/message: 4c30501d085eaccea47af34212bb26a7
vwGkYB/origin/0/0: 0627e0040ca4939c9a57349e98c51797
vwGkYB/translation: 4c30501d085eaccea47af34212bb26a7
Xlkxmi/translation: 3251e7239a9b349a86d5785a113cd6b9
Xlkxmi/message: 3251e7239a9b349a86d5785a113cd6b9
Xlkxmi/origin/0/0: 0627e0040ca4939c9a57349e98c51797
lpIMne/message: 37ca1f4e0afc9a0b8e9617f767103c92
lpIMne/origin/0/0: 0627e0040ca4939c9a57349e98c51797
lpIMne/translation: 37ca1f4e0afc9a0b8e9617f767103c92
@@ -2186,11 +2196,20 @@ checksums:
v3YoMA/message: 3fff865dc00435f82896fc302ea45630
v3YoMA/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
v3YoMA/translation: 3fff865dc00435f82896fc302ea45630
GXGbXi/translation: 8686cb61f9bd3cd7ed100cc3af819458
GXGbXi/message: 8686cb61f9bd3cd7ed100cc3af819458
GXGbXi/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
8AbRER/message: 3cb81c4829857556f5d117c9a23f7bcc
8AbRER/origin/0/0: 81b173297e9c022d9ebde5f7018c95f9
8AbRER/origin/1/0: 0eeeb7e68164ff118040918f1ef00a7a
8AbRER/origin/2/0: 2201bf679c663a732d7f1b3c3ce3897f
8AbRER/translation: 3cb81c4829857556f5d117c9a23f7bcc
2gHjVM/translation: 873c4eb14a7e32e7fd7793047b6028a1
2gHjVM/message: 873c4eb14a7e32e7fd7793047b6028a1
2gHjVM/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
2gHjVM/origin/1/0: 627b72cd19a7154ae1c28cbd7ca0c730
2gHjVM/origin/2/0: 0627e0040ca4939c9a57349e98c51797
2gHjVM/origin/3/0: 0627e0040ca4939c9a57349e98c51797
Eit43O/message: b5d9a1edd988f455041aee9fa7fb3d63
Eit43O/origin/0/0: 1d6b5f88cdad160a67842f6b65387448
Eit43O/translation: b5d9a1edd988f455041aee9fa7fb3d63
@@ -2452,9 +2471,9 @@ checksums:
kp6L3T/message: b7eadb89c615874f416d9658d0428c4c
kp6L3T/origin/0/0: 30791e26a172d47d3cb0693e82679246
kp6L3T/translation: b7eadb89c615874f416d9658d0428c4c
pEb95p/translation: 1d4c2a0ec7e4c707a4470fa4262b495d
pEb95p/message: 1d4c2a0ec7e4c707a4470fa4262b495d
pEb95p/origin/0/0: 1b05beba6bdd908015fa8e798717f024
pEb95p/translation: 1d4c2a0ec7e4c707a4470fa4262b495d
MHrjPM/message: 344e64395eaff6822a57d18623853e1a
MHrjPM/origin/0/0: a50c4fbde8da6e0a77c4e0aef0b44dea
MHrjPM/translation: 344e64395eaff6822a57d18623853e1a
@@ -2502,9 +2521,9 @@ checksums:
LeM5RY/message: dfeac280858332082ad34d2623a59863
LeM5RY/origin/0/0: 0dccc39d0debbf3e089cb6d292de762b
LeM5RY/translation: dfeac280858332082ad34d2623a59863
5MPY04/translation: 661f2e21a85fbe1281b141a8041c8778
5MPY04/message: 661f2e21a85fbe1281b141a8041c8778
5MPY04/origin/0/0: 1b05beba6bdd908015fa8e798717f024
5MPY04/translation: 661f2e21a85fbe1281b141a8041c8778
W1ewR0/message: ddcac5d14d48358b043a9487b42eec16
W1ewR0/origin/0/0: fc0c18a095a68277a0f90a115942683b
W1ewR0/origin/1/0: 1b05beba6bdd908015fa8e798717f024
@@ -2951,12 +2970,18 @@ checksums:
MdN5zD/message: e2e816f2b7a13b1056d79e7f25088664
MdN5zD/origin/0/0: 0dccc39d0debbf3e089cb6d292de762b
MdN5zD/translation: e2e816f2b7a13b1056d79e7f25088664
ZhQoZn/translation: b05a1191bf048d76e7d6c1c3e76ce1fd
ZhQoZn/message: b05a1191bf048d76e7d6c1c3e76ce1fd
ZhQoZn/origin/0/0: 0627e0040ca4939c9a57349e98c51797
2SePRT/message: 257b840726f972f384243a72767f880f
2SePRT/origin/0/0: 6cda8b872e4902cbc0191d9375adf4ef
2SePRT/translation: 257b840726f972f384243a72767f880f
uOqaMC/message: 24fc6cdc8740f37a83df85f582f03293
uOqaMC/origin/0/0: 6cda8b872e4902cbc0191d9375adf4ef
uOqaMC/translation: 24fc6cdc8740f37a83df85f582f03293
8Zjc7q/translation: 65609a209e74f230563a7c8d7258a83c
8Zjc7q/message: 65609a209e74f230563a7c8d7258a83c
8Zjc7q/origin/0/0: 278a40eb45b1219f424703d0abc1d273
GoDLB9/message: 8c8d944e07388c5877effdb2c2803dcf
GoDLB9/origin/0/0: 5c60161e173f5eae9276d8e4c70b4ebb
GoDLB9/translation: 8c8d944e07388c5877effdb2c2803dcf
@@ -2984,6 +3009,9 @@ checksums:
Ozt2vv/message: 8f730f68d52b7efa727aa0784282ebcb
Ozt2vv/origin/0/0: 0627e0040ca4939c9a57349e98c51797
Ozt2vv/translation: 8f730f68d52b7efa727aa0784282ebcb
EuK5ez/translation: d262a1ba9ebeea549d982f9cf85a6c18
EuK5ez/message: d262a1ba9ebeea549d982f9cf85a6c18
EuK5ez/origin/0/0: 0627e0040ca4939c9a57349e98c51797
"%2FtOdd2/message": 099e1780348cd7bf617721344371a430
"%2FtOdd2/origin/0/0": 03c00909b41be01231cd3e843c8ee44e
"%2FtOdd2/translation": 099e1780348cd7bf617721344371a430

View File

@@ -1,6 +1,7 @@
import { useTheme } from "next-themes";
import { useRouter } from "next/navigation";
import { t } from "@lingui/core/macro";
import { env } from "next-runtime-env";
import { useTheme } from "next-themes";
import { useEffect, useRef, useState } from "react";
import {
TbLayoutSidebarLeftCollapse,
@@ -15,6 +16,9 @@ import { useClickOutside } from "~/hooks/useClickOutside";
import { useModal } from "~/providers/modal";
import { useWorkspace, WorkspaceProvider } from "~/providers/workspace";
import { api } from "~/utils/api";
import { ChangePasswordFormConfirmation } from "~/views/settings/components/ChangePasswordConfirmation";
import Button from "./Button";
import Modal from "./modal";
import SideNavigation from "./SideNavigation";
interface DashboardProps {
@@ -43,7 +47,7 @@ export default function Dashboard({
hasRightPanel = false,
}: DashboardProps) {
const { resolvedTheme } = useTheme();
const { openModal } = useModal();
const { openModal, closeModal, modalContentType } = useModal();
const { availableWorkspaces, hasLoaded } = useWorkspace();
const router = useRouter();
@@ -102,13 +106,32 @@ export default function Dashboard({
useEffect(() => {
if (hasLoaded && availableWorkspaces.length === 0) {
if (env("NEXT_PUBLIC_KAN_ENV") === "cloud") {
router.push(`/onboarding/select-plan?returnUrl=${encodeURIComponent(window.location.pathname)}`);
router.push(
`/onboarding/select-plan?returnUrl=${encodeURIComponent(window.location.pathname)}`,
);
} else {
openModal("NEW_WORKSPACE", undefined, undefined, false);
}
}
}, [hasLoaded, availableWorkspaces.length, openModal, router]);
useEffect(() => {
const isCredentialsEnabled =
env("NEXT_PUBLIC_ALLOW_CREDENTIALS")?.toLowerCase() === "true";
if (
!userLoading &&
user &&
isCredentialsEnabled &&
user.hasMagicLinkAccount &&
!user.hasPassword &&
!sessionStorage.getItem("set_password_prompted")
) {
sessionStorage.setItem("set_password_prompted", "1");
openModal("SET_PASSWORD_PROMPT");
}
}, [user, userLoading, openModal]);
const isDarkMode = resolvedTheme === "dark";
return (
@@ -203,6 +226,30 @@ export default function Dashboard({
</div>
</div>
</div>
<Modal
modalSize="sm"
isVisible={modalContentType === "SET_PASSWORD_PROMPT"}
>
{user?.hasPassword ? (
<div className="p-5">
<h2 className="pb-4 text-base font-medium dark:text-white">{t`Password already set`}</h2>
<p className="mb-6 text-sm text-light-900">
{t`Your account already has a password. You can change it from your account settings.`}
</p>
<Button
variant="secondary"
onClick={closeModal}
fullWidth
size="lg"
>
{t`Close`}
</Button>
</div>
) : (
<ChangePasswordFormConfirmation hasPassword={false} />
)}
</Modal>
</>
);
}

View File

@@ -552,7 +552,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 90]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
],
"translation": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut."
},
@@ -1271,7 +1271,7 @@
["src/views/members/components/DeleteMemberConfirmation.tsx", 55],
["src/views/onboarding/select-plan/index.tsx", 209],
["src/views/settings/components/Avatar.tsx", 287],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 168],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 206],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
40
@@ -1333,9 +1333,9 @@
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Passwort ändern"
},
@@ -1462,6 +1462,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/components/Dashboard.tsx", 246],
["src/views/card/index.tsx", 367],
["src/views/members/components/EditMemberPermissionsModal.tsx", 172],
["src/views/settings/components/NewApiKeyModal.tsx", 136]
@@ -1581,7 +1582,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 150]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 188]
],
"translation": "Bestätigen Sie Ihr neues Passwort"
},
@@ -1751,11 +1752,11 @@
"translation": "Mitglieder kopieren"
},
"7mrkyO": {
"translation": "Ticket-ID kopieren",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 87]]
"origin": [["src/views/card/components/Dropdown.tsx", 87]],
"translation": "Ticket-ID kopieren"
},
"9PaIxv": {
"message": "Core features",
@@ -2521,7 +2522,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 159]
],
"translation": "Geben Sie Ihr aktuelles Passwort ein"
},
@@ -2530,7 +2531,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 111]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 146]
],
"translation": "Geben Sie Ihr aktuelles Passwort ein und wählen Sie ein neues sicheres Passwort."
},
@@ -2553,7 +2554,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 136]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 174]
],
"translation": "Geben Sie Ihr neues Passwort ein"
},
@@ -2590,7 +2591,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 89]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 120]
],
"translation": "Fehler beim Ändern des Passworts"
},
@@ -2664,6 +2665,15 @@
],
"translation": "Fehler beim Einladen des Mitglieds"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 121]
],
"translation": "Fehler beim Festlegen des Passworts"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -3283,11 +3293,11 @@
"translation": "Ich bestätige, dass alle Workspace-Daten dauerhaft gelöscht werden und möchte fortfahren."
},
"VOwhhz": {
"translation": "ID kopiert",
"message": "ID copied",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 64]]
"origin": [["src/views/card/components/Dropdown.tsx", 64]],
"translation": "ID kopiert"
},
"iwr7AP": {
"message": "Ideas",
@@ -4021,7 +4031,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
],
"translation": "Neues Passwort ist erforderlich"
},
@@ -4030,7 +4040,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 31]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 36]
],
"translation": "Neues Passwort muss sich vom aktuellen Passwort unterscheiden"
},
@@ -4311,12 +4321,19 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Teilzeit"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 236]],
"translation": "Passwort bereits festgelegt"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 69]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Passwort geändert"
},
@@ -4339,16 +4356,25 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 22]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
],
"translation": "Passwort muss mindestens 8 Zeichen lang sein"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Passwort festgelegt"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 27]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 30]
],
"translation": "Passwörter stimmen nicht überein"
},
@@ -4480,7 +4506,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 25]
],
"translation": "Bitte bestätigen Sie Ihr neues Passwort"
},
@@ -5119,6 +5145,13 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Senior"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 93]],
"translation": "Legen Sie ein Passwort fest, um die passwortbasierte Anmeldung zu aktivieren."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -5130,6 +5163,18 @@
],
"translation": "Fälligkeitsdatum festlegen"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Passwort festlegen"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -5587,7 +5632,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 84]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 114]
],
"translation": "Das eingegebene aktuelle Passwort ist falsch."
},
@@ -5772,11 +5817,11 @@
"translation": "Dieser Workspace-Benutzername ist bereits vergeben"
},
"pEb95p": {
"translation": "Ticket-ID in die Zwischenablage kopiert",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 66]]
"origin": [["src/views/card/components/Dropdown.tsx", 66]],
"translation": "Ticket-ID in die Zwischenablage kopiert"
},
"MHrjPM": {
"message": "Title",
@@ -5892,11 +5937,11 @@
"translation": "Überschreibungen konnten nicht gelöscht werden"
},
"5MPY04": {
"translation": "ID konnte nicht kopiert werden",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 71]]
"origin": [["src/views/card/components/Dropdown.tsx", 71]],
"translation": "ID konnte nicht kopiert werden"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -6869,7 +6914,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 92]],
"translation": "Sie sind dabei, Ihr Passwort zu ändern."
},
"3WXdoZ": {
@@ -6951,6 +6996,15 @@
"origin": [["src/views/settings/PermissionsSettings.tsx", 86]],
"translation": "Sie müssen Administrator sein, um Workspace-Berechtigungen zu verwalten."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 147]
],
"translation": "Sie haben sich ohne Passwort angemeldet. Legen Sie ein Passwort fest, um die passwortbasierte Anmeldung zu aktivieren."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -6965,6 +7019,13 @@
"origin": [["src/views/invite/index.tsx", 135]],
"translation": "Sie wurden eingeladen, einem Workspace beizutreten."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 238]],
"translation": "Ihr Konto hat bereits ein Passwort. Sie können es in Ihren Kontoeinstellungen ändern."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -7028,10 +7089,19 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 70]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 95]
],
"translation": "Ihr Passwort wurde geändert."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 96]
],
"translation": "Ihr Passwort wurde festgelegt."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -933,7 +933,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
90
122
]
],
"translation": "An unexpected error occurred. Please try again later."
@@ -2133,7 +2133,7 @@
],
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
168
206
],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
@@ -2241,15 +2241,15 @@
],
[
"src/views/settings/AccountSettings.tsx",
98
100
],
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
109
142
],
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
178
216
]
],
"translation": "Change Password"
@@ -2443,6 +2443,10 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/components/Dashboard.tsx",
246
],
[
"src/views/card/index.tsx",
367
@@ -2645,7 +2649,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
150
188
]
],
"translation": "Confirm your new password"
@@ -2923,7 +2927,6 @@
"translation": "Copy members"
},
"7mrkyO": {
"translation": "Copy ticket ID",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
@@ -2932,7 +2935,8 @@
"src/views/card/components/Dropdown.tsx",
87
]
]
],
"translation": "Copy ticket ID"
},
"9PaIxv": {
"message": "Core features",
@@ -4246,7 +4250,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
122
159
]
],
"translation": "Enter your current password"
@@ -4258,7 +4262,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
111
146
]
],
"translation": "Enter your current password and choose a new secure password."
@@ -4294,7 +4298,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
136
174
]
],
"translation": "Enter your new password"
@@ -4354,7 +4358,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
89
120
]
],
"translation": "Error Changing Password"
@@ -4471,6 +4475,18 @@
],
"translation": "Error inviting member"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
121
]
],
"translation": "Error Setting Password"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -5517,7 +5533,6 @@
"translation": "I acknowledge that all of the workspace data will be permanently deleted and want to proceed."
},
"VOwhhz": {
"translation": "ID copied",
"message": "ID copied",
"placeholders": {},
"comments": [],
@@ -5526,7 +5541,8 @@
"src/views/card/components/Dropdown.tsx",
64
]
]
],
"translation": "ID copied"
},
"iwr7AP": {
"message": "Ideas",
@@ -6778,7 +6794,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
23
24
]
],
"translation": "New password is required"
@@ -6790,7 +6806,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
31
36
]
],
"translation": "New password must be different from current password"
@@ -7263,6 +7279,18 @@
],
"translation": "Part-time"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [
[
"src/components/Dashboard.tsx",
236
]
],
"translation": "Password already set"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
@@ -7270,7 +7298,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
69
93
]
],
"translation": "Password Changed"
@@ -7306,11 +7334,23 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
22
23
]
],
"translation": "Password must be at least 8 characters"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
93
]
],
"translation": "Password Set"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
@@ -7318,7 +7358,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
27
30
]
],
"translation": "Passwords do not match"
@@ -7534,7 +7574,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
24
25
]
],
"translation": "Please confirm your new password"
@@ -8738,6 +8778,18 @@
],
"translation": "Senior"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/settings/AccountSettings.tsx",
93
]
],
"translation": "Set a password to enable password-based login."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -8758,6 +8810,30 @@
],
"translation": "Set due date"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/settings/AccountSettings.tsx",
88
],
[
"src/views/settings/AccountSettings.tsx",
100
],
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
142
],
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
216
]
],
"translation": "Set Password"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -9509,7 +9585,7 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
84
114
]
],
"translation": "The current password you entered is incorrect."
@@ -9803,7 +9879,6 @@
"translation": "This workspace username has already been taken"
},
"pEb95p": {
"translation": "Ticket ID copied to clipboard",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
@@ -9812,7 +9887,8 @@
"src/views/card/components/Dropdown.tsx",
66
]
]
],
"translation": "Ticket ID copied to clipboard"
},
"MHrjPM": {
"message": "Title",
@@ -10003,7 +10079,6 @@
"translation": "Unable to clear overrides"
},
"5MPY04": {
"translation": "Unable to copy ID",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
@@ -10012,7 +10087,8 @@
"src/views/card/components/Dropdown.tsx",
71
]
]
],
"translation": "Unable to copy ID"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -11646,7 +11722,7 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
91
92
]
],
"translation": "You are about to change your password."
@@ -11799,6 +11875,18 @@
],
"translation": "You need to be an admin to manage workspace permissions."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
147
]
],
"translation": "You signed in without a password. Set a password to enable password-based login."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -11823,6 +11911,18 @@
],
"translation": "You've been invited to join a workspace."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [
[
"src/components/Dashboard.tsx",
238
]
],
"translation": "Your account already has a password. You can change it from your account settings."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -11926,11 +12026,23 @@
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
70
95
]
],
"translation": "Your password has been changed."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
96
]
],
"translation": "Your password has been set."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -552,7 +552,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 90]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
],
"translation": "Se produjo un error inesperado. Por favor, inténtalo de nuevo más tarde."
},
@@ -1271,7 +1271,7 @@
["src/views/members/components/DeleteMemberConfirmation.tsx", 55],
["src/views/onboarding/select-plan/index.tsx", 209],
["src/views/settings/components/Avatar.tsx", 287],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 168],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 206],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
40
@@ -1333,9 +1333,9 @@
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Cambiar contraseña"
},
@@ -1462,6 +1462,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/components/Dashboard.tsx", 246],
["src/views/card/index.tsx", 367],
["src/views/members/components/EditMemberPermissionsModal.tsx", 172],
["src/views/settings/components/NewApiKeyModal.tsx", 136]
@@ -1581,7 +1582,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 150]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 188]
],
"translation": "Confirma tu nueva contraseña"
},
@@ -1751,11 +1752,11 @@
"translation": "Copiar miembros"
},
"7mrkyO": {
"translation": "Copiar ID del ticket",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 87]]
"origin": [["src/views/card/components/Dropdown.tsx", 87]],
"translation": "Copiar ID del ticket"
},
"9PaIxv": {
"message": "Core features",
@@ -2521,7 +2522,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 159]
],
"translation": "Introduce tu contraseña actual"
},
@@ -2530,7 +2531,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 111]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 146]
],
"translation": "Introduce tu contraseña actual y elige una nueva contraseña segura."
},
@@ -2553,7 +2554,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 136]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 174]
],
"translation": "Introduce tu nueva contraseña"
},
@@ -2590,7 +2591,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 89]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 120]
],
"translation": "Error al cambiar la contraseña"
},
@@ -2664,6 +2665,15 @@
],
"translation": "Error al invitar al miembro"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 121]
],
"translation": "Error al Establecer la Contraseña"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -3283,11 +3293,11 @@
"translation": "Reconozco que todos los datos del espacio de trabajo se eliminarán permanentemente y quiero continuar."
},
"VOwhhz": {
"translation": "ID copiado",
"message": "ID copied",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 64]]
"origin": [["src/views/card/components/Dropdown.tsx", 64]],
"translation": "ID copiado"
},
"iwr7AP": {
"message": "Ideas",
@@ -4021,7 +4031,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
],
"translation": "Se requiere una nueva contraseña"
},
@@ -4030,7 +4040,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 31]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 36]
],
"translation": "La nueva contraseña debe ser diferente de la contraseña actual"
},
@@ -4311,12 +4321,19 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Tiempo parcial"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 236]],
"translation": "Contraseña ya establecida"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 69]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Contraseña cambiada"
},
@@ -4339,16 +4356,25 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 22]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
],
"translation": "La contraseña debe tener al menos 8 caracteres"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Contraseña Establecida"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 27]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 30]
],
"translation": "Las contraseñas no coinciden"
},
@@ -4480,7 +4506,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 25]
],
"translation": "Por favor, confirma tu nueva contraseña"
},
@@ -5119,6 +5145,13 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Senior"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 93]],
"translation": "Establece una contraseña para habilitar el inicio de sesión con contraseña."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -5130,6 +5163,18 @@
],
"translation": "Establecer fecha de vencimiento"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Establecer Contraseña"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -5587,7 +5632,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 84]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 114]
],
"translation": "La contraseña actual que ingresaste es incorrecta."
},
@@ -5772,11 +5817,11 @@
"translation": "Este nombre de usuario de espacio de trabajo ya está en uso"
},
"pEb95p": {
"translation": "ID del ticket copiado al portapapeles",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 66]]
"origin": [["src/views/card/components/Dropdown.tsx", 66]],
"translation": "ID del ticket copiado al portapapeles"
},
"MHrjPM": {
"message": "Title",
@@ -5892,11 +5937,11 @@
"translation": "No se pudieron borrar las anulaciones"
},
"5MPY04": {
"translation": "No se pudo copiar el ID",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 71]]
"origin": [["src/views/card/components/Dropdown.tsx", 71]],
"translation": "No se pudo copiar el ID"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -6869,7 +6914,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 92]],
"translation": "Estás a punto de cambiar tu contraseña."
},
"3WXdoZ": {
@@ -6951,6 +6996,15 @@
"origin": [["src/views/settings/PermissionsSettings.tsx", 86]],
"translation": "Necesitas ser administrador para gestionar los permisos del espacio de trabajo."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 147]
],
"translation": "Iniciaste sesión sin contraseña. Establece una contraseña para habilitar el inicio de sesión con contraseña."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -6965,6 +7019,13 @@
"origin": [["src/views/invite/index.tsx", 135]],
"translation": "Has sido invitado a unirte a un espacio de trabajo."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 238]],
"translation": "Tu cuenta ya tiene una contraseña. Puedes cambiarla desde la configuración de tu cuenta."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -7028,10 +7089,19 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 70]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 95]
],
"translation": "Tu contraseña ha sido cambiada."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 96]
],
"translation": "Tu contraseña ha sido establecida."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -552,7 +552,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 90]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
],
"translation": "Une erreur inattendue s'est produite. Veuillez réessayer ultérieurement."
},
@@ -1271,7 +1271,7 @@
["src/views/members/components/DeleteMemberConfirmation.tsx", 55],
["src/views/onboarding/select-plan/index.tsx", 209],
["src/views/settings/components/Avatar.tsx", 287],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 168],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 206],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
40
@@ -1333,9 +1333,9 @@
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Changer le mot de passe"
},
@@ -1462,6 +1462,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/components/Dashboard.tsx", 246],
["src/views/card/index.tsx", 367],
["src/views/members/components/EditMemberPermissionsModal.tsx", 172],
["src/views/settings/components/NewApiKeyModal.tsx", 136]
@@ -1581,7 +1582,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 150]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 188]
],
"translation": "Confirmez votre nouveau mot de passe"
},
@@ -1751,11 +1752,11 @@
"translation": "Copier les membres"
},
"7mrkyO": {
"translation": "Copier l'ID du ticket",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 87]]
"origin": [["src/views/card/components/Dropdown.tsx", 87]],
"translation": "Copier l'ID du ticket"
},
"9PaIxv": {
"message": "Core features",
@@ -2521,7 +2522,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 159]
],
"translation": "Saisissez votre mot de passe actuel"
},
@@ -2530,7 +2531,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 111]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 146]
],
"translation": "Saisissez votre mot de passe actuel et choisissez un nouveau mot de passe sécurisé."
},
@@ -2553,7 +2554,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 136]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 174]
],
"translation": "Saisissez votre nouveau mot de passe"
},
@@ -2590,7 +2591,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 89]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 120]
],
"translation": "Erreur lors du changement de mot de passe"
},
@@ -2664,6 +2665,15 @@
],
"translation": "Erreur lors de l'invitation du membre"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 121]
],
"translation": "Erreur lors de la définition du mot de passe"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -3283,11 +3293,11 @@
"translation": "Je reconnais que toutes les données de l'espace de travail seront définitivement supprimées et je souhaite continuer."
},
"VOwhhz": {
"translation": "ID copié",
"message": "ID copied",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 64]]
"origin": [["src/views/card/components/Dropdown.tsx", 64]],
"translation": "ID copié"
},
"iwr7AP": {
"message": "Ideas",
@@ -4021,7 +4031,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
],
"translation": "Le nouveau mot de passe est requis"
},
@@ -4030,7 +4040,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 31]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 36]
],
"translation": "Le nouveau mot de passe doit être différent du mot de passe actuel"
},
@@ -4311,12 +4321,19 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Temps partiel"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 236]],
"translation": "Mot de passe déjà défini"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 69]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Mot de passe modifié"
},
@@ -4339,16 +4356,25 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 22]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
],
"translation": "Le mot de passe doit contenir au moins 8 caractères"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Mot de passe défini"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 27]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 30]
],
"translation": "Les mots de passe ne correspondent pas"
},
@@ -4480,7 +4506,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 25]
],
"translation": "Veuillez confirmer votre nouveau mot de passe"
},
@@ -5119,6 +5145,13 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Senior"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 93]],
"translation": "Définissez un mot de passe pour activer la connexion par mot de passe."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -5130,6 +5163,18 @@
],
"translation": "Définir la date d'échéance"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Définir un mot de passe"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -5587,7 +5632,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 84]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 114]
],
"translation": "Le mot de passe actuel que vous avez saisi est incorrect."
},
@@ -5772,11 +5817,11 @@
"translation": "Ce nom d'utilisateur d'espace de travail est déjà pris"
},
"pEb95p": {
"translation": "ID du ticket copié dans le presse-papiers",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 66]]
"origin": [["src/views/card/components/Dropdown.tsx", 66]],
"translation": "ID du ticket copié dans le presse-papiers"
},
"MHrjPM": {
"message": "Title",
@@ -5892,11 +5937,11 @@
"translation": "Impossible d'effacer les remplacements"
},
"5MPY04": {
"translation": "Impossible de copier l'ID",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 71]]
"origin": [["src/views/card/components/Dropdown.tsx", 71]],
"translation": "Impossible de copier l'ID"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -6869,7 +6914,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 92]],
"translation": "Vous êtes sur le point de modifier votre mot de passe."
},
"3WXdoZ": {
@@ -6951,6 +6996,15 @@
"origin": [["src/views/settings/PermissionsSettings.tsx", 86]],
"translation": "Vous devez être administrateur pour gérer les permissions de l'espace de travail."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 147]
],
"translation": "Vous vous êtes connecté sans mot de passe. Définissez un mot de passe pour activer la connexion par mot de passe."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -6965,6 +7019,13 @@
"origin": [["src/views/invite/index.tsx", 135]],
"translation": "Vous avez été invité à rejoindre un espace de travail."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 238]],
"translation": "Votre compte possède déjà un mot de passe. Vous pouvez le modifier depuis les paramètres de votre compte."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -7028,10 +7089,19 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 70]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 95]
],
"translation": "Votre mot de passe a été modifié."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 96]
],
"translation": "Votre mot de passe a été défini."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -552,7 +552,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 90]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
],
"translation": "Si è verificato un errore imprevisto. Riprova più tardi."
},
@@ -1271,7 +1271,7 @@
["src/views/members/components/DeleteMemberConfirmation.tsx", 55],
["src/views/onboarding/select-plan/index.tsx", 209],
["src/views/settings/components/Avatar.tsx", 287],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 168],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 206],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
40
@@ -1333,9 +1333,9 @@
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Cambia password"
},
@@ -1462,6 +1462,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/components/Dashboard.tsx", 246],
["src/views/card/index.tsx", 367],
["src/views/members/components/EditMemberPermissionsModal.tsx", 172],
["src/views/settings/components/NewApiKeyModal.tsx", 136]
@@ -1581,7 +1582,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 150]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 188]
],
"translation": "Conferma la tua nuova password"
},
@@ -1751,11 +1752,11 @@
"translation": "Copia membri"
},
"7mrkyO": {
"translation": "Copia ID ticket",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 87]]
"origin": [["src/views/card/components/Dropdown.tsx", 87]],
"translation": "Copia ID ticket"
},
"9PaIxv": {
"message": "Core features",
@@ -2521,7 +2522,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 159]
],
"translation": "Inserisci la tua password attuale"
},
@@ -2530,7 +2531,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 111]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 146]
],
"translation": "Inserisci la tua password attuale e scegli una nuova password sicura."
},
@@ -2553,7 +2554,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 136]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 174]
],
"translation": "Inserisci la tua nuova password"
},
@@ -2590,7 +2591,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 89]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 120]
],
"translation": "Errore durante la modifica della password"
},
@@ -2664,6 +2665,15 @@
],
"translation": "Errore durante l'invito del membro"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 121]
],
"translation": "Errore nell'impostazione della password"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -3283,11 +3293,11 @@
"translation": "Riconosco che tutti i dati del workspace verranno eliminati definitivamente e voglio procedere."
},
"VOwhhz": {
"translation": "ID copiato",
"message": "ID copied",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 64]]
"origin": [["src/views/card/components/Dropdown.tsx", 64]],
"translation": "ID copiato"
},
"iwr7AP": {
"message": "Ideas",
@@ -4021,7 +4031,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
],
"translation": "La nuova password è obbligatoria"
},
@@ -4030,7 +4040,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 31]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 36]
],
"translation": "La nuova password deve essere diversa da quella attuale"
},
@@ -4311,12 +4321,19 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Part-time"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 236]],
"translation": "Password già impostata"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 69]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Password modificata"
},
@@ -4339,16 +4356,25 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 22]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
],
"translation": "La password deve contenere almeno 8 caratteri"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Password impostata"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 27]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 30]
],
"translation": "Le password non corrispondono"
},
@@ -4480,7 +4506,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 25]
],
"translation": "Conferma la tua nuova password"
},
@@ -5119,6 +5145,13 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Senior"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 93]],
"translation": "Imposta una password per abilitare l'accesso tramite password."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -5130,6 +5163,18 @@
],
"translation": "Imposta scadenza"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Imposta password"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -5587,7 +5632,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 84]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 114]
],
"translation": "La password attuale inserita non è corretta."
},
@@ -5772,11 +5817,11 @@
"translation": "Questo nome utente dello workspace è già stato utilizzato"
},
"pEb95p": {
"translation": "ID ticket copiato negli appunti",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 66]]
"origin": [["src/views/card/components/Dropdown.tsx", 66]],
"translation": "ID ticket copiato negli appunti"
},
"MHrjPM": {
"message": "Title",
@@ -5892,11 +5937,11 @@
"translation": "Impossibile cancellare le sostituzioni"
},
"5MPY04": {
"translation": "Impossibile copiare l'ID",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 71]]
"origin": [["src/views/card/components/Dropdown.tsx", 71]],
"translation": "Impossibile copiare l'ID"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -6869,7 +6914,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 92]],
"translation": "Stai per modificare la tua password."
},
"3WXdoZ": {
@@ -6951,6 +6996,15 @@
"origin": [["src/views/settings/PermissionsSettings.tsx", 86]],
"translation": "Devi essere un amministratore per gestire i permessi del workspace."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 147]
],
"translation": "Hai effettuato l'accesso senza una password. Imposta una password per abilitare l'accesso tramite password."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -6965,6 +7019,13 @@
"origin": [["src/views/invite/index.tsx", 135]],
"translation": "Sei stato invitato a unirti a un workspace."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 238]],
"translation": "Il tuo account ha già una password. Puoi modificarla dalle impostazioni del tuo account."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -7028,10 +7089,19 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 70]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 95]
],
"translation": "La tua password è stata modificata."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 96]
],
"translation": "La tua password è stata impostata."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -552,7 +552,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 90]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
],
"translation": "Er is een onverwachte fout opgetreden. Probeer het later opnieuw."
},
@@ -1271,7 +1271,7 @@
["src/views/members/components/DeleteMemberConfirmation.tsx", 55],
["src/views/onboarding/select-plan/index.tsx", 209],
["src/views/settings/components/Avatar.tsx", 287],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 168],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 206],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
40
@@ -1333,9 +1333,9 @@
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Wachtwoord wijzigen"
},
@@ -1462,6 +1462,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/components/Dashboard.tsx", 246],
["src/views/card/index.tsx", 367],
["src/views/members/components/EditMemberPermissionsModal.tsx", 172],
["src/views/settings/components/NewApiKeyModal.tsx", 136]
@@ -1581,7 +1582,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 150]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 188]
],
"translation": "Bevestig je nieuwe wachtwoord"
},
@@ -1751,11 +1752,11 @@
"translation": "Leden kopiëren"
},
"7mrkyO": {
"translation": "Ticket-ID kopiëren",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 87]]
"origin": [["src/views/card/components/Dropdown.tsx", 87]],
"translation": "Ticket-ID kopiëren"
},
"9PaIxv": {
"message": "Core features",
@@ -2521,7 +2522,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 159]
],
"translation": "Voer je huidige wachtwoord in"
},
@@ -2530,7 +2531,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 111]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 146]
],
"translation": "Voer je huidige wachtwoord in en kies een nieuw veilig wachtwoord."
},
@@ -2553,7 +2554,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 136]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 174]
],
"translation": "Voer je nieuwe wachtwoord in"
},
@@ -2590,7 +2591,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 89]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 120]
],
"translation": "Fout bij wijzigen wachtwoord"
},
@@ -2664,6 +2665,15 @@
],
"translation": "Fout bij uitnodigen lid"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 121]
],
"translation": "Fout bij instellen wachtwoord"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -3283,11 +3293,11 @@
"translation": "Ik bevestig dat alle werkruimtegegevens permanent worden verwijderd en wil doorgaan."
},
"VOwhhz": {
"translation": "ID gekopieerd",
"message": "ID copied",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 64]]
"origin": [["src/views/card/components/Dropdown.tsx", 64]],
"translation": "ID gekopieerd"
},
"iwr7AP": {
"message": "Ideas",
@@ -4021,7 +4031,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
],
"translation": "Nieuw wachtwoord is verplicht"
},
@@ -4030,7 +4040,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 31]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 36]
],
"translation": "Nieuw wachtwoord moet verschillen van het huidige wachtwoord"
},
@@ -4311,12 +4321,19 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Parttime"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 236]],
"translation": "Wachtwoord is al ingesteld"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 69]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Wachtwoord gewijzigd"
},
@@ -4339,16 +4356,25 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 22]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
],
"translation": "Wachtwoord moet minimaal 8 tekens bevatten"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Wachtwoord ingesteld"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 27]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 30]
],
"translation": "Wachtwoorden komen niet overeen"
},
@@ -4480,7 +4506,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 25]
],
"translation": "Bevestig je nieuwe wachtwoord"
},
@@ -5119,6 +5145,13 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Senior"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 93]],
"translation": "Stel een wachtwoord in om inloggen met wachtwoord mogelijk te maken."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -5130,6 +5163,18 @@
],
"translation": "Vervaldatum instellen"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Wachtwoord instellen"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -5587,7 +5632,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 84]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 114]
],
"translation": "Het huidige wachtwoord dat je hebt ingevoerd is onjuist."
},
@@ -5772,11 +5817,11 @@
"translation": "Deze workspace-gebruikersnaam is al in gebruik"
},
"pEb95p": {
"translation": "Ticket-ID gekopieerd naar klembord",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 66]]
"origin": [["src/views/card/components/Dropdown.tsx", 66]],
"translation": "Ticket-ID gekopieerd naar klembord"
},
"MHrjPM": {
"message": "Title",
@@ -5892,11 +5937,11 @@
"translation": "Kan overschrijvingen niet wissen"
},
"5MPY04": {
"translation": "Kan ID niet kopiëren",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 71]]
"origin": [["src/views/card/components/Dropdown.tsx", 71]],
"translation": "Kan ID niet kopiëren"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -6869,7 +6914,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 92]],
"translation": "Je staat op het punt je wachtwoord te wijzigen."
},
"3WXdoZ": {
@@ -6951,6 +6996,15 @@
"origin": [["src/views/settings/PermissionsSettings.tsx", 86]],
"translation": "Je moet een beheerder zijn om werkruimte-rechten te beheren."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 147]
],
"translation": "U bent ingelogd zonder wachtwoord. Stel een wachtwoord in om inloggen met wachtwoord mogelijk te maken."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -6965,6 +7019,13 @@
"origin": [["src/views/invite/index.tsx", 135]],
"translation": "Je bent uitgenodigd om lid te worden van een werkruimte."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 238]],
"translation": "Uw account heeft al een wachtwoord. U kunt dit wijzigen via uw accountinstellingen."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -7028,10 +7089,19 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 70]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 95]
],
"translation": "Je wachtwoord is gewijzigd."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 96]
],
"translation": "Uw wachtwoord is ingesteld."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -552,7 +552,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 90]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
],
"translation": "Wystąpił nieoczekiwany błąd. Spróbuj ponownie później."
},
@@ -1271,7 +1271,7 @@
["src/views/members/components/DeleteMemberConfirmation.tsx", 55],
["src/views/onboarding/select-plan/index.tsx", 209],
["src/views/settings/components/Avatar.tsx", 287],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 168],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 206],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
40
@@ -1333,9 +1333,9 @@
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Zmień hasło"
},
@@ -1462,6 +1462,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/components/Dashboard.tsx", 246],
["src/views/card/index.tsx", 367],
["src/views/members/components/EditMemberPermissionsModal.tsx", 172],
["src/views/settings/components/NewApiKeyModal.tsx", 136]
@@ -1581,7 +1582,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 150]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 188]
],
"translation": "Potwierdź nowe hasło"
},
@@ -1751,11 +1752,11 @@
"translation": "Kopiuj członków"
},
"7mrkyO": {
"translation": "Skopiuj ID zgłoszenia",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 87]]
"origin": [["src/views/card/components/Dropdown.tsx", 87]],
"translation": "Skopiuj ID zgłoszenia"
},
"9PaIxv": {
"message": "Core features",
@@ -2521,7 +2522,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 159]
],
"translation": "Wprowadź swoje obecne hasło"
},
@@ -2530,7 +2531,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 111]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 146]
],
"translation": "Wprowadź swoje obecne hasło i wybierz nowe, bezpieczne hasło."
},
@@ -2553,7 +2554,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 136]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 174]
],
"translation": "Wprowadź nowe hasło"
},
@@ -2590,7 +2591,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 89]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 120]
],
"translation": "Błąd podczas zmiany hasła"
},
@@ -2664,6 +2665,15 @@
],
"translation": "Błąd podczas zapraszania członka"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 121]
],
"translation": "Błąd podczas ustawiania hasła"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -3283,11 +3293,11 @@
"translation": "Potwierdzam, że wszystkie dane przestrzeni roboczej zostaną trwale usunięte i chcę kontynuować."
},
"VOwhhz": {
"translation": "ID skopiowane",
"message": "ID copied",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 64]]
"origin": [["src/views/card/components/Dropdown.tsx", 64]],
"translation": "ID skopiowane"
},
"iwr7AP": {
"message": "Ideas",
@@ -4021,7 +4031,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
],
"translation": "Wymagane jest nowe hasło"
},
@@ -4030,7 +4040,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 31]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 36]
],
"translation": "Nowe hasło musi się różnić od obecnego"
},
@@ -4311,12 +4321,19 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Niepełny etat"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 236]],
"translation": "Hasło już ustawione"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 69]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Hasło zostało zmienione"
},
@@ -4339,16 +4356,25 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 22]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
],
"translation": "Hasło musi mieć co najmniej 8 znaków"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Hasło ustawione"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 27]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 30]
],
"translation": "Hasła nie są zgodne"
},
@@ -4480,7 +4506,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 25]
],
"translation": "Potwierdź nowe hasło"
},
@@ -5119,6 +5145,13 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Starszy"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 93]],
"translation": "Ustaw hasło, aby włączyć logowanie za pomocą hasła."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -5130,6 +5163,18 @@
],
"translation": "Ustaw termin"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Ustaw hasło"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -5587,7 +5632,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 84]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 114]
],
"translation": "Wprowadzone obecne hasło jest nieprawidłowe."
},
@@ -5772,11 +5817,11 @@
"translation": "Ta nazwa użytkownika workspace jest już zajęta"
},
"pEb95p": {
"translation": "ID zgłoszenia skopiowane do schowka",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 66]]
"origin": [["src/views/card/components/Dropdown.tsx", 66]],
"translation": "ID zgłoszenia skopiowane do schowka"
},
"MHrjPM": {
"message": "Title",
@@ -5892,11 +5937,11 @@
"translation": "Nie można wyczyścić nadpisań"
},
"5MPY04": {
"translation": "Nie udało się skopiować ID",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 71]]
"origin": [["src/views/card/components/Dropdown.tsx", 71]],
"translation": "Nie udało się skopiować ID"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -6869,7 +6914,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 92]],
"translation": "Zaraz zmienisz swoje hasło."
},
"3WXdoZ": {
@@ -6951,6 +6996,15 @@
"origin": [["src/views/settings/PermissionsSettings.tsx", 86]],
"translation": "Musisz być administratorem, aby zarządzać uprawnieniami przestrzeni roboczej."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 147]
],
"translation": "Zalogowałeś się bez hasła. Ustaw hasło, aby włączyć logowanie za pomocą hasła."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -6965,6 +7019,13 @@
"origin": [["src/views/invite/index.tsx", 135]],
"translation": "Otrzymałeś zaproszenie do przestrzeni roboczej."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 238]],
"translation": "Twoje konto ma już ustawione hasło. Możesz je zmienić w ustawieniach konta."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -7028,10 +7089,19 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 70]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 95]
],
"translation": "Twoje hasło zostało zmienione."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 96]
],
"translation": "Twoje hasło zostało ustawione."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -552,7 +552,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 90]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
],
"translation": "Ocorreu um erro inesperado. Por favor, tente novamente mais tarde."
},
@@ -1271,7 +1271,7 @@
["src/views/members/components/DeleteMemberConfirmation.tsx", 55],
["src/views/onboarding/select-plan/index.tsx", 209],
["src/views/settings/components/Avatar.tsx", 287],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 168],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 206],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
40
@@ -1333,9 +1333,9 @@
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Alterar senha"
},
@@ -1462,6 +1462,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/components/Dashboard.tsx", 246],
["src/views/card/index.tsx", 367],
["src/views/members/components/EditMemberPermissionsModal.tsx", 172],
["src/views/settings/components/NewApiKeyModal.tsx", 136]
@@ -1581,7 +1582,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 150]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 188]
],
"translation": "Confirme sua nova senha"
},
@@ -1751,11 +1752,11 @@
"translation": "Copiar membros"
},
"7mrkyO": {
"translation": "Copiar ID do ticket",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 87]]
"origin": [["src/views/card/components/Dropdown.tsx", 87]],
"translation": "Copiar ID do ticket"
},
"9PaIxv": {
"message": "Core features",
@@ -2521,7 +2522,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 159]
],
"translation": "Insira sua senha atual"
},
@@ -2530,7 +2531,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 111]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 146]
],
"translation": "Insira sua senha atual e escolha uma nova senha segura."
},
@@ -2553,7 +2554,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 136]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 174]
],
"translation": "Insira sua nova senha"
},
@@ -2590,7 +2591,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 89]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 120]
],
"translation": "Erro ao alterar senha"
},
@@ -2664,6 +2665,15 @@
],
"translation": "Erro ao convidar membro"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 121]
],
"translation": "Erro ao Definir Senha"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -3283,11 +3293,11 @@
"translation": "Reconheço que todos os dados do workspace serão permanentemente excluídos e desejo prosseguir."
},
"VOwhhz": {
"translation": "ID copiado",
"message": "ID copied",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 64]]
"origin": [["src/views/card/components/Dropdown.tsx", 64]],
"translation": "ID copiado"
},
"iwr7AP": {
"message": "Ideas",
@@ -4021,7 +4031,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
],
"translation": "A nova senha é obrigatória"
},
@@ -4030,7 +4040,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 31]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 36]
],
"translation": "A nova senha deve ser diferente da senha atual"
},
@@ -4311,12 +4321,19 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Meio período"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 236]],
"translation": "Senha já definida"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 69]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Senha alterada"
},
@@ -4339,16 +4356,25 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 22]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
],
"translation": "A senha deve ter pelo menos 8 caracteres"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Senha Definida"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 27]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 30]
],
"translation": "As senhas não coincidem"
},
@@ -4480,7 +4506,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 25]
],
"translation": "Confirme sua nova senha"
},
@@ -5119,6 +5145,13 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Sênior"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 93]],
"translation": "Defina uma senha para habilitar o login por senha."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -5130,6 +5163,18 @@
],
"translation": "Definir data de vencimento"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Definir Senha"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -5587,7 +5632,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 84]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 114]
],
"translation": "A senha atual que você digitou está incorreta."
},
@@ -5772,11 +5817,11 @@
"translation": "Este nome de usuário de workspace já está em uso"
},
"pEb95p": {
"translation": "ID do ticket copiado para a área de transferência",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 66]]
"origin": [["src/views/card/components/Dropdown.tsx", 66]],
"translation": "ID do ticket copiado para a área de transferência"
},
"MHrjPM": {
"message": "Title",
@@ -5892,11 +5937,11 @@
"translation": "Não foi possível limpar substituições"
},
"5MPY04": {
"translation": "Não foi possível copiar o ID",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 71]]
"origin": [["src/views/card/components/Dropdown.tsx", 71]],
"translation": "Não foi possível copiar o ID"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -6869,7 +6914,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 92]],
"translation": "Você está prestes a alterar sua senha."
},
"3WXdoZ": {
@@ -6951,6 +6996,15 @@
"origin": [["src/views/settings/PermissionsSettings.tsx", 86]],
"translation": "Você precisa ser um administrador para gerenciar as permissões do workspace."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 147]
],
"translation": "Você fez login sem uma senha. Defina uma senha para habilitar o login por senha."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -6965,6 +7019,13 @@
"origin": [["src/views/invite/index.tsx", 135]],
"translation": "Você foi convidado para entrar em um workspace."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 238]],
"translation": "Sua conta já possui uma senha. Você pode alterá-la nas configurações da sua conta."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -7028,10 +7089,19 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 70]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 95]
],
"translation": "Sua senha foi alterada."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 96]
],
"translation": "Sua senha foi definida."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -552,7 +552,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 90]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
],
"translation": "Произошла непредвиденная ошибка. Пожалуйста, попробуйте позже."
},
@@ -1271,7 +1271,7 @@
["src/views/members/components/DeleteMemberConfirmation.tsx", 55],
["src/views/onboarding/select-plan/index.tsx", 209],
["src/views/settings/components/Avatar.tsx", 287],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 168],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 206],
[
"src/views/settings/components/ClearCustomPermissionsConfirmation.tsx",
40
@@ -1333,9 +1333,9 @@
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Сменить пароль"
},
@@ -1462,6 +1462,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/components/Dashboard.tsx", 246],
["src/views/card/index.tsx", 367],
["src/views/members/components/EditMemberPermissionsModal.tsx", 172],
["src/views/settings/components/NewApiKeyModal.tsx", 136]
@@ -1581,7 +1582,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 150]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 188]
],
"translation": "Подтвердите новый пароль"
},
@@ -1751,11 +1752,11 @@
"translation": "Копировать участников"
},
"7mrkyO": {
"translation": "Скопировать ID задачи",
"message": "Copy ticket ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 87]]
"origin": [["src/views/card/components/Dropdown.tsx", 87]],
"translation": "Скопировать ID задачи"
},
"9PaIxv": {
"message": "Core features",
@@ -2521,7 +2522,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 122]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 159]
],
"translation": "Введите текущий пароль"
},
@@ -2530,7 +2531,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 111]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 146]
],
"translation": "Введите текущий пароль и выберите новый надёжный пароль."
},
@@ -2553,7 +2554,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 136]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 174]
],
"translation": "Введите новый пароль"
},
@@ -2590,7 +2591,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 89]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 120]
],
"translation": "Ошибка при смене пароля"
},
@@ -2664,6 +2665,15 @@
],
"translation": "Ошибка при приглашении участника"
},
"sZixXc": {
"message": "Error Setting Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 121]
],
"translation": "Ошибка установки пароля"
},
"sVBn4K": {
"message": "Error updating display name",
"placeholders": {},
@@ -3283,11 +3293,11 @@
"translation": "Я подтверждаю, что все данные рабочего пространства будут безвозвратно удалены, и хочу продолжить."
},
"VOwhhz": {
"translation": "ID скопирован",
"message": "ID copied",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 64]]
"origin": [["src/views/card/components/Dropdown.tsx", 64]],
"translation": "ID скопирован"
},
"iwr7AP": {
"message": "Ideas",
@@ -4021,7 +4031,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
],
"translation": "Требуется новый пароль"
},
@@ -4030,7 +4040,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 31]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 36]
],
"translation": "Новый пароль должен отличаться от текущего"
},
@@ -4311,12 +4321,19 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Частичная занятость"
},
"nG7mus": {
"message": "Password already set",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 236]],
"translation": "Пароль уже установлен"
},
"IrZaAn": {
"message": "Password Changed",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 69]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Пароль изменён"
},
@@ -4339,16 +4356,25 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 22]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 23]
],
"translation": "Пароль должен содержать не менее 8 символов"
},
"Xlkxmi": {
"message": "Password Set",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 93]
],
"translation": "Пароль установлен"
},
"lpIMne": {
"message": "Passwords do not match",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 27]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 30]
],
"translation": "Пароли не совпадают"
},
@@ -4480,7 +4506,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 24]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 25]
],
"translation": "Пожалуйста, подтвердите новый пароль"
},
@@ -5119,6 +5145,13 @@
"origin": [["src/views/boards/components/TemplateBoards.tsx", 82]],
"translation": "Старший"
},
"GXGbXi": {
"message": "Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 93]],
"translation": "Установите пароль, чтобы включить вход по паролю."
},
"8AbRER": {
"message": "Set due date",
"placeholders": {},
@@ -5130,6 +5163,18 @@
],
"translation": "Установить срок"
},
"2gHjVM": {
"message": "Set Password",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 100],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 142],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 216]
],
"translation": "Установить пароль"
},
"Eit43O": {
"message": "set the due date",
"placeholders": {},
@@ -5587,7 +5632,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 84]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 114]
],
"translation": "Введённый вами текущий пароль неверен."
},
@@ -5772,11 +5817,11 @@
"translation": "Это имя пользователя рабочей области уже занято"
},
"pEb95p": {
"translation": "ID задачи скопирован в буфер обмена",
"message": "Ticket ID copied to clipboard",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 66]]
"origin": [["src/views/card/components/Dropdown.tsx", 66]],
"translation": "ID задачи скопирован в буфер обмена"
},
"MHrjPM": {
"message": "Title",
@@ -5892,11 +5937,11 @@
"translation": "Не удалось сбросить переопределения"
},
"5MPY04": {
"translation": "Не удалось скопировать ID",
"message": "Unable to copy ID",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/Dropdown.tsx", 71]]
"origin": [["src/views/card/components/Dropdown.tsx", 71]],
"translation": "Не удалось скопировать ID"
},
"W1ewR0": {
"message": "Unable to copy link",
@@ -6869,7 +6914,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 92]],
"translation": "Вы собираетесь изменить свой пароль."
},
"3WXdoZ": {
@@ -6951,6 +6996,15 @@
"origin": [["src/views/settings/PermissionsSettings.tsx", 86]],
"translation": "Вам нужно быть администратором, чтобы управлять правами доступа рабочего пространства."
},
"ZhQoZn": {
"message": "You signed in without a password. Set a password to enable password-based login.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 147]
],
"translation": "Вы вошли без пароля. Установите пароль, чтобы включить вход по паролю."
},
"2SePRT": {
"message": "You've been invited to join a workspace on kan.bn.",
"placeholders": {},
@@ -6965,6 +7019,13 @@
"origin": [["src/views/invite/index.tsx", 135]],
"translation": "Вас пригласили присоединиться к рабочему пространству."
},
"8Zjc7q": {
"message": "Your account already has a password. You can change it from your account settings.",
"placeholders": {},
"comments": [],
"origin": [["src/components/Dashboard.tsx", 238]],
"translation": "В вашей учетной записи уже есть пароль. Вы можете изменить его в настройках учетной записи."
},
"GoDLB9": {
"message": "Your account has been deleted.",
"placeholders": {},
@@ -7028,10 +7089,19 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 70]
["src/views/settings/components/ChangePasswordConfirmation.tsx", 95]
],
"translation": "Ваш пароль был изменён."
},
"EuK5ez": {
"message": "Your password has been set.",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/ChangePasswordConfirmation.tsx", 96]
],
"translation": "Ваш пароль установлен."
},
"/tOdd2": {
"message": "Your profile image has been updated.",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
export type WorkspacePlan = "free" | "team" | "pro" | "enterprise";
export interface TierConfig {
plan: WorkspacePlan;
seats: number | null;
unlimitedSeats: boolean;
}
const TIER_MAP: Record<number, TierConfig> = {
1: { plan: "team", seats: 5, unlimitedSeats: false },
2: { plan: "pro", seats: 15, unlimitedSeats: false },
3: { plan: "pro", seats: null, unlimitedSeats: true },
};
export function tierConfig(tier: number): TierConfig {
const config = TIER_MAP[tier];
if (!config) throw new Error(`Unknown partner tier: ${tier}`);
return config;
}

View File

@@ -0,0 +1,168 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { createNextApiContext } from "@kan/api/trpc";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { withRateLimit } from "@kan/api/utils/rateLimit";
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { createLogger } from "@kan/logger";
import { tierConfig } from "./_utils";
const log = createLogger("api");
interface TokenResponse {
access_token: string;
token_type: string;
}
interface OAuthLicenseResponse {
license_key: string;
status: string;
}
interface LicenseDetailResponse {
license_key: string;
status: string;
tier: number;
}
async function exchangeCodeForToken(code: string): Promise<TokenResponse> {
const tokenUrl = process.env.PARTNER_TOKEN_URL;
const clientId = process.env.PARTNER_CLIENT_ID;
const clientSecret = process.env.PARTNER_CLIENT_SECRET;
const redirectUrl = process.env.PARTNER_REDIRECT_URL;
if (!tokenUrl) throw new Error("PARTNER_TOKEN_URL not configured");
if (!clientId) throw new Error("PARTNER_CLIENT_ID not configured");
if (!clientSecret) throw new Error("PARTNER_CLIENT_SECRET not configured");
if (!redirectUrl) throw new Error("PARTNER_REDIRECT_URL not configured");
const res = await fetch(tokenUrl, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code,
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUrl,
}),
});
if (!res.ok) throw new Error(`Token exchange failed: ${res.status}`);
return res.json() as Promise<TokenResponse>;
}
async function fetchOAuthLicense(
accessToken: string,
): Promise<OAuthLicenseResponse> {
const oauthLicenseUrl = process.env.PARTNER_OAUTH_LICENSE_URL;
if (!oauthLicenseUrl)
throw new Error("PARTNER_OAUTH_LICENSE_URL not configured");
const res = await fetch(`${oauthLicenseUrl}?access_token=${accessToken}`);
if (!res.ok) throw new Error(`OAuth license fetch failed: ${res.status}`);
return res.json() as Promise<OAuthLicenseResponse>;
}
async function fetchLicenseDetail(
licenseKey: string,
): Promise<LicenseDetailResponse> {
const licenseApiUrl = process.env.PARTNER_LICENSE_API_URL;
const apiKey = process.env.PARTNER_API_KEY;
if (!licenseApiUrl) throw new Error("PARTNER_LICENSE_API_URL not configured");
if (!apiKey) throw new Error("PARTNER_API_KEY not configured");
const apiKeyHeader = process.env.PARTNER_API_KEY_HEADER;
if (!apiKeyHeader) throw new Error("PARTNER_API_KEY_HEADER not configured");
const res = await fetch(`${licenseApiUrl}/${licenseKey}`, {
headers: { [apiKeyHeader]: apiKey },
});
if (!res.ok) throw new Error(`License detail fetch failed: ${res.status}`);
return res.json() as Promise<LicenseDetailResponse>;
}
export default withRateLimit(
{ points: 20, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "GET") {
return res.status(405).json({ message: "Method not allowed" });
}
const { code } = req.query;
if (!code || typeof code !== "string") {
return res.status(400).json({ message: "Missing code parameter" });
}
let license: LicenseDetailResponse;
try {
const tokenData = await exchangeCodeForToken(code);
const oauthLicense = await fetchOAuthLicense(tokenData.access_token);
license = await fetchLicenseDetail(oauthLicense.license_key);
} catch (err) {
log.error({ err }, "Partner OAuth flow failed");
return res.redirect(`/partner/activate?error=oauth_failed`);
}
const { db, user } = await createNextApiContext(req);
const cfg = tierConfig(license.tier);
const isActive = license.status === "active";
const status = isActive ? "active" : "inactive";
if (!user) {
await subscriptionRepo.upsertByPartnerLicenseKey(
db,
license.license_key,
{
plan: cfg.plan,
status,
partnerTier: license.tier,
seats: cfg.seats,
unlimitedSeats: cfg.unlimitedSeats,
},
);
return res.redirect(
`/partner/activate?license_key=${encodeURIComponent(license.license_key)}`,
);
}
const memberships = await workspaceRepo.getAllByUserId(db, user.id);
const workspace = memberships?.[0]?.workspace;
if (!workspace) {
await subscriptionRepo.upsertByPartnerLicenseKey(
db,
license.license_key,
{
plan: cfg.plan,
status,
partnerTier: license.tier,
seats: cfg.seats,
unlimitedSeats: cfg.unlimitedSeats,
},
);
return res.redirect(
`/onboarding?license_key=${encodeURIComponent(license.license_key)}`,
);
}
await subscriptionRepo.upsertByPartnerLicenseKey(db, license.license_key, {
plan: cfg.plan,
status,
partnerTier: license.tier,
seats: cfg.seats,
unlimitedSeats: cfg.unlimitedSeats,
referenceId: workspace.publicId,
});
if (isActive) {
await workspaceRepo.update(db, workspace.publicId, { plan: cfg.plan });
}
return res.redirect(`/?partner_activated=1`);
}),
);

View File

@@ -0,0 +1,64 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { createNextApiContext } from "@kan/api/trpc";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { withRateLimit } from "@kan/api/utils/rateLimit";
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
export default withRateLimit(
{ points: 20, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "GET") {
return res.status(405).json({ message: "Method not allowed" });
}
const { license_key } = req.query;
if (!license_key || typeof license_key !== "string") {
return res.redirect("/?partner_error=missing_license");
}
const { db, user } = await createNextApiContext(req);
if (!user) {
return res.redirect(
`/login?next=${encodeURIComponent(`/api/partner/link?license_key=${license_key}`)}`,
);
}
const sub = await subscriptionRepo.getByPartnerLicenseKey(db, license_key);
if (!sub) {
return res.redirect("/?partner_error=invalid_license");
}
if (sub.status !== "active") {
return res.redirect("/?partner_error=license_inactive");
}
const memberships = await workspaceRepo.getAllByUserId(db, user.id);
const workspace = memberships?.[0]?.workspace;
if (!workspace) {
return res.redirect(
`/onboarding?license_key=${encodeURIComponent(license_key)}`,
);
}
await subscriptionRepo.upsertByPartnerLicenseKey(db, license_key, {
plan: sub.plan,
status: sub.status,
partnerTier: sub.partnerTier ?? 1,
seats: sub.seats ?? null,
unlimitedSeats: sub.unlimitedSeats,
referenceId: workspace.publicId,
});
await workspaceRepo.update(db, workspace.publicId, {
plan: sub.plan as "free" | "team" | "pro" | "enterprise",
});
return res.redirect("/?partner_activated=1");
}),
);

View File

@@ -0,0 +1,192 @@
import { createHmac, timingSafeEqual } from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";
import type { Readable } from "node:stream";
import { createNextApiContext } from "@kan/api/trpc";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { createLogger } from "@kan/logger";
import { tierConfig } from "./_utils";
const log = createLogger("api");
async function buffer(readable: Readable) {
const chunks: Buffer[] = [];
for await (const chunk of readable) {
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
}
return Buffer.concat(chunks);
}
function verifySignature(
rawBody: string,
signature: string,
timestamp: string,
): boolean {
const secret = process.env.PARTNER_API_KEY;
if (!secret) return false;
const ts = Number(timestamp);
if (isNaN(ts) || Date.now() / 1000 - ts > 300) return false;
const payload = `${timestamp}.${rawBody}`;
const expected = createHmac("sha256", secret).update(payload).digest("hex");
try {
return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
} catch {
return false;
}
}
interface WebhookPayload {
event:
| "purchase"
| "activate"
| "deactivate"
| "upgrade"
| "downgrade"
| "migrate";
license_key: string;
license_status: string;
tier: number;
prev_license_key?: string;
}
export default withApiLogging(
async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "POST") {
return res.status(405).json({ message: "Method not allowed" });
}
const signatureHeader = process.env.PARTNER_SIGNATURE_HEADER;
const timestampHeader = process.env.PARTNER_TIMESTAMP_HEADER;
if (!signatureHeader || !timestampHeader) {
log.error("Webhook signature headers not configured");
return res.status(500).json({ message: "Server misconfiguration" });
}
const signature = req.headers[signatureHeader] as string | undefined;
const timestamp = req.headers[timestampHeader] as string | undefined;
if (!signature || !timestamp) {
return res.status(400).json({ message: "Missing signature headers" });
}
const buf = await buffer(req as unknown as Readable);
const rawBody = buf.toString("utf8");
if (!verifySignature(rawBody, signature, timestamp)) {
return res.status(401).json({ message: "Invalid signature" });
}
const payload = JSON.parse(rawBody) as WebhookPayload;
const { event, license_key, license_status, tier, prev_license_key } =
payload;
const { db } = await createNextApiContext(req);
switch (event) {
case "purchase": {
const cfg = tierConfig(tier);
await subscriptionRepo.upsertByPartnerLicenseKey(db, license_key, {
plan: cfg.plan,
status: license_status,
partnerTier: tier,
seats: cfg.seats,
unlimitedSeats: cfg.unlimitedSeats,
});
break;
}
case "activate": {
const cfg = tierConfig(tier);
await subscriptionRepo.upsertByPartnerLicenseKey(db, license_key, {
plan: cfg.plan,
status: "active",
partnerTier: tier,
seats: cfg.seats,
unlimitedSeats: cfg.unlimitedSeats,
});
break;
}
case "deactivate": {
const sub = await subscriptionRepo.getByPartnerLicenseKey(
db,
license_key,
);
if (sub) {
await subscriptionRepo.updateById(db, sub.id, {
plan: "free",
status: "inactive",
});
if (sub.referenceId) {
await workspaceRepo.update(db, sub.referenceId, { plan: "free" });
}
}
break;
}
case "upgrade":
case "downgrade": {
const sub = await subscriptionRepo.getByPartnerLicenseKey(
db,
license_key,
);
if (sub) {
const cfg = tierConfig(tier);
await subscriptionRepo.upsertByPartnerLicenseKey(db, license_key, {
plan: cfg.plan,
status: license_status,
partnerTier: tier,
seats: cfg.seats,
unlimitedSeats: cfg.unlimitedSeats,
});
if (sub.referenceId) {
await workspaceRepo.update(db, sub.referenceId, { plan: cfg.plan });
}
}
break;
}
case "migrate": {
if (prev_license_key) {
const sub = await subscriptionRepo.getByPartnerLicenseKey(
db,
prev_license_key,
);
if (sub) {
const cfg = tierConfig(tier);
await subscriptionRepo.upsertByPartnerLicenseKey(db, license_key, {
plan: cfg.plan,
status: sub.status,
partnerTier: tier,
seats: cfg.seats,
unlimitedSeats: cfg.unlimitedSeats,
referenceId: sub.referenceId ?? undefined,
});
await subscriptionRepo.updateById(db, sub.id, {
status: "inactive",
});
}
}
break;
}
default:
log.warn({ event }, "Unhandled partner webhook event");
}
return res.status(200).json({ success: true, event });
},
);
export const config = {
api: {
bodyParser: false,
},
};

View File

@@ -0,0 +1,98 @@
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { useEffect, useState } from "react";
import { authClient } from "@kan/auth/client";
import { Auth } from "~/components/AuthForm";
import { PageHead } from "~/components/PageHead";
import PatternedBackground from "~/components/PatternedBackground";
export default function PartnerActivatePage() {
const router = useRouter();
const searchParams = useSearchParams();
const licenseKey = searchParams.get("license_key");
const error = searchParams.get("error");
const { data: session, isPending } = authClient.useSession();
const [isMagicLinkSent, setIsMagicLinkSent] = useState(false);
const [magicLinkRecipient, setMagicLinkRecipient] = useState("");
useEffect(() => {
if (!isPending && session?.user && licenseKey) {
router.push(
`/api/partner/link?license_key=${encodeURIComponent(licenseKey)}`,
);
} else if (!isPending && session?.user && !licenseKey) {
router.push("/boards");
}
}, [session, isPending, licenseKey, router]);
if (isPending || (session?.user && licenseKey)) return null;
return (
<>
<PageHead title={t`Activate | kan.bn`} />
<main className="h-screen bg-light-100 pt-20 dark:bg-dark-50 sm:pt-0">
<div className="justify-top flex h-full flex-col items-center px-4 sm:justify-center">
<div className="z-10 flex w-full flex-col items-center">
<Link href="/">
<h1 className="mb-6 text-lg font-bold tracking-tight text-light-1000 dark:text-dark-1000">
kan.bn
</h1>
</Link>
<p className="mb-2 text-3xl font-bold tracking-tight text-light-1000 dark:text-dark-1000">
{isMagicLinkSent ? t`Check your inbox` : t`Activate your account`}
</p>
<p className="mb-10 text-sm text-light-800 dark:text-dark-800">
{isMagicLinkSent ? (
<Trans>We sent a link to {magicLinkRecipient}</Trans>
) : (
t`Sign in or create an account to activate your license`
)}
</p>
{error && (
<div className="mb-4 w-full rounded-md bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-900/20 dark:text-red-400 sm:max-w-md">
{t`Something went wrong during activation. Please try again.`}
</div>
)}
{!isMagicLinkSent && (
<div className="w-full rounded-lg border border-light-500 bg-light-300 px-4 py-10 dark:border-dark-400 dark:bg-dark-200 sm:max-w-md lg:px-10">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<Auth
setIsMagicLinkSent={(val, recipient) => {
setIsMagicLinkSent(val);
setMagicLinkRecipient(recipient);
}}
/>
</div>
</div>
)}
<p className="mt-4 text-sm text-light-1000 dark:text-dark-1000">
<Trans>
Already have an account?{" "}
<span className="underline">
<Link
href={
licenseKey
? `/login?next=${encodeURIComponent(`/api/partner/link?license_key=${licenseKey}`)}`
: "/login"
}
>
Sign in
</Link>
</span>
</Trans>
</p>
</div>
<PatternedBackground />
</div>
</main>
</>
);
}

View File

@@ -85,17 +85,19 @@ export default function AccountSettings() {
{isCredentialsEnabled && (
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
{t`Change Password`}
{data?.hasPassword ? t`Change Password` : t`Set Password`}
</h2>
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
{t`You are about to change your password.`}
{data?.hasPassword
? t`You are about to change your password.`
: t`Set a password to enable password-based login.`}
</p>
<div className="mt-4">
<Button
variant="secondary"
onClick={() => openModal("CHANGE_PASSWORD")}
>
{t`Change Password`}
{data?.hasPassword ? t`Change Password` : t`Set Password`}
</Button>
</div>
</div>
@@ -113,7 +115,7 @@ export default function AccountSettings() {
modalSize="sm"
isVisible={isOpen && modalContentType === "CHANGE_PASSWORD"}
>
<ChangePasswordFormConfirmation />
<ChangePasswordFormConfirmation hasPassword={data?.hasPassword ?? false} />
</Modal>
{/* Global modals */}

View File

@@ -2,7 +2,6 @@ import { useRouter } from "next/navigation";
import { zodResolver } from "@hookform/resolvers/zod";
import { t } from "@lingui/core/macro";
import { useMutation } from "@tanstack/react-query";
import { env } from "next-runtime-env";
import { useForm } from "react-hook-form";
import { z } from "zod";
@@ -14,27 +13,43 @@ import { useModal } from "~/providers/modal";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
const FormSchema = z
.object({
currentPassword: z.string().min(1, t`Current password is required`),
const buildSchema = (hasPassword: boolean) => {
const base = z.object({
currentPassword: hasPassword
? z.string().min(1, t`Current password is required`)
: z.string().optional(),
newPassword: z
.string()
.min(8, t`Password must be at least 8 characters`)
.min(1, t`New password is required`),
confirmPassword: z.string().min(1, t`Please confirm your new password`),
})
.refine((data) => data.newPassword === data.confirmPassword, {
message: t`Passwords do not match`,
path: ["confirmPassword"],
})
.refine((data) => data.currentPassword !== data.newPassword, {
message: t`New password must be different from current password`,
path: ["newPassword"],
});
type FormValues = z.infer<typeof FormSchema>;
return base
.refine((data) => data.newPassword === data.confirmPassword, {
message: t`Passwords do not match`,
path: ["confirmPassword"],
})
.refine(
(data) => !hasPassword || data.currentPassword !== data.newPassword,
{
message: t`New password must be different from current password`,
path: ["newPassword"],
},
);
};
export function ChangePasswordFormConfirmation() {
interface FormValues {
currentPassword?: string;
newPassword: string;
confirmPassword: string;
}
interface Props {
hasPassword: boolean;
}
export function ChangePasswordFormConfirmation({ hasPassword }: Props) {
const { closeModal } = useModal();
const { showPopup } = usePopup();
const router = useRouter();
@@ -47,15 +62,24 @@ export function ChangePasswordFormConfirmation() {
reset,
setError,
} = useForm<FormValues>({
resolver: zodResolver(FormSchema),
resolver: zodResolver(buildSchema(hasPassword)),
mode: "onChange",
});
const setPasswordMutation = api.user.setPassword.useMutation();
const changePasswordMutation = useMutation({
mutationFn: async (data: FormValues) => {
if (!hasPassword) {
await setPasswordMutation.mutateAsync({
newPassword: data.newPassword,
});
return;
}
const response = await authClient.changePassword({
newPassword: data.newPassword,
currentPassword: data.currentPassword,
currentPassword: data.currentPassword ?? "",
revokeOtherSessions: true,
});
@@ -66,12 +90,18 @@ export function ChangePasswordFormConfirmation() {
onSuccess: async () => {
closeModal();
showPopup({
header: t`Password Changed`,
message: t`Your password has been changed.`,
header: hasPassword ? t`Password Changed` : t`Password Set`,
message: hasPassword
? t`Your password has been changed.`
: t`Your password has been set.`,
icon: "success",
});
utils.invalidate();
if (!hasPassword) {
sessionStorage.removeItem("set_password_prompted");
}
utils.user.getUser.invalidate();
reset();
router.push("/");
},
@@ -86,7 +116,9 @@ export function ChangePasswordFormConfirmation() {
} else {
closeModal();
showPopup({
header: t`Error Changing Password`,
header: hasPassword
? t`Error Changing Password`
: t`Error Setting Password`,
message: t`An unexpected error occurred. Please try again later.`,
icon: "error",
});
@@ -106,27 +138,33 @@ export function ChangePasswordFormConfirmation() {
return (
<div className="p-5">
<div className="flex w-full flex-col justify-between pb-4">
<h2 className="text-md pb-4 font-medium dark:text-white">{t`Change Password`}</h2>
<h2 className="pb-4 text-base font-medium dark:text-white">
{hasPassword ? t`Change Password` : t`Set Password`}
</h2>
<p className="mb-4 text-sm text-light-900">
{t`Enter your current password and choose a new secure password.`}
{hasPassword
? t`Enter your current password and choose a new secure password.`
: t`You signed in without a password. Set a password to enable password-based login.`}
</p>
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="space-y-2">
<div>
<Input
id="currentPassword"
type="password"
{...register("currentPassword")}
placeholder={t`Enter your current password`}
/>
{errors.currentPassword && (
<p className="mt-2 text-xs text-red-400">
{errors.currentPassword.message}
</p>
)}
</div>
{hasPassword && (
<div>
<Input
id="currentPassword"
type="password"
{...register("currentPassword")}
placeholder={t`Enter your current password`}
/>
{errors.currentPassword && (
<p className="mt-2 text-xs text-red-400">
{errors.currentPassword.message}
</p>
)}
</div>
)}
<div>
<Input
@@ -175,7 +213,7 @@ export function ChangePasswordFormConfirmation() {
fullWidth
size="lg"
>
{t`Change Password`}
{hasPassword ? t`Change Password` : t`Set Password`}
</Button>
</div>
</form>

View File

@@ -187,6 +187,7 @@ export const cardRouter = createTRPCRouter({
"card.created",
{
id: String(newCard.id),
publicId: newCard.publicId,
title: input.title,
description: input.description,
dueDate: input.dueDate ?? null,
@@ -1070,6 +1071,7 @@ export const cardRouter = createTRPCRouter({
movedToNewList ? "card.moved" : "card.updated",
{
id: String(result.id),
publicId: result.publicId,
title: result.title,
description: result.description,
dueDate: result.dueDate,
@@ -1165,6 +1167,7 @@ export const cardRouter = createTRPCRouter({
"card.deleted",
{
id: String(fullCard.id),
publicId: fullCard.publicId,
title: fullCard.title,
description: fullCard.description,
dueDate: fullCard.dueDate,

View File

@@ -2,9 +2,9 @@ import { TRPCError } from "@trpc/server";
import { z } from "zod";
import * as userRepo from "@kan/db/repository/user.repo";
import { generateAvatarUrl } from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { generateAvatarUrl } from "@kan/shared/utils";
export const userRouter = createTRPCRouter({
getUser: protectedProcedure
@@ -27,6 +27,8 @@ export const userRouter = createTRPCRouter({
name: z.string().nullable(),
image: z.string().nullable(),
stripeCustomerId: z.string().nullable(),
hasPassword: z.boolean(),
hasMagicLinkAccount: z.boolean(),
apiKey: z
.object({
id: z.number(),
@@ -61,6 +63,8 @@ export const userRouter = createTRPCRouter({
return {
...result,
image: imageUrl,
hasPassword: result.hasPassword,
hasMagicLinkAccount: result.hasMagicLinkAccount,
apiKey: apiKey ? { id: apiKey.id, prefix: apiKey.prefix } : null,
};
}),
@@ -114,4 +118,60 @@ export const userRouter = createTRPCRouter({
image: imageUrl,
};
}),
setPassword: protectedProcedure
.meta({
openapi: {
method: "POST",
path: "/users/me/password",
summary: "Set password",
description:
"Sets a password for a user who signed up via magic link and has no password yet",
tags: ["Users"],
protect: true,
},
})
.input(
z.object({
newPassword: z
.string()
.min(8, "Password must be at least 8 characters"),
}),
)
.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 existing = await userRepo.getById(ctx.db, userId);
if (!existing) {
throw new TRPCError({
message: `User not found`,
code: "NOT_FOUND",
});
}
if (existing.hasPassword) {
throw new TRPCError({
message: `Password already set; use change password instead`,
code: "BAD_REQUEST",
});
}
try {
await ctx.auth.api.setPassword({ newPassword: input.newPassword });
} catch {
throw new TRPCError({
message: "Failed to set password",
code: "INTERNAL_SERVER_ERROR",
});
}
return { success: true };
}),
});

View File

@@ -334,6 +334,7 @@ export const webhookRouter = createTRPCRouter({
const testPayload = createCardWebhookPayload("card.created", {
id: "test-card-id",
publicId: "test-card-public-id",
title: "Test Card",
description: "This is a test webhook payload",
dueDate: null,

View File

@@ -63,6 +63,11 @@ const createAuthWithHeaders = (
headers,
query: { referenceId: input.workspacePublicId },
}),
setPassword: (input: { newPassword: string }) =>
auth.api.setPassword({
headers,
body: { newPassword: input.newPassword },
}),
},
};
};

View File

@@ -43,6 +43,7 @@ describe("webhook utilities", () => {
"card.created",
{
id: "card-123",
publicId: "card-pub-123",
title: "Test Card",
listId: "list-456",
},
@@ -55,12 +56,14 @@ describe("webhook utilities", () => {
expect(payload.timestamp).toBe("2024-01-15T12:00:00.000Z");
expect(payload.data.card).toEqual({
id: "card-123",
publicId: "card-pub-123",
title: "Test Card",
description: undefined,
dueDate: null,
listId: "list-456",
boardId: "board-789",
});
expect(payload.data.card.publicId).toBe("card-pub-123");
});
it("includes optional card fields when provided", () => {
@@ -69,6 +72,7 @@ describe("webhook utilities", () => {
"card.updated",
{
id: "card-123",
publicId: "card-pub-123",
title: "Test Card",
description: "A description",
dueDate,
@@ -88,6 +92,7 @@ describe("webhook utilities", () => {
"card.created",
{
id: "card-123",
publicId: "card-pub-123",
title: "Test Card",
listId: "list-456",
},
@@ -108,6 +113,7 @@ describe("webhook utilities", () => {
"card.created",
{
id: "card-123",
publicId: "card-pub-123",
title: "Test Card",
listId: "list-456",
},
@@ -128,6 +134,7 @@ describe("webhook utilities", () => {
"card.created",
{
id: "card-123",
publicId: "card-pub-123",
title: "Test Card",
listId: "list-456",
},
@@ -151,6 +158,7 @@ describe("webhook utilities", () => {
"card.updated",
{
id: "card-123",
publicId: "card-pub-123",
title: "Updated Title",
listId: "list-456",
},
@@ -172,6 +180,7 @@ describe("webhook utilities", () => {
"card.moved",
{
id: "card-123",
publicId: "card-pub-123",
title: "Moved Card",
listId: "list-public-done",
},
@@ -212,6 +221,7 @@ describe("webhook utilities", () => {
data: {
card: {
id: "card-123",
publicId: "card-pub-123",
title: "Test Card",
listId: "list-456",
boardId: "board-789",
@@ -408,6 +418,7 @@ describe("webhook utilities", () => {
data: {
card: {
id: "card-123",
publicId: "card-pub-123",
title: "Test Card",
listId: "list-456",
boardId: "board-789",

View File

@@ -16,6 +16,7 @@ export interface WebhookPayload {
data: {
card: {
id: string;
publicId: string;
title: string;
description?: string | null;
dueDate?: string | null; // ISO string after JSON serialization
@@ -221,6 +222,7 @@ export function createCardWebhookPayload(
event: WebhookEventType,
card: {
id: string;
publicId: string;
title: string;
description?: string | null;
dueDate?: Date | null;
@@ -243,6 +245,7 @@ export function createCardWebhookPayload(
data: {
card: {
id: card.id,
publicId: card.publicId,
title: card.title,
description: card.description,
dueDate: card.dueDate?.toISOString() ?? null,

View File

@@ -0,0 +1,3 @@
ALTER TABLE "subscription" ADD COLUMN "partnerLicenseKey" varchar(255);--> statement-breakpoint
ALTER TABLE "subscription" ADD COLUMN "partnerTier" integer;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "subscription_partner_license_key_idx" ON "subscription" USING btree ("partnerLicenseKey");

File diff suppressed because it is too large Load Diff

View File

@@ -232,6 +232,19 @@
"when": 1776809379931,
"tag": "20260421220939_AddCardNumber",
"breakpoints": true
},
{
"idx": 33,
"version": "7",
"when": 1778600989523,
"breakpoints": true
},
{
"idx": 34,
"version": "7",
"when": 1778617946519,
"tag": "20260512203226_AddPartnerLicenseToSubscription",
"breakpoints": true
}
]
}

View File

@@ -7,6 +7,7 @@ export const updateById = async (
db: dbClient,
subscriptionId: number,
updates: {
plan?: string;
unlimitedSeats?: boolean;
status?: string;
seats?: number | null;
@@ -89,3 +90,47 @@ export const create = async (
const [result] = await db.insert(subscription).values(data).returning();
return result;
};
export const getByPartnerLicenseKey = async (
db: dbClient,
partnerLicenseKey: string,
) => {
const result = await db.query.subscription.findFirst({
where: eq(subscription.partnerLicenseKey, partnerLicenseKey),
});
return result;
};
export const upsertByPartnerLicenseKey = async (
db: dbClient,
partnerLicenseKey: string,
data: {
plan: string;
status: string;
partnerTier: number;
seats: number | null;
unlimitedSeats: boolean;
referenceId?: string;
},
) => {
const existing = await getByPartnerLicenseKey(db, partnerLicenseKey);
if (existing) {
const [result] = await db
.update(subscription)
.set({ ...data, updatedAt: new Date() })
.where(eq(subscription.partnerLicenseKey, partnerLicenseKey))
.returning();
return result;
}
const [result] = await db
.insert(subscription)
.values({
partnerLicenseKey,
...data,
referenceId: data.referenceId ?? null,
})
.returning();
return result;
};

View File

@@ -1,8 +1,11 @@
import { count, desc, eq } from "drizzle-orm";
import { and, count, desc, eq, isNotNull } from "drizzle-orm";
import { v4 as uuidv4 } from "uuid";
import type { dbClient } from "@kan/db/client";
import { apikey, users } from "@kan/db/schema";
import { account, apikey, users } from "@kan/db/schema";
const PROVIDER_CREDENTIAL = "credential";
const PROVIDER_MAGIC_LINK = "magic-link";
export const getCount = async (db: dbClient) => {
const result = await db.select({ count: count() }).from(users);
@@ -11,27 +14,58 @@ export const getCount = async (db: dbClient) => {
};
export const getById = async (db: dbClient, userId: string) => {
return await db.query.users.findFirst({
columns: {
id: true,
name: true,
email: true,
image: true,
stripeCustomerId: true,
},
with: {
apiKeys: {
columns: {
id: true,
prefix: true,
key: true,
},
orderBy: desc(apikey.createdAt),
limit: 1,
const [user, credentialAccount, magicLinkAccount] = await Promise.all([
db.query.users.findFirst({
columns: {
id: true,
name: true,
email: true,
image: true,
stripeCustomerId: true,
},
},
where: eq(users.id, userId),
});
with: {
apiKeys: {
columns: {
id: true,
prefix: true,
key: true,
},
orderBy: desc(apikey.createdAt),
limit: 1,
},
},
where: eq(users.id, userId),
}),
db
.select({ id: account.id })
.from(account)
.where(
and(
eq(account.userId, userId),
eq(account.providerId, PROVIDER_CREDENTIAL),
isNotNull(account.password),
),
)
.limit(1),
db
.select({ id: account.id })
.from(account)
.where(
and(
eq(account.userId, userId),
eq(account.providerId, PROVIDER_MAGIC_LINK),
),
)
.limit(1),
]);
if (!user) return undefined;
return {
...user,
hasPassword: credentialAccount.length > 0,
hasMagicLinkAccount: magicLinkAccount.length > 0,
};
};
export const getByStripeCustomerId = async (

View File

@@ -5,31 +5,42 @@ import {
integer,
pgTable,
timestamp,
uniqueIndex,
varchar,
} from "drizzle-orm/pg-core";
import { workspaces } from "./workspaces";
export const subscription = pgTable("subscription", {
id: bigserial("id", { mode: "number" }).primaryKey(),
plan: varchar("plan", { length: 255 }).notNull(),
referenceId: varchar("referenceId", { length: 12 }).references(
() => workspaces.publicId,
{ onDelete: "set null" },
),
stripeCustomerId: varchar("stripeCustomerId", { length: 255 }),
stripeSubscriptionId: varchar("stripeSubscriptionId", { length: 255 }),
status: varchar("status", { length: 255 }).notNull(),
periodStart: timestamp("periodStart"),
periodEnd: timestamp("periodEnd"),
cancelAtPeriodEnd: boolean("cancelAtPeriodEnd"),
seats: integer("seats"),
unlimitedSeats: boolean("unlimitedSeats").default(false).notNull(),
trialStart: timestamp("trialStart"),
trialEnd: timestamp("trialEnd"),
createdAt: timestamp("createdAt").notNull().defaultNow(),
updatedAt: timestamp("updatedAt").notNull().defaultNow(),
}).enableRLS();
export const subscription = pgTable(
"subscription",
{
id: bigserial("id", { mode: "number" }).primaryKey(),
plan: varchar("plan", { length: 255 }).notNull(),
referenceId: varchar("referenceId", { length: 12 }).references(
() => workspaces.publicId,
{ onDelete: "set null" },
),
stripeCustomerId: varchar("stripeCustomerId", { length: 255 }),
stripeSubscriptionId: varchar("stripeSubscriptionId", { length: 255 }),
status: varchar("status", { length: 255 }).notNull(),
periodStart: timestamp("periodStart"),
periodEnd: timestamp("periodEnd"),
cancelAtPeriodEnd: boolean("cancelAtPeriodEnd"),
seats: integer("seats"),
unlimitedSeats: boolean("unlimitedSeats").default(false).notNull(),
trialStart: timestamp("trialStart"),
trialEnd: timestamp("trialEnd"),
partnerLicenseKey: varchar("partnerLicenseKey", { length: 255 }),
partnerTier: integer("partnerTier"),
createdAt: timestamp("createdAt").notNull().defaultNow(),
updatedAt: timestamp("updatedAt").notNull().defaultNow(),
},
(table) => [
uniqueIndex("subscription_partner_license_key_idx").on(
table.partnerLicenseKey,
),
],
).enableRLS();
export const subscriptionsRelations = relations(subscription, ({ one }) => ({
workspace: one(workspaces, {