Compare commits

..

2 Commits

Author SHA1 Message Date
Henry
4f6fc66f84 chore: add LOG_LEVEL to docker compose and readme 2026-03-13 22:26:04 +00:00
Henry
3604a49a89 feat: add logger package to improve observability 2026-03-13 22:19:45 +00:00
83 changed files with 956 additions and 9312 deletions

2
.gitignore vendored
View File

@@ -54,5 +54,3 @@ i18n.cache
# pgdata
/apps/web/pgdata
.claude

View File

@@ -11,10 +11,10 @@ https://kan.bn/api/v1
## Authentication
Most endpoints require authentication using your API key. You can create one in the [settings page](https://kan.bn/settings) of your account. Include this key as a Bearer token in the `Authorization` header of each request.
Most endpoints require authentication using your API key. You can create one in the [settings page](https://kan.bn/settings) of your account. Include this key in the `x-api-key` header of each request.
```
'Authorization': 'Bearer kan_123456789'
'x-api-key': kan_123456789
```
## Response codes

View File

@@ -103,9 +103,6 @@ checksums:
tMYIu7/message: 8b1f0bb96a905646ecfad1cfdfa42168
tMYIu7/origin/0/0: d77eb1f90f2fa86a55181629d88d237a
tMYIu7/translation: 8b1f0bb96a905646ecfad1cfdfa42168
UGCIzB/translation: f7b6699c16726afc48f1e1d1339b2e7e
UGCIzB/message: f7b6699c16726afc48f1e1d1339b2e7e
UGCIzB/origin/0/0: 0eeeb7e68164ff118040918f1ef00a7a
B3xxao/message: 9d80becc4fe65a74e0004e4fd290735d
B3xxao/origin/0/0: 6812b79a8fdc3527586b523d4c137340
B3xxao/translation: 9d80becc4fe65a74e0004e4fd290735d
@@ -519,27 +516,23 @@ checksums:
dEgA5A/message: 2e2a849c2223911717de8caa2c71bade
dEgA5A/origin/0/0: cf5db0532c4f1cf49cebe78926c03385
dEgA5A/origin/1/0: a50c4fbde8da6e0a77c4e0aef0b44dea
dEgA5A/origin/2/0: 97bd206a05414e2a571020d64e166093
dEgA5A/origin/3/0: 522726bb62051656dc0d165185d6b451
dEgA5A/origin/4/0: b2059fa4b8b99cc10440da26475b234c
dEgA5A/origin/5/0: 2a382c1cdf010f01a8e95c3e05f6df53
dEgA5A/origin/6/0: d55f532bb5ae247e3a018978be0da463
dEgA5A/origin/7/0: 4b4f55c033add8db69178563832227c6
dEgA5A/origin/8/0: 68450df287fb46ea629e818732c1291a
dEgA5A/origin/9/0: 03c00909b41be01231cd3e843c8ee44e
dEgA5A/origin/10/0: 0627e0040ca4939c9a57349e98c51797
dEgA5A/origin/11/0: 15792234f408027822ee71ccddde6f9b
dEgA5A/origin/12/0: 5c60161e173f5eae9276d8e4c70b4ebb
dEgA5A/origin/13/0: a1fde3fcce608870830f168f027727ff
dEgA5A/origin/14/0: 83911e3eacbad4583e2b1647a784d154
dEgA5A/origin/15/0: dbad0c8d7863cb41f5495264f82e7081
dEgA5A/origin/2/0: 522726bb62051656dc0d165185d6b451
dEgA5A/origin/3/0: b2059fa4b8b99cc10440da26475b234c
dEgA5A/origin/4/0: 2a382c1cdf010f01a8e95c3e05f6df53
dEgA5A/origin/5/0: d55f532bb5ae247e3a018978be0da463
dEgA5A/origin/6/0: 4b4f55c033add8db69178563832227c6
dEgA5A/origin/7/0: 68450df287fb46ea629e818732c1291a
dEgA5A/origin/8/0: 03c00909b41be01231cd3e843c8ee44e
dEgA5A/origin/9/0: 0627e0040ca4939c9a57349e98c51797
dEgA5A/origin/10/0: 15792234f408027822ee71ccddde6f9b
dEgA5A/origin/11/0: 5c60161e173f5eae9276d8e4c70b4ebb
dEgA5A/origin/12/0: a1fde3fcce608870830f168f027727ff
dEgA5A/origin/13/0: 83911e3eacbad4583e2b1647a784d154
dEgA5A/origin/14/0: dbad0c8d7863cb41f5495264f82e7081
dEgA5A/translation: 2e2a849c2223911717de8caa2c71bade
kryGs%2B/message: bba0beaced7ea954ceb980f2b022ffee
kryGs%2B/origin/0/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
kryGs%2B/translation: bba0beaced7ea954ceb980f2b022ffee
sU2uWc/translation: 3fe52244676faf64e5266b6713d01e07
sU2uWc/message: 3fe52244676faf64e5266b6713d01e07
sU2uWc/origin/0/0: 97bd206a05414e2a571020d64e166093
fEY2vP/message: 91509e2f92b0b3b11330b6983139fdbf
fEY2vP/origin/0/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
fEY2vP/origin/1/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
@@ -548,9 +541,8 @@ checksums:
ZH7TVS/origin/0/0: 984122104bcc6d8fa6d4c8a35e7751d2
ZH7TVS/translation: 7c34f59f4005e6cb3a6ff546ea0b96e3
rQXTmd/message: 9387a4702b302f23f2f7c20b73a2b3dd
rQXTmd/origin/0/0: fc0c18a095a68277a0f90a115942683b
rQXTmd/origin/1/0: 1b05beba6bdd908015fa8e798717f024
rQXTmd/origin/2/0: 9c8b544f5db130cd7c5eda9064ba3957
rQXTmd/origin/0/0: 1b05beba6bdd908015fa8e798717f024
rQXTmd/origin/1/0: 9c8b544f5db130cd7c5eda9064ba3957
rQXTmd/translation: 9387a4702b302f23f2f7c20b73a2b3dd
VhMDMg/message: a552fc5c4189ebc3e2e6018edda7d18f
VhMDMg/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
@@ -558,9 +550,6 @@ checksums:
VhMDMg/origin/2/0: 0627e0040ca4939c9a57349e98c51797
VhMDMg/origin/3/0: 0627e0040ca4939c9a57349e98c51797
VhMDMg/translation: a552fc5c4189ebc3e2e6018edda7d18f
fzGyWs/message: 25dbbca7a83f6af119179e7a56c953d4
fzGyWs/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
fzGyWs/translation: 25dbbca7a83f6af119179e7a56c953d4
yD3ZSw/message: 293d49fc3c75e9c425b64bd7126e6b46
yD3ZSw/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
yD3ZSw/translation: 293d49fc3c75e9c425b64bd7126e6b46
@@ -707,18 +696,6 @@ checksums:
F0YmUY/message: 9def232c9163d109f0e9093684cfe75f
F0YmUY/origin/0/0: 1b05beba6bdd908015fa8e798717f024
F0YmUY/translation: 9def232c9163d109f0e9093684cfe75f
0utuU6/translation: 9d7e453b24dfaf217352aeadc262074d
0utuU6/message: 9d7e453b24dfaf217352aeadc262074d
0utuU6/origin/0/0: 97bd206a05414e2a571020d64e166093
fDQ7hA/translation: 1d24ecbad9f2f0081ab51c7366e40506
fDQ7hA/message: 1d24ecbad9f2f0081ab51c7366e40506
fDQ7hA/origin/0/0: 97bd206a05414e2a571020d64e166093
1bJKhj/translation: f61803ac8da8253e4ba35f92b3af1101
1bJKhj/message: f61803ac8da8253e4ba35f92b3af1101
1bJKhj/origin/0/0: 0eeeb7e68164ff118040918f1ef00a7a
uvH2xS/translation: ecc2617544a6d385d18678577fa112fd
uvH2xS/message: ecc2617544a6d385d18678577fa112fd
uvH2xS/origin/0/0: 97bd206a05414e2a571020d64e166093
9PaIxv/message: da95932e7a1465a5d21aa3b855a46dc2
9PaIxv/origin/0/0: c0b59968b2b234167c488a05cc38710b
9PaIxv/translation: da95932e7a1465a5d21aa3b855a46dc2
@@ -858,8 +835,7 @@ checksums:
tmpW%2Bw/origin/0/0: 0ad2af981579a62b084b3d41462c5546
tmpW%2Bw/translation: e915cd160651ad4b61a67e8da0737c23
nabda1/message: 18811ccf76fbb7a2772d648f7c5f0ea7
nabda1/origin/0/0: 0eeeb7e68164ff118040918f1ef00a7a
nabda1/origin/1/0: 1b05beba6bdd908015fa8e798717f024
nabda1/origin/0/0: 1b05beba6bdd908015fa8e798717f024
nabda1/translation: 18811ccf76fbb7a2772d648f7c5f0ea7
njJFtc/message: a3dae0c9ddbc73677aad9812e3dffd69
njJFtc/origin/0/0: b2059fa4b8b99cc10440da26475b234c
@@ -940,15 +916,12 @@ checksums:
vUr5JT/origin/0/0: 4a1be3707d14c696a16f453d132fb248
vUr5JT/translation: 6ae8282ab8b4a01b4ce87b119e13714b
DPfwMq/message: ffd408fa29d5bc9039ef8ea1b9b699bb
DPfwMq/origin/0/0: 81b173297e9c022d9ebde5f7018c95f9
DPfwMq/origin/1/0: 120e67abaa784f3adb192e7519919a81
DPfwMq/origin/2/0: fc68b5394b9b466d5d67df855060544d
DPfwMq/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/origin/1/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/origin/2/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/origin/3/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/origin/4/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/origin/5/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/origin/6/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/origin/7/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/origin/8/0: eb63312c63f6c8d2a5b6520c89012123
DPfwMq/translation: ffd408fa29d5bc9039ef8ea1b9b699bb
R3FsF4/message: 142b391cca3e05f2af9617092f7358c5
R3FsF4/origin/0/0: 3a89a46ac678e34dc8eb687b07a4ad4b
@@ -970,16 +943,6 @@ checksums:
zxKs%2By/message: 0b6c8ad7aba0873b7e212d5f1949ca97
zxKs%2By/origin/0/0: db1e755dc7d73e037f425111bf7c30a6
zxKs%2By/translation: 0b6c8ad7aba0873b7e212d5f1949ca97
euc6Ns/translation: 27756566785c2b8463e21582c4bb619b
euc6Ns/message: 27756566785c2b8463e21582c4bb619b
euc6Ns/origin/0/0: 97bd206a05414e2a571020d64e166093
BTDPLo/translation: f6a33afcf5ff1489551c94867e083dca
BTDPLo/message: f6a33afcf5ff1489551c94867e083dca
BTDPLo/origin/0/0: 97bd206a05414e2a571020d64e166093
BTDPLo/origin/1/0: 0eeeb7e68164ff118040918f1ef00a7a
06EQqT/translation: e9f56489fb395a890ae2557200c29a36
06EQqT/message: e9f56489fb395a890ae2557200c29a36
06EQqT/origin/0/0: 97bd206a05414e2a571020d64e166093
ePK91l/message: eee7f39ff90b18852afc1671f21fbaa9
ePK91l/origin/0/0: b697a20a9536be4dd8ecd143461eafc7
ePK91l/origin/1/0: dc1a240d2d0cccb102c67ca311d4e457
@@ -1026,9 +989,6 @@ checksums:
IqjpYe/message: 81d41cf573a7109c376d30d905beb596
IqjpYe/origin/0/0: 5f6c4a0c9be5098099167871ae1564af
IqjpYe/translation: 81d41cf573a7109c376d30d905beb596
bFREhu/translation: b2cc9a311fc28d1594252f697c7f5342
bFREhu/message: b2cc9a311fc28d1594252f697c7f5342
bFREhu/origin/0/0: 97bd206a05414e2a571020d64e166093
0%2BewPp/message: 785fe23c0eef0a5b60b5b2a88151de31
0%2BewPp/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
0%2BewPp/origin/1/0: eb63312c63f6c8d2a5b6520c89012123
@@ -1207,9 +1167,6 @@ checksums:
l31EoQ/message: b4c05c9174637f4da2a41ead0d65bc01
l31EoQ/origin/0/0: 4980d8e27a628e7cd9a70b839d844083
l31EoQ/translation: b4c05c9174637f4da2a41ead0d65bc01
q3il6U/message: 79a4fffd70bfbedb6174ea4c767f4ab9
q3il6U/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
q3il6U/translation: 79a4fffd70bfbedb6174ea4c767f4ab9
"%2B3TYXa/message": 9fa1c942feb40d6350322b71561df789
"%2B3TYXa/origin/0/0": 4980d8e27a628e7cd9a70b839d844083
"%2B3TYXa/translation": 9fa1c942feb40d6350322b71561df789
@@ -1463,10 +1420,9 @@ checksums:
h7MgpO/origin/0/0: feacdaf2791aaddec128f463ffa2e807
h7MgpO/translation: ef00d7494b69def6841620bd6554d040
h8DugX/message: 6f15627a90002323eac018274b6922d6
h8DugX/origin/0/0: 120e67abaa784f3adb192e7519919a81
h8DugX/origin/1/0: db1e755dc7d73e037f425111bf7c30a6
h8DugX/origin/2/0: 984122104bcc6d8fa6d4c8a35e7751d2
h8DugX/origin/3/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
h8DugX/origin/0/0: db1e755dc7d73e037f425111bf7c30a6
h8DugX/origin/1/0: 984122104bcc6d8fa6d4c8a35e7751d2
h8DugX/origin/2/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
h8DugX/translation: 6f15627a90002323eac018274b6922d6
AZI%2Fwb/message: e1ceda0c6cc32fb04cb9423582ad8fe4
AZI%2Fwb/origin/0/0: c0b59968b2b234167c488a05cc38710b
@@ -1477,9 +1433,6 @@ checksums:
vXIe7J/message: 277fd1a41cc237a437cd1d5e4a80463b
vXIe7J/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
vXIe7J/translation: 277fd1a41cc237a437cd1d5e4a80463b
k7rCa%2F/message: 890b7851af05f97ac53644e4154c13b4
k7rCa%2F/origin/0/0: 3de48fb04f7df9bdbe399911cdb3fe86
k7rCa%2F/translation: 890b7851af05f97ac53644e4154c13b4
8Is1ih/message: 624592ba2ecf647a3fe60d493c1ee71a
8Is1ih/origin/0/0: ff4a160eca5f8da4f075fd1dd6061fa6
8Is1ih/origin/1/0: fadd5a5b6963ec5f261072f7e8d2140b
@@ -1511,14 +1464,12 @@ checksums:
TCt%2F8K/translation: ffd8033392a01d7b33f13afa51bccb9a
edWbV6/message: 4a4805700b31ec65bcb3ca962bccfd1b
edWbV6/origin/0/0: 85d0a3237fa9329fba3f0278a2cd7284
edWbV6/origin/1/0: fc0c18a095a68277a0f90a115942683b
edWbV6/origin/2/0: 1b05beba6bdd908015fa8e798717f024
edWbV6/origin/3/0: 9c8b544f5db130cd7c5eda9064ba3957
edWbV6/origin/4/0: faa8a2576972b4eb31565d8ea6098f43
edWbV6/origin/1/0: 1b05beba6bdd908015fa8e798717f024
edWbV6/origin/2/0: 9c8b544f5db130cd7c5eda9064ba3957
edWbV6/origin/3/0: faa8a2576972b4eb31565d8ea6098f43
edWbV6/translation: 4a4805700b31ec65bcb3ca962bccfd1b
2BBAbc/message: 94f13e7ef909a4de9db7abaa1f9f0b61
2BBAbc/origin/0/0: 97bd206a05414e2a571020d64e166093
2BBAbc/origin/1/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
2BBAbc/origin/0/0: 15fd9c05fc09ccbbb54aadc4aa5e5501
2BBAbc/translation: 94f13e7ef909a4de9db7abaa1f9f0b61
zQX5Dj/message: e925e2e6ccaf0eb4064a888aaea8d3c2
zQX5Dj/origin/0/0: 5e445d66b146e0ca392faa35098701c4
@@ -1556,10 +1507,6 @@ checksums:
DbZgsJ/message: 7a1c63b75cea690b3fda6c3f203e31ff
DbZgsJ/origin/0/0: 0ad2af981579a62b084b3d41462c5546
DbZgsJ/translation: 7a1c63b75cea690b3fda6c3f203e31ff
hB02vO/translation: c13aa2a155ab4aff1c8fed6650a94e1a
hB02vO/message: c13aa2a155ab4aff1c8fed6650a94e1a
hB02vO/origin/0/0: fc68b5394b9b466d5d67df855060544d
hB02vO/origin/1/0: 0eeeb7e68164ff118040918f1ef00a7a
IDvohQ/message: 35d0822f65971b97774b962561b02649
IDvohQ/origin/0/0: 1d6b5f88cdad160a67842f6b65387448
IDvohQ/translation: 35d0822f65971b97774b962561b02649
@@ -1567,9 +1514,6 @@ checksums:
jl3aEJ/placeholders/0/0: 06f23520ebabfffed4999b5bcf61c9cd
jl3aEJ/origin/0/0: 1d6b5f88cdad160a67842f6b65387448
jl3aEJ/translation: 4c38799ff25321ea25017bf4cd8e2e4f
agPptk/message: 43983f2178fb28aacaef6c5f41282c4a
agPptk/origin/0/0: 3de48fb04f7df9bdbe399911cdb3fe86
agPptk/translation: 43983f2178fb28aacaef6c5f41282c4a
W%2FElkg/message: 1f527cd6d1ed602930bcaa303f503b51
W%2FElkg/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
W%2FElkg/translation: 1f527cd6d1ed602930bcaa303f503b51
@@ -1593,9 +1537,9 @@ checksums:
"%2FbZzdR/message": 40ea106c56d79634e058e087ad322025
"%2FbZzdR/origin/0/0": c0b59968b2b234167c488a05cc38710b
"%2FbZzdR/translation": 40ea106c56d79634e058e087ad322025
hty0d5/translation: b4b1c8396e445e8b7956c747da2cf601
hty0d5/message: b4b1c8396e445e8b7956c747da2cf601
hty0d5/origin/0/0: 04c86ea96f180745739d59e822da4026
hty0d5/translation: b4b1c8396e445e8b7956c747da2cf601
"%2B8Nek%2F/message": 818f1192e32bb855597f930d3e78806e
"%2B8Nek%2F/origin/0/0": 9737a9cef0646ff4bbd942b1924b9a2c
"%2B8Nek%2F/origin/1/0": 82a5cf8d69f098641eb357bf102cd504
@@ -1603,12 +1547,6 @@ checksums:
vneRvS/message: e228107df80015377112d41f4f155cb3
vneRvS/origin/0/0: 8628ddb6f4ded25c0f17e2cbda8207d3
vneRvS/translation: e228107df80015377112d41f4f155cb3
51UCsN/translation: ce14fde5e972c2b5276c25ddded817c4
51UCsN/message: ce14fde5e972c2b5276c25ddded817c4
51UCsN/origin/0/0: 0eeeb7e68164ff118040918f1ef00a7a
J4%2BOTA/translation: 6ad27e803483208b5313660b7b5791d3
J4%2BOTA/message: 6ad27e803483208b5313660b7b5791d3
J4%2BOTA/origin/0/0: fd0e66cef8d46e96bcf3143a12f9b6db
BOqTi5/message: 11edf0427766c26db541d46379dc3c16
BOqTi5/placeholders/0/0: 9db68a4b386b9fa1fd1d5797c32be100
BOqTi5/placeholders/1/0: b837a9e8bd06b58be95699b3ee663f43
@@ -1923,16 +1861,10 @@ checksums:
PZCqeW/origin/1/0: 8628ddb6f4ded25c0f17e2cbda8207d3
PZCqeW/translation: 325dea6dd0348a27a6818db2c1340c98
fuwKpE/message: 346a18a54564881c888b95873d03df44
fuwKpE/origin/0/0: 97bd206a05414e2a571020d64e166093
fuwKpE/origin/1/0: fd0e66cef8d46e96bcf3143a12f9b6db
fuwKpE/origin/2/0: fc0c18a095a68277a0f90a115942683b
fuwKpE/origin/3/0: 1b05beba6bdd908015fa8e798717f024
fuwKpE/origin/4/0: 9c8b544f5db130cd7c5eda9064ba3957
fuwKpE/origin/5/0: faa8a2576972b4eb31565d8ea6098f43
fuwKpE/origin/0/0: 1b05beba6bdd908015fa8e798717f024
fuwKpE/origin/1/0: 9c8b544f5db130cd7c5eda9064ba3957
fuwKpE/origin/2/0: faa8a2576972b4eb31565d8ea6098f43
fuwKpE/translation: 346a18a54564881c888b95873d03df44
nYNMZZ/translation: d5a8a1b38eaf02d02cd6ac2211b42a92
nYNMZZ/message: d5a8a1b38eaf02d02cd6ac2211b42a92
nYNMZZ/origin/0/0: 97bd206a05414e2a571020d64e166093
aHCEmh/message: ce27f1aeacccc542a174c4b2bce022b0
aHCEmh/origin/0/0: 98e2302d0d8d12ce65c4140b87450673
aHCEmh/origin/1/0: 634473ef965836871efe40875a36b6ab
@@ -2081,9 +2013,9 @@ checksums:
YEOVrT/message: 86b76024524fc585b2c3950126ef6f62
YEOVrT/origin/0/0: fadd5a5b6963ec5f261072f7e8d2140b
YEOVrT/translation: 86b76024524fc585b2c3950126ef6f62
"%2B5kO8P/translation": 77b65adf8ae2b69eaed51b6104ded65e
"%2B5kO8P/message": 77b65adf8ae2b69eaed51b6104ded65e
"%2B5kO8P/origin/0/0": 04c86ea96f180745739d59e822da4026
"%2B5kO8P/translation": 77b65adf8ae2b69eaed51b6104ded65e
tfDRzk/message: f7a2929f33bc420195e59ac5a8bcd454
tfDRzk/origin/0/0: a50c4fbde8da6e0a77c4e0aef0b44dea
tfDRzk/origin/1/0: b2059fa4b8b99cc10440da26475b234c
@@ -2113,9 +2045,6 @@ checksums:
K3iLeO/message: e3bf6f95419169f460fb6918a0afc512
K3iLeO/origin/0/0: 5e2ed4920ffd2f535ece848ff44900bd
K3iLeO/translation: e3bf6f95419169f460fb6918a0afc512
zYRVNp/translation: 8b36455dd5b43e56d2ab06e522c3bacc
zYRVNp/message: 8b36455dd5b43e56d2ab06e522c3bacc
zYRVNp/origin/0/0: 97bd206a05414e2a571020d64e166093
wgNoIs/message: eedc7cdb02de467c15dc418a066a77f2
wgNoIs/origin/0/0: 3c57a918258af4b44c187f58d203345f
wgNoIs/origin/1/0: 3c57a918258af4b44c187f58d203345f
@@ -2145,9 +2074,7 @@ checksums:
v3YoMA/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
v3YoMA/translation: 3fff865dc00435f82896fc302ea45630
8AbRER/message: 3cb81c4829857556f5d117c9a23f7bcc
8AbRER/origin/0/0: 81b173297e9c022d9ebde5f7018c95f9
8AbRER/origin/1/0: 0eeeb7e68164ff118040918f1ef00a7a
8AbRER/origin/2/0: 2201bf679c663a732d7f1b3c3ce3897f
8AbRER/origin/0/0: 2201bf679c663a732d7f1b3c3ce3897f
8AbRER/translation: 3cb81c4829857556f5d117c9a23f7bcc
Eit43O/message: b5d9a1edd988f455041aee9fa7fb3d63
Eit43O/origin/0/0: 1d6b5f88cdad160a67842f6b65387448
@@ -2212,9 +2139,6 @@ checksums:
zEcnBA/message: 513c3b4d9f0fda60fe079d42dcbd3882
zEcnBA/origin/0/0: c0b59968b2b234167c488a05cc38710b
zEcnBA/translation: 513c3b4d9f0fda60fe079d42dcbd3882
4Revpc/message: 3adb3ae7891c5736776df840cff2360b
4Revpc/origin/0/0: 3de48fb04f7df9bdbe399911cdb3fe86
4Revpc/translation: 3adb3ae7891c5736776df840cff2360b
"%2B%2BrVAm/message": 2227508afb0d38a027e323207e47d4a2
"%2B%2BrVAm/origin/0/0": eb63312c63f6c8d2a5b6520c89012123
"%2B%2BrVAm/translation": 2227508afb0d38a027e323207e47d4a2
@@ -2244,9 +2168,9 @@ checksums:
zzDlyQ/origin/0/0: d96af8a06c2e38a72238bcd16e426859
zzDlyQ/origin/1/0: d96af8a06c2e38a72238bcd16e426859
zzDlyQ/translation: c43827becada6750f7a25890905f38b9
DBC3t5/translation: d21934fb36dd8a82e36352968207d44e
DBC3t5/message: d21934fb36dd8a82e36352968207d44e
DBC3t5/origin/0/0: 04c86ea96f180745739d59e822da4026
DBC3t5/translation: d21934fb36dd8a82e36352968207d44e
uZg2%2Bw/message: 21af8119141afcce5f12da489f34db6a
uZg2%2Bw/origin/0/0: dbad0c8d7863cb41f5495264f82e7081
uZg2%2Bw/translation: 21af8119141afcce5f12da489f34db6a
@@ -2311,9 +2235,6 @@ checksums:
C6gv54/message: 1573c77059bc92df17e661befda8d9b7
C6gv54/origin/0/0: 0ad2af981579a62b084b3d41462c5546
C6gv54/translation: 1573c77059bc92df17e661befda8d9b7
nKBUeF/translation: 07e3ce10820b8ab28643e38b2ed51f64
nKBUeF/message: 07e3ce10820b8ab28643e38b2ed51f64
nKBUeF/origin/0/0: 97bd206a05414e2a571020d64e166093
jlB2RY/message: 67a76bf346ab4b48563269e9d941a64a
jlB2RY/origin/0/0: 0627e0040ca4939c9a57349e98c51797
jlB2RY/translation: 67a76bf346ab4b48563269e9d941a64a
@@ -2386,9 +2307,6 @@ checksums:
MHrjPM/message: 344e64395eaff6822a57d18623853e1a
MHrjPM/origin/0/0: a50c4fbde8da6e0a77c4e0aef0b44dea
MHrjPM/translation: 344e64395eaff6822a57d18623853e1a
wK4OTM/translation: b2a89bfd8e13d77f626e84840602a603
wK4OTM/message: b2a89bfd8e13d77f626e84840602a603
wK4OTM/origin/0/0: 97bd206a05414e2a571020d64e166093
Se%2FUIp/message: d60813ea824f373462471e092d136eed
Se%2FUIp/origin/0/0: eb63312c63f6c8d2a5b6520c89012123
Se%2FUIp/origin/1/0: eb63312c63f6c8d2a5b6520c89012123
@@ -2431,10 +2349,9 @@ checksums:
LeM5RY/origin/0/0: 0dccc39d0debbf3e089cb6d292de762b
LeM5RY/translation: dfeac280858332082ad34d2623a59863
W1ewR0/message: ddcac5d14d48358b043a9487b42eec16
W1ewR0/origin/0/0: fc0c18a095a68277a0f90a115942683b
W1ewR0/origin/1/0: 1b05beba6bdd908015fa8e798717f024
W1ewR0/origin/2/0: 9c8b544f5db130cd7c5eda9064ba3957
W1ewR0/origin/3/0: faa8a2576972b4eb31565d8ea6098f43
W1ewR0/origin/0/0: 1b05beba6bdd908015fa8e798717f024
W1ewR0/origin/1/0: 9c8b544f5db130cd7c5eda9064ba3957
W1ewR0/origin/2/0: faa8a2576972b4eb31565d8ea6098f43
W1ewR0/translation: ddcac5d14d48358b043a9487b42eec16
zl1b97/message: 90112ea12ec6fa42097a6e022ae048a4
zl1b97/origin/0/0: 984122104bcc6d8fa6d4c8a35e7751d2
@@ -2467,12 +2384,6 @@ checksums:
2QGEbi/message: 550198b2c87f06726a843c79c1026ed9
2QGEbi/origin/0/0: 4b4f55c033add8db69178563832227c6
2QGEbi/translation: 550198b2c87f06726a843c79c1026ed9
Oo4E6p/translation: af985cf72c47df76e2f2139a427d9948
Oo4E6p/message: af985cf72c47df76e2f2139a427d9948
Oo4E6p/origin/0/0: 97bd206a05414e2a571020d64e166093
K7k9u3/translation: 1cf9d227042f6a65adba38cb7296830d
K7k9u3/message: 1cf9d227042f6a65adba38cb7296830d
K7k9u3/origin/0/0: fd0e66cef8d46e96bcf3143a12f9b6db
8yFGGF/message: 39025a0c53818438829603d213baef06
8yFGGF/origin/0/0: 68450df287fb46ea629e818732c1291a
8yFGGF/translation: 39025a0c53818438829603d213baef06
@@ -2715,9 +2626,9 @@ checksums:
v1kQyJ/origin/0/0: 77c9f6139024b2069e4fdc5120f69cb5
v1kQyJ/origin/1/0: 566b5982085ae649b2be0c614f666591
v1kQyJ/translation: 0329bc102d3ab3c73410d126e119de12
xLTZVf/translation: ce928c74255344905f5c3f02f44c991e
xLTZVf/message: ce928c74255344905f5c3f02f44c991e
xLTZVf/origin/0/0: 5f6c4a0c9be5098099167871ae1564af
xLTZVf/translation: ce928c74255344905f5c3f02f44c991e
9eF5oV/message: 4928884739ba559e6e4b960e80fe1452
9eF5oV/origin/0/0: 4a1be3707d14c696a16f453d132fb248
9eF5oV/translation: 4928884739ba559e6e4b960e80fe1452

View File

@@ -28,7 +28,6 @@ const config = {
],
},
/** Enables hot reloading for local packages without a build step */
transpilePackages: [
"@kan/api",
@@ -66,8 +65,6 @@ const config = {
},
},
},
serverExternalPackages: ["pino"],
experimental: {
// instrumentationHook: true,
swcPlugins: [["@lingui/swc-plugin", {}]],

View File

@@ -1,6 +1,4 @@
import { useTheme } from "next-themes";
import { useRouter } from "next/navigation";
import { env } from "next-runtime-env";
import { useEffect, useRef, useState } from "react";
import {
TbLayoutSidebarLeftCollapse,
@@ -45,7 +43,6 @@ export default function Dashboard({
const { resolvedTheme } = useTheme();
const { openModal } = useModal();
const { availableWorkspaces, hasLoaded } = useWorkspace();
const router = useRouter();
const { data: session, isPending: sessionLoading } = authClient.useSession();
const { data: user, isLoading: userLoading } = api.user.getUser.useQuery(
@@ -101,13 +98,9 @@ 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)}`);
} else {
openModal("NEW_WORKSPACE", undefined, undefined, false);
}
openModal("NEW_WORKSPACE", undefined, undefined, false);
}
}, [hasLoaded, availableWorkspaces.length, openModal, router]);
}, [hasLoaded, availableWorkspaces.length, openModal]);
const isDarkMode = resolvedTheme === "dark";

View File

@@ -1,32 +0,0 @@
import { t } from "@lingui/core/macro";
import { HiOutlineAdjustmentsHorizontal } from "react-icons/hi2";
import { useFontSize, type FontSize } from "~/providers/font-size";
const fontSizeOptions: { value: FontSize; label: () => string }[] = [
{ value: "small", label: () => t`Small` },
{ value: "medium", label: () => t`Medium` },
{ value: "large", label: () => t`Large` },
];
export function FontSizeSelector() {
const { fontSize, setFontSize } = useFontSize();
return (
<div className="relative">
<HiOutlineAdjustmentsHorizontal className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
<select
id="font-size-select"
value={fontSize}
onChange={(e) => setFontSize(e.target.value as FontSize)}
className="block w-full max-w-[180px] rounded-lg border-0 bg-light-50 pl-10 text-sm shadow-sm ring-1 ring-inset ring-light-300 focus:ring-2 focus:ring-inset focus:ring-light-400 dark:bg-dark-50 dark:text-dark-1000 dark:ring-dark-300 dark:focus:ring-dark-500"
>
{fontSizeOptions.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label()}
</option>
))}
</select>
</div>
);
}

View File

@@ -88,7 +88,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
</button>
)}
{iconRight && type !== "password" && (
<div className="absolute right-3 top-1/2 -translate-y-1/2 leading-[0]">
<div className="absolute right-3 top-1/2 -translate-y-1/2">
{iconRight}
</div>
)}

View File

@@ -39,7 +39,7 @@ export function LabelForm({
labelPublicId: entityId,
},
{
enabled: !!isEdit && entityId.length >= 12,
enabled: isEdit && !!entityId,
},
);

View File

@@ -127,7 +127,7 @@ export function NewWorkspaceForm() {
body: JSON.stringify({
slug: slug || undefined,
workspacePublicId: values.publicId,
cancelUrl: window.location.pathname + window.location.search,
cancelUrl: "/settings/workspace?upgrade=pro",
successUrl: "/boards",
}),
},

View File

@@ -1,130 +0,0 @@
import Placeholder from "@tiptap/extension-placeholder";
import { EditorContent, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { useEffect, useRef } from "react";
import { twMerge } from "tailwind-merge";
interface PlainTextEditorProps {
content: string;
onChange?: (value: string) => void;
onBlur?: (value: string) => void;
onEnter?: (value: string) => void;
onEscape?: () => void;
readOnly?: boolean;
placeholder?: string;
className?: string;
}
export default function PlainTextEditor({
content,
onChange,
onBlur,
onEnter,
onEscape,
readOnly = false,
placeholder,
className,
}: PlainTextEditorProps) {
const onEnterRef = useRef(onEnter);
const onEscapeRef = useRef(onEscape);
const onBlurRef = useRef(onBlur);
const onChangeRef = useRef(onChange);
const contentRef = useRef(content);
useEffect(() => {
onEnterRef.current = onEnter;
}, [onEnter]);
useEffect(() => {
onEscapeRef.current = onEscape;
}, [onEscape]);
useEffect(() => {
onBlurRef.current = onBlur;
}, [onBlur]);
useEffect(() => {
onChangeRef.current = onChange;
}, [onChange]);
useEffect(() => {
contentRef.current = content;
}, [content]);
const editor = useEditor(
{
extensions: [
StarterKit.configure({
bold: false,
italic: false,
strike: false,
code: false,
codeBlock: false,
blockquote: false,
heading: false,
bulletList: false,
orderedList: false,
listItem: false,
horizontalRule: false,
hardBreak: false,
}),
Placeholder.configure({ placeholder }),
],
content,
editable: !readOnly,
onUpdate: ({ editor }) => onChangeRef.current?.(editor.getText()),
onBlur: ({ editor }) => onBlurRef.current?.(editor.getText()),
editorProps: {
handleKeyDown: (view, event) => {
if (event.key === "Enter") {
event.preventDefault();
onEnterRef.current?.(view.state.doc.textContent.trim());
return true;
}
if (event.key === "Escape") {
event.preventDefault();
// Reset to original content before calling the callback
editor?.commands.setContent(contentRef.current, false);
editor?.commands.blur();
onEscapeRef.current?.();
return true;
}
return false;
},
attributes: {
class: "outline-none focus:outline-none focus-visible:ring-0",
},
},
},
[],
);
useEffect(() => {
if (!editor) return;
if (content !== editor.getText()) {
editor.commands.setContent(content, false);
}
}, [content, editor]);
useEffect(() => {
if (!editor) return;
editor.setEditable(!readOnly);
}, [readOnly, editor]);
return (
<>
<style jsx global>{`
.plain-text-editor p.is-empty::before {
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
color: var(--placeholder-color, #9ca3af);
}
.plain-text-editor .tiptap p {
margin: 0 !important;
}
`}</style>
<EditorContent
editor={editor}
className={twMerge("plain-text-editor", className)}
/>
</>
);
}

View File

@@ -7,17 +7,15 @@ const Toggle = ({
label,
disabled,
showLabel = true,
labelPosition = "before",
}: {
isChecked: boolean;
onChange: () => void;
label: string;
disabled?: boolean;
showLabel?: boolean;
labelPosition?: "before" | "after";
}) => (
<div className="flex items-center">
{showLabel && labelPosition === "before" && (
<div className="mr-4 flex items-center justify-end">
{showLabel && (
<span className="mr-2 text-xs text-light-900 dark:text-dark-900">
{label}
</span>
@@ -40,11 +38,6 @@ const Toggle = ({
)}
/>
</Switch>
{showLabel && labelPosition === "after" && (
<span className="ml-2 text-xs text-light-900 dark:text-dark-900">
{label}
</span>
)}
</div>
);

View File

@@ -1,7 +1,5 @@
import { Button, Menu, Transition } from "@headlessui/react";
import { t } from "@lingui/core/macro";
import { useRouter } from "next/navigation";
import { env } from "next-runtime-env";
import { Fragment, useState } from "react";
import { HiCheck, HiMagnifyingGlass } from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
@@ -20,7 +18,6 @@ export default function WorkspaceMenu({
const { workspace, isLoading, availableWorkspaces, switchWorkspace } =
useWorkspace();
const { openModal } = useModal();
const router = useRouter();
const [isOpen, setIsOpen] = useState(false);
const { tooltipContent: commandPaletteShortcutTooltipContent } =
@@ -150,11 +147,7 @@ export default function WorkspaceMenu({
<div className="border-t-[1px] border-light-600 p-1 dark:border-dark-500">
<Menu.Item>
<button
onClick={() =>
env("NEXT_PUBLIC_KAN_ENV") === "cloud"
? router.push(`/onboarding/select-plan?returnUrl=${encodeURIComponent(window.location.pathname)}`)
: openModal("NEW_WORKSPACE")
}
onClick={() => openModal("NEW_WORKSPACE")}
className="flex w-full items-center justify-between rounded-[5px] px-3 py-2 text-left text-xs text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
>
{t`Create workspace`}

View File

@@ -71,7 +71,7 @@
"0": ["isTemplate ? \"Template\" : \"Board\""]
},
"comments": [],
"origin": [["src/views/board/index.tsx", 556]],
"origin": [["src/views/board/index.tsx", 440]],
"translation": "{0} nicht gefunden"
},
"0xWkkH": {
@@ -238,13 +238,6 @@
"origin": [["src/views/home/components/Features.tsx", 110]],
"translation": "Aktivitätsprotokolle"
},
"UGCIzB": {
"translation": "Label hinzufügen / bearbeiten",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 50]]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -287,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"translation": "Details hinzufügen..."
},
"lyqwgn": {
@@ -295,8 +288,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/LabelSelector.tsx", 114],
["src/views/card/components/LabelSelector.tsx", 119]
["src/views/card/components/LabelSelector.tsx", 113],
["src/views/card/components/LabelSelector.tsx", 118]
],
"translation": "Label hinzufügen"
},
@@ -305,7 +298,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 135],
["src/views/card/components/MemberSelector.tsx", 134],
["src/views/members/components/InviteMemberForm.tsx", 257]
],
"translation": "Mitglied hinzufügen"
@@ -849,7 +842,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/card/index.tsx", 317],
["src/views/public/board/index.tsx", 125]
],
@@ -1242,7 +1235,6 @@
"origin": [
["src/components/DeleteLabelConfirmation.tsx", 49],
["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 176],
["src/views/board/components/CardContextDuplicateModal.tsx", 267],
["src/views/board/components/DeleteBoardConfirmation.tsx", 44],
["src/views/card/components/Comment.tsx", 195],
["src/views/card/components/DeleteCardConfirmation.tsx", 86],
@@ -1269,13 +1261,6 @@
"origin": [["src/views/card/index.tsx", 317]],
"translation": "Karte"
},
"sU2uWc": {
"translation": "Karte dupliziert",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 67]]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -1298,7 +1283,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 307],
["src/views/card/components/Dropdown.tsx", 44],
["src/views/public/board/CardModal.tsx", 38]
],
@@ -1309,25 +1293,18 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/AccountSettings.tsx", 77],
["src/views/settings/AccountSettings.tsx", 87],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
],
"translation": "Passwort ändern"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 63]],
"translation": "Schriftgröße der Anwendung ändern."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 53]],
"origin": [["src/views/settings/AccountSettings.tsx", 52]],
"translation": "Ändern Sie Ihre Spracheinstellungen."
},
"gZxH/p": {
@@ -1677,40 +1654,6 @@
"origin": [["src/views/card/components/Dropdown.tsx", 58]],
"translation": "Karten-Link kopieren"
},
"0utuU6": {
"translation": "Checklisten kopieren",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 257]
]
},
"fDQ7hA": {
"translation": "Labels kopieren",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 221]
]
},
"1bJKhj": {
"translation": "Link zur Karte kopieren",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 62]]
},
"uvH2xS": {
"translation": "Mitglieder kopieren",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 239]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -1772,7 +1715,7 @@
"message": "Create label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Label erstellen"
},
"XTKtey": {
@@ -1807,7 +1750,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewCardForm.tsx", 417],
["src/views/card/components/LabelSelector.tsx", 101]
["src/views/card/components/LabelSelector.tsx", 100]
],
"translation": "Neues Label erstellen"
},
@@ -1816,8 +1759,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 91],
["src/views/board/index.tsx", 670]
["src/views/board/index.tsx", 76],
["src/views/board/index.tsx", 554]
],
"translation": "Neue Liste erstellen"
},
@@ -2018,8 +1961,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 70],
["src/views/settings/AccountSettings.tsx", 80],
["src/views/settings/AccountSettings.tsx", 59],
["src/views/settings/AccountSettings.tsx", 69],
["src/views/settings/components/DeleteAccountConfirmation.tsx", 96]
],
"translation": "Account löschen"
@@ -2035,10 +1978,7 @@
"message": "Delete card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMenu.tsx", 74],
["src/views/card/components/Dropdown.tsx", 76]
],
"origin": [["src/views/card/components/Dropdown.tsx", 76]],
"translation": "Karte löschen"
},
"njJFtc": {
@@ -2146,7 +2086,7 @@
"message": "Display name",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 36]],
"origin": [["src/views/settings/AccountSettings.tsx", 35]],
"translation": "Anzeigename"
},
"MlyjJu": {
@@ -2224,9 +2164,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 36],
["src/views/board/components/CardContextLabelsModal.tsx", 48],
["src/views/board/components/CardContextMembersModal.tsx", 68],
["src/views/boards/components/TemplateBoards.tsx", 17],
["src/views/boards/components/TemplateBoards.tsx", 23],
["src/views/boards/components/TemplateBoards.tsx", 29],
@@ -2282,34 +2219,6 @@
"origin": [["src/views/board/components/Filters.tsx", 122]],
"translation": "Morgen fällig"
},
"euc6Ns": {
"translation": "Duplizieren",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"BTDPLo": {
"translation": "Karte duplizieren",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 111],
["src/views/board/components/CardContextMenu.tsx", 68]
]
},
"06EQqT": {
"translation": "Wird dupliziert…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -2395,7 +2304,7 @@
"comments": [],
"origin": [
["src/views/members/components/InviteMemberForm.tsx", 272],
["src/views/settings/AccountSettings.tsx", 43]
["src/views/settings/AccountSettings.tsx", 42]
],
"translation": "E-Mail"
},
@@ -2422,15 +2331,6 @@
"origin": [["src/views/settings/WorkspaceSettings.tsx", 100]],
"translation": "E-Mail-Sichtbarkeit"
},
"bFREhu": {
"translation": "Ende der Liste",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 200]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -2868,13 +2768,6 @@
"origin": [["src/views/home/components/Faqs.tsx", 150]],
"translation": "Finden Sie Antworten auf häufig gestellte Fragen zu Kan. Können Sie nicht finden, wonach Sie suchen? <0>Kontaktieren Sie uns</0> gerne."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 60]],
"translation": "Schriftgröße"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -2987,7 +2880,7 @@
"message": "Get started by creating a new list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 655]],
"origin": [["src/views/board/index.tsx", 539]],
"translation": "Legen Sie los, indem Sie eine neue Liste erstellen"
},
"oW13KZ": {
@@ -3294,7 +3187,7 @@
"message": "in",
"placeholders": {},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 167]],
"origin": [["src/components/CommandPallette.tsx", 168]],
"translation": "in"
},
"WbTDwg": {
@@ -3407,7 +3300,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 115],
["src/views/card/components/MemberSelector.tsx", 114],
["src/views/members/components/InviteMemberForm.tsx", 367]
],
"translation": "Mitglied einladen"
@@ -3479,7 +3372,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextLabelsModal.tsx", 31],
["src/views/board/components/Filters.tsx", 155],
["src/views/board/components/NewCardForm.tsx", 421],
["src/views/card/index.tsx", 133]
@@ -3506,16 +3398,9 @@
"message": "Language",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 50]],
"origin": [["src/views/settings/AccountSettings.tsx", 49]],
"translation": "Sprache"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 9]],
"translation": "Groß"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -3591,7 +3476,6 @@
"comments": [],
"origin": [
["src/views/board/components/UpdateBoardSlugButton.tsx", 80],
["src/views/board/index.tsx", 305],
["src/views/card/components/Dropdown.tsx", 42],
["src/views/public/board/CardModal.tsx", 36],
["src/views/public/board/index.tsx", 77]
@@ -3602,10 +3486,7 @@
"message": "List",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 117],
["src/views/card/index.tsx", 124]
],
"origin": [["src/views/card/index.tsx", 124]],
"translation": "Liste"
},
"zQX5Dj": {
@@ -3694,16 +3575,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 91]],
"translation": "Vorlage erstellen"
},
"hB02vO": {
"translation": "Mitglieder verwalten",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMembersModal.tsx", 51],
["src/views/board/components/CardContextMenu.tsx", 38]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -3720,13 +3591,6 @@
"origin": [["src/views/card/components/ActivityList.tsx", 258]],
"translation": "hat Checklistenelement <0>{0}</0> als unvollständig markiert"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 8]],
"translation": "Mittel"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -3778,13 +3642,11 @@
"translation": "Erwähnungen"
},
"hty0d5": {
"translation": "Montag",
"message": "Monday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]
],
"translation": "Montag"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]]
},
"+8Nek/": {
"message": "Monthly",
@@ -3803,20 +3665,6 @@
"origin": [["src/views/members/components/InviteMemberForm.tsx", 159]],
"translation": "Monatliche Abrechnung"
},
"51UCsN": {
"translation": "In andere Liste verschieben",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 44]]
},
"J4+OTA": {
"translation": "In Liste verschieben",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 70]]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -3928,7 +3776,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewListForm.tsx", 113],
["src/views/board/index.tsx", 619]
["src/views/board/index.tsx", 503]
],
"translation": "Neue Liste"
},
@@ -4054,14 +3902,14 @@
"message": "No lists",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 651]],
"origin": [["src/views/board/index.tsx", 535]],
"translation": "Keine Listen"
},
"fvLNDy": {
"message": "No lists have been created yet",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 656]],
"origin": [["src/views/board/index.tsx", 540]],
"translation": "Es wurden noch keine Listen erstellt"
},
"i30J2U": {
@@ -4077,7 +3925,7 @@
"debouncedQuery": ["debouncedQuery"]
},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 183]],
"origin": [["src/components/CommandPallette.tsx", 184]],
"translation": "Keine Ergebnisse für \"{debouncedQuery}\" gefunden."
},
"Q9pQaX": {
@@ -4128,7 +3976,7 @@
"message": "Once you delete your account, there is no going back. This action cannot be undone.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 73]],
"origin": [["src/views/settings/AccountSettings.tsx", 62]],
"translation": "Sobald Sie Ihr Konto löschen, gibt es kein Zurück mehr. Diese Aktion kann nicht rückgängig gemacht werden."
},
"dmKdk0": {
@@ -4453,13 +4301,13 @@
["src/views/board/components/NewTemplateForm.tsx", 77],
["src/views/board/components/UpdateBoardSlugForm.tsx", 73],
["src/views/board/components/VisibilityButton.tsx", 57],
["src/views/board/index.tsx", 217],
["src/views/board/index.tsx", 273],
["src/views/board/index.tsx", 194],
["src/views/board/index.tsx", 250],
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -4509,24 +4357,12 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 76],
["src/views/board/components/CardContextMoveListModal.tsx", 42],
["src/views/board/index.tsx", 314],
["src/views/card/components/Dropdown.tsx", 51],
["src/views/public/board/CardModal.tsx", 45],
["src/views/public/board/index.tsx", 86]
],
"translation": "Bitte versuchen Sie es erneut."
},
"nYNMZZ": {
"translation": "Position in Liste (0 = erste, leer lassen für Ende)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 193]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -4602,7 +4438,7 @@
"message": "Profile picture",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 30]],
"origin": [["src/views/settings/AccountSettings.tsx", 29]],
"translation": "Profilbild"
},
"7d1a0d": {
@@ -4871,13 +4707,11 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Samstag",
"message": "Saturday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]
],
"translation": "Samstag"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]]
},
"tfDRzk": {
"message": "Save",
@@ -4948,15 +4782,6 @@
"origin": [["src/views/home/components/Testimonials.tsx", 239]],
"translation": "Sehen Sie, was unsere Nutzer über Kan sagen"
},
"zYRVNp": {
"translation": "Liste auswählen",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 131]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -5027,11 +4852,7 @@
"message": "Set due date",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 20],
["src/views/board/components/CardContextMenu.tsx", 56],
["src/views/card/components/DueDateSelector.tsx", 121]
],
"origin": [["src/views/card/components/DueDateSelector.tsx", 121]],
"translation": "Fälligkeitsdatum festlegen"
},
"Eit43O": {
@@ -5055,7 +4876,7 @@
"message": "Settings | Account",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 26]],
"origin": [["src/views/settings/AccountSettings.tsx", 25]],
"translation": "Einstellungen | Konto"
},
"iy1wb+": {
@@ -5185,13 +5006,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 7]],
"translation": "Klein"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -5264,13 +5078,11 @@
"translation": "Erfolg"
},
"DBC3t5": {
"translation": "Sonntag",
"message": "Sunday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]
],
"translation": "Sonntag"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -5331,8 +5143,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 565]
["src/views/board/index.tsx", 413],
["src/views/board/index.tsx", 449]
],
"translation": "Vorlage"
},
@@ -5426,13 +5238,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 47]],
"translation": "Das Board wurde wiederhergestellt."
},
"nKBUeF": {
"translation": "Die Karte wurde dupliziert.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 69]]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -5615,15 +5420,6 @@
"origin": [["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 127]],
"translation": "Titel"
},
"wK4OTM": {
"translation": "Titel (optional)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 180]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -5726,7 +5522,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 312],
["src/views/card/components/Dropdown.tsx", 49],
["src/views/public/board/CardModal.tsx", 43],
["src/views/public/board/index.tsx", 84]
@@ -5798,7 +5593,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"translation": "Checklistenelement konnte nicht gelöscht werden"
},
"2QGEbi": {
@@ -5808,20 +5603,6 @@
"origin": [["src/views/card/components/DeleteCommentConfirmation.tsx", 45]],
"translation": "Kommentar konnte nicht gelöscht werden"
},
"Oo4E6p": {
"translation": "Karte konnte nicht dupliziert werden",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 75]]
},
"K7k9u3": {
"translation": "Karte konnte nicht verschoben werden",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 41]]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -5880,7 +5661,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "Karte konnte nicht aktualisiert werden"
@@ -5896,7 +5677,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"translation": "Checklisten-Element konnte nicht aktualisiert werden"
},
"fYVH36": {
@@ -5925,7 +5706,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "Liste konnte nicht aktualisiert werden"
@@ -6095,7 +5876,7 @@
"message": "Update label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Label aktualisieren"
},
"L5ZPjz": {
@@ -6387,11 +6168,11 @@
"translation": "Webhooks"
},
"xLTZVf": {
"translation": "Wochenbeginn",
"message": "Week start day",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]],
"translation": "Wochenbeginn"
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]]
},
"9eF5oV": {
"message": "Welcome back",
@@ -6434,7 +6215,7 @@
"comments": [],
"origin": [
["src/components/SettingsLayout.tsx", 47],
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/boards/index.tsx", 48],
["src/views/members/index.tsx", 258],
["src/views/public/board/index.tsx", 125],
@@ -6613,7 +6394,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 80]],
"translation": "Sie sind dabei, Ihr Passwort zu ändern."
},
"aelko+": {
@@ -6645,8 +6426,8 @@
["src/views/board/components/List.tsx", 117],
["src/views/board/components/UpdateBoardSlugButton.tsx", 58],
["src/views/board/components/VisibilityButton.tsx", 72],
["src/views/board/index.tsx", 603],
["src/views/board/index.tsx", 661],
["src/views/board/index.tsx", 487],
["src/views/board/index.tsx", 545],
["src/views/boards/components/BoardsList.tsx", 71],
["src/views/boards/index.tsx", 59],
["src/views/boards/index.tsx", 80]

File diff suppressed because one or more lines are too long

View File

@@ -123,7 +123,7 @@
"origin": [
[
"src/views/board/index.tsx",
556
440
]
],
"translation": "{0} not found"
@@ -400,18 +400,6 @@
],
"translation": "Activity logs"
},
"UGCIzB": {
"translation": "Add / edit label",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextMenu.tsx",
50
]
]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -483,7 +471,7 @@
"origin": [
[
"src/views/card/components/ChecklistItemRow.tsx",
169
200
]
],
"translation": "Add details..."
@@ -495,11 +483,11 @@
"origin": [
[
"src/views/card/components/LabelSelector.tsx",
114
113
],
[
"src/views/card/components/LabelSelector.tsx",
119
118
]
],
"translation": "Add label"
@@ -511,7 +499,7 @@
"origin": [
[
"src/views/card/components/MemberSelector.tsx",
135
134
],
[
"src/views/members/components/InviteMemberForm.tsx",
@@ -1421,7 +1409,7 @@
"origin": [
[
"src/views/board/index.tsx",
529
413
],
[
"src/views/card/index.tsx",
@@ -2063,10 +2051,6 @@
"src/components/YouTubeEmbed/EditYouTubeModal.tsx",
176
],
[
"src/views/board/components/CardContextDuplicateModal.tsx",
267
],
[
"src/views/board/components/DeleteBoardConfirmation.tsx",
44
@@ -2134,18 +2118,6 @@
],
"translation": "Card"
},
"sU2uWc": {
"translation": "Card duplicated",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
67
]
]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -2179,10 +2151,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/index.tsx",
307
],
[
"src/views/card/components/Dropdown.tsx",
44
@@ -2201,11 +2169,11 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
88
77
],
[
"src/views/settings/AccountSettings.tsx",
98
87
],
[
"src/views/settings/components/ChangePasswordConfirmation.tsx",
@@ -2218,18 +2186,6 @@
],
"translation": "Change Password"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/settings/AccountSettings.tsx",
63
]
],
"translation": "Change the application font size."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
@@ -2237,7 +2193,7 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
53
52
]
],
"translation": "Change your language preferences."
@@ -2814,54 +2770,6 @@
],
"translation": "Copy card link"
},
"0utuU6": {
"translation": "Copy checklists",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
257
]
]
},
"fDQ7hA": {
"translation": "Copy labels",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
221
]
]
},
"1bJKhj": {
"translation": "Copy link to card",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextMenu.tsx",
62
]
]
},
"uvH2xS": {
"translation": "Copy members",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
239
]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -2965,7 +2873,7 @@
"origin": [
[
"src/components/LabelForm.tsx",
236
238
]
],
"translation": "Create label"
@@ -3025,7 +2933,7 @@
],
[
"src/views/card/components/LabelSelector.tsx",
101
100
]
],
"translation": "Create new label"
@@ -3037,11 +2945,11 @@
"origin": [
[
"src/views/board/index.tsx",
91
76
],
[
"src/views/board/index.tsx",
670
554
]
],
"translation": "Create new list"
@@ -3389,11 +3297,11 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
70
59
],
[
"src/views/settings/AccountSettings.tsx",
80
69
],
[
"src/views/settings/components/DeleteAccountConfirmation.tsx",
@@ -3419,10 +3327,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextMenu.tsx",
74
],
[
"src/views/card/components/Dropdown.tsx",
76
@@ -3609,7 +3513,7 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
36
35
]
],
"translation": "Display name"
@@ -3747,18 +3651,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDueDateModal.tsx",
36
],
[
"src/views/board/components/CardContextLabelsModal.tsx",
48
],
[
"src/views/board/components/CardContextMembersModal.tsx",
68
],
[
"src/views/boards/components/TemplateBoards.tsx",
17
@@ -3866,46 +3758,6 @@
],
"translation": "Due tomorrow"
},
"euc6Ns": {
"translation": "Duplicate",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
279
]
]
},
"BTDPLo": {
"translation": "Duplicate card",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
111
],
[
"src/views/board/components/CardContextMenu.tsx",
68
]
]
},
"06EQqT": {
"translation": "Duplicating…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
279
]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -4049,7 +3901,7 @@
],
[
"src/views/settings/AccountSettings.tsx",
43
42
]
],
"translation": "Email"
@@ -4090,18 +3942,6 @@
],
"translation": "Email visibility"
},
"bFREhu": {
"translation": "End of list",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
200
]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -4814,18 +4654,6 @@
],
"translation": "Find answers to common questions about Kan. Can't find what you're looking for? Feel free to <0>contact us</0>."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/settings/AccountSettings.tsx",
60
]
],
"translation": "Font size"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -5029,7 +4857,7 @@
"origin": [
[
"src/views/board/index.tsx",
655
539
]
],
"translation": "Get started by creating a new list"
@@ -5537,7 +5365,7 @@
"origin": [
[
"src/components/CommandPallette.tsx",
167
168
]
],
"translation": "in"
@@ -5729,7 +5557,7 @@
"origin": [
[
"src/views/card/components/MemberSelector.tsx",
115
114
],
[
"src/views/members/components/InviteMemberForm.tsx",
@@ -5847,10 +5675,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextLabelsModal.tsx",
31
],
[
"src/views/board/components/Filters.tsx",
155
@@ -5897,23 +5721,11 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
50
49
]
],
"translation": "Language"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [
[
"src/components/FontSizeSelector.tsx",
9
]
],
"translation": "Large"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -6039,10 +5851,6 @@
"src/views/board/components/UpdateBoardSlugButton.tsx",
80
],
[
"src/views/board/index.tsx",
305
],
[
"src/views/card/components/Dropdown.tsx",
42
@@ -6063,10 +5871,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
117
],
[
"src/views/card/index.tsx",
124
@@ -6218,22 +6022,6 @@
],
"translation": "Make template"
},
"hB02vO": {
"translation": "Manage members",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextMembersModal.tsx",
51
],
[
"src/views/board/components/CardContextMenu.tsx",
38
]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -6262,18 +6050,6 @@
],
"translation": "marked checklist item <0>{0}</0> as incomplete"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [
[
"src/components/FontSizeSelector.tsx",
8
]
],
"translation": "Medium"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -6367,6 +6143,7 @@
"translation": "Mentions"
},
"hty0d5": {
"translation": "Monday",
"message": "Monday",
"placeholders": {},
"comments": [],
@@ -6375,8 +6152,7 @@
"src/views/settings/components/UpdateWeekStartDayForm.tsx",
53
]
],
"translation": "Monday"
]
},
"+8Nek/": {
"message": "Monthly",
@@ -6406,30 +6182,6 @@
],
"translation": "monthly billing"
},
"51UCsN": {
"translation": "Move to another list",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextMenu.tsx",
44
]
]
},
"J4+OTA": {
"translation": "Move to list",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextMoveListModal.tsx",
70
]
]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -6628,7 +6380,7 @@
],
[
"src/views/board/index.tsx",
619
503
]
],
"translation": "New list"
@@ -6836,7 +6588,7 @@
"origin": [
[
"src/views/board/index.tsx",
651
535
]
],
"translation": "No lists"
@@ -6848,7 +6600,7 @@
"origin": [
[
"src/views/board/index.tsx",
656
540
]
],
"translation": "No lists have been created yet"
@@ -6876,7 +6628,7 @@
"origin": [
[
"src/components/CommandPallette.tsx",
183
184
]
],
"translation": "No results found for \"{debouncedQuery}\"."
@@ -6960,7 +6712,7 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
73
62
]
],
"translation": "Once you delete your account, there is no going back. This action cannot be undone."
@@ -7512,11 +7264,11 @@
],
[
"src/views/board/index.tsx",
217
194
],
[
"src/views/board/index.tsx",
273
250
],
[
"src/views/boards/components/ImportBoardsForm.tsx",
@@ -7532,11 +7284,11 @@
],
[
"src/views/card/components/ChecklistItemRow.tsx",
67
69
],
[
"src/views/card/components/ChecklistItemRow.tsx",
95
97
],
[
"src/views/card/components/ChecklistNameInput.tsx",
@@ -7686,18 +7438,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
76
],
[
"src/views/board/components/CardContextMoveListModal.tsx",
42
],
[
"src/views/board/index.tsx",
314
],
[
"src/views/card/components/Dropdown.tsx",
51
@@ -7713,18 +7453,6 @@
],
"translation": "Please try again."
},
"nYNMZZ": {
"translation": "Position in list (0 = first, leave empty for end)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
193
]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -7864,7 +7592,7 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
30
29
]
],
"translation": "Profile picture"
@@ -8317,6 +8045,7 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Saturday",
"message": "Saturday",
"placeholders": {},
"comments": [],
@@ -8325,8 +8054,7 @@
"src/views/settings/components/UpdateWeekStartDayForm.tsx",
54
]
],
"translation": "Saturday"
]
},
"tfDRzk": {
"message": "Save",
@@ -8444,18 +8172,6 @@
],
"translation": "See what our users are saying about Kan"
},
"zYRVNp": {
"translation": "Select a list",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
131
]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -8573,14 +8289,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDueDateModal.tsx",
20
],
[
"src/views/board/components/CardContextMenu.tsx",
56
],
[
"src/views/card/components/DueDateSelector.tsx",
121
@@ -8623,7 +8331,7 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
26
25
]
],
"translation": "Settings | Account"
@@ -8840,18 +8548,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [
[
"src/components/FontSizeSelector.tsx",
7
]
],
"translation": "Small"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -8969,6 +8665,7 @@
"translation": "Success"
},
"DBC3t5": {
"translation": "Sunday",
"message": "Sunday",
"placeholders": {},
"comments": [],
@@ -8977,8 +8674,7 @@
"src/views/settings/components/UpdateWeekStartDayForm.tsx",
52
]
],
"translation": "Sunday"
]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -9075,11 +8771,11 @@
"origin": [
[
"src/views/board/index.tsx",
529
413
],
[
"src/views/board/index.tsx",
565
449
]
],
"translation": "Template"
@@ -9236,18 +8932,6 @@
],
"translation": "The board has been unarchived."
},
"nKBUeF": {
"translation": "The card has been duplicated.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
69
]
]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -9536,18 +9220,6 @@
],
"translation": "Title"
},
"wK4OTM": {
"translation": "Title (optional)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
180
]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -9717,10 +9389,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/index.tsx",
312
],
[
"src/views/card/components/Dropdown.tsx",
49
@@ -9843,7 +9511,7 @@
"origin": [
[
"src/views/card/components/ChecklistItemRow.tsx",
94
96
]
],
"translation": "Unable to delete checklist item"
@@ -9860,30 +9528,6 @@
],
"translation": "Unable to delete comment"
},
"Oo4E6p": {
"translation": "Unable to duplicate card",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
75
]
]
},
"K7k9u3": {
"translation": "Unable to move card",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextMoveListModal.tsx",
41
]
]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -9975,7 +9619,7 @@
"origin": [
[
"src/views/board/index.tsx",
272
249
],
[
"src/views/card/index.tsx",
@@ -10003,7 +9647,7 @@
"origin": [
[
"src/views/card/components/ChecklistItemRow.tsx",
66
68
]
],
"translation": "Unable to update checklist item"
@@ -10051,7 +9695,7 @@
"origin": [
[
"src/views/board/index.tsx",
216
193
],
[
"src/views/card/components/ListSelector.tsx",
@@ -10363,7 +10007,7 @@
"origin": [
[
"src/components/LabelForm.tsx",
236
238
]
],
"translation": "Update label"
@@ -10853,6 +10497,7 @@
"translation": "Webhooks"
},
"xLTZVf": {
"translation": "Week start day",
"message": "Week start day",
"placeholders": {},
"comments": [],
@@ -10861,8 +10506,7 @@
"src/views/settings/WorkspaceSettings.tsx",
91
]
],
"translation": "Week start day"
]
},
"9eF5oV": {
"message": "Welcome back",
@@ -10935,7 +10579,7 @@
],
[
"src/views/board/index.tsx",
529
413
],
[
"src/views/boards/index.tsx",
@@ -11223,7 +10867,7 @@
"origin": [
[
"src/views/settings/AccountSettings.tsx",
91
80
]
],
"translation": "You are about to change your password."
@@ -11283,11 +10927,11 @@
],
[
"src/views/board/index.tsx",
603
487
],
[
"src/views/board/index.tsx",
661
545
],
[
"src/views/boards/components/BoardsList.tsx",

File diff suppressed because one or more lines are too long

View File

@@ -71,7 +71,7 @@
"0": ["isTemplate ? \"Template\" : \"Board\""]
},
"comments": [],
"origin": [["src/views/board/index.tsx", 556]],
"origin": [["src/views/board/index.tsx", 440]],
"translation": "{0} no encontrado"
},
"0xWkkH": {
@@ -238,13 +238,6 @@
"origin": [["src/views/home/components/Features.tsx", 110]],
"translation": "Registros de actividad"
},
"UGCIzB": {
"translation": "Añadir / editar etiqueta",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 50]]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -287,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"translation": "Añadir detalles..."
},
"lyqwgn": {
@@ -295,8 +288,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/LabelSelector.tsx", 114],
["src/views/card/components/LabelSelector.tsx", 119]
["src/views/card/components/LabelSelector.tsx", 113],
["src/views/card/components/LabelSelector.tsx", 118]
],
"translation": "Añadir etiqueta"
},
@@ -305,7 +298,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 135],
["src/views/card/components/MemberSelector.tsx", 134],
["src/views/members/components/InviteMemberForm.tsx", 257]
],
"translation": "Añadir miembro"
@@ -849,7 +842,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/card/index.tsx", 317],
["src/views/public/board/index.tsx", 125]
],
@@ -1242,7 +1235,6 @@
"origin": [
["src/components/DeleteLabelConfirmation.tsx", 49],
["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 176],
["src/views/board/components/CardContextDuplicateModal.tsx", 267],
["src/views/board/components/DeleteBoardConfirmation.tsx", 44],
["src/views/card/components/Comment.tsx", 195],
["src/views/card/components/DeleteCardConfirmation.tsx", 86],
@@ -1269,13 +1261,6 @@
"origin": [["src/views/card/index.tsx", 317]],
"translation": "Tarjeta"
},
"sU2uWc": {
"translation": "Tarjeta duplicada",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 67]]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -1298,7 +1283,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 307],
["src/views/card/components/Dropdown.tsx", 44],
["src/views/public/board/CardModal.tsx", 38]
],
@@ -1309,25 +1293,18 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/AccountSettings.tsx", 77],
["src/views/settings/AccountSettings.tsx", 87],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
],
"translation": "Cambiar contraseña"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 63]],
"translation": "Cambia el tamaño de fuente de la aplicación."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 53]],
"origin": [["src/views/settings/AccountSettings.tsx", 52]],
"translation": "Cambia tus preferencias de idioma."
},
"gZxH/p": {
@@ -1677,40 +1654,6 @@
"origin": [["src/views/card/components/Dropdown.tsx", 58]],
"translation": "Copiar enlace de la tarjeta"
},
"0utuU6": {
"translation": "Copiar listas de verificación",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 257]
]
},
"fDQ7hA": {
"translation": "Copiar etiquetas",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 221]
]
},
"1bJKhj": {
"translation": "Copiar enlace a la tarjeta",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 62]]
},
"uvH2xS": {
"translation": "Copiar miembros",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 239]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -1772,7 +1715,7 @@
"message": "Create label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Crear etiqueta"
},
"XTKtey": {
@@ -1807,7 +1750,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewCardForm.tsx", 417],
["src/views/card/components/LabelSelector.tsx", 101]
["src/views/card/components/LabelSelector.tsx", 100]
],
"translation": "Crear nueva etiqueta"
},
@@ -1816,8 +1759,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 91],
["src/views/board/index.tsx", 670]
["src/views/board/index.tsx", 76],
["src/views/board/index.tsx", 554]
],
"translation": "Crear nueva lista"
},
@@ -2018,8 +1961,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 70],
["src/views/settings/AccountSettings.tsx", 80],
["src/views/settings/AccountSettings.tsx", 59],
["src/views/settings/AccountSettings.tsx", 69],
["src/views/settings/components/DeleteAccountConfirmation.tsx", 96]
],
"translation": "Eliminar cuenta"
@@ -2035,10 +1978,7 @@
"message": "Delete card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMenu.tsx", 74],
["src/views/card/components/Dropdown.tsx", 76]
],
"origin": [["src/views/card/components/Dropdown.tsx", 76]],
"translation": "Eliminar tarjeta"
},
"njJFtc": {
@@ -2146,7 +2086,7 @@
"message": "Display name",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 36]],
"origin": [["src/views/settings/AccountSettings.tsx", 35]],
"translation": "Nombre para mostrar"
},
"MlyjJu": {
@@ -2224,9 +2164,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 36],
["src/views/board/components/CardContextLabelsModal.tsx", 48],
["src/views/board/components/CardContextMembersModal.tsx", 68],
["src/views/boards/components/TemplateBoards.tsx", 17],
["src/views/boards/components/TemplateBoards.tsx", 23],
["src/views/boards/components/TemplateBoards.tsx", 29],
@@ -2282,34 +2219,6 @@
"origin": [["src/views/board/components/Filters.tsx", 122]],
"translation": "Vence mañana"
},
"euc6Ns": {
"translation": "Duplicar",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"BTDPLo": {
"translation": "Duplicar tarjeta",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 111],
["src/views/board/components/CardContextMenu.tsx", 68]
]
},
"06EQqT": {
"translation": "Duplicando…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -2395,7 +2304,7 @@
"comments": [],
"origin": [
["src/views/members/components/InviteMemberForm.tsx", 272],
["src/views/settings/AccountSettings.tsx", 43]
["src/views/settings/AccountSettings.tsx", 42]
],
"translation": "Email"
},
@@ -2422,15 +2331,6 @@
"origin": [["src/views/settings/WorkspaceSettings.tsx", 100]],
"translation": "Visibilidad del email"
},
"bFREhu": {
"translation": "Fin de la lista",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 200]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -2868,13 +2768,6 @@
"origin": [["src/views/home/components/Faqs.tsx", 150]],
"translation": "Encuentra respuestas a preguntas frecuentes sobre Kan. ¿No encuentras lo que buscas? No dudes en <0>contactarnos</0>."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 60]],
"translation": "Tamaño de fuente"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -2987,7 +2880,7 @@
"message": "Get started by creating a new list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 655]],
"origin": [["src/views/board/index.tsx", 539]],
"translation": "Comienza creando una nueva lista"
},
"oW13KZ": {
@@ -3294,7 +3187,7 @@
"message": "in",
"placeholders": {},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 167]],
"origin": [["src/components/CommandPallette.tsx", 168]],
"translation": "en"
},
"WbTDwg": {
@@ -3407,7 +3300,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 115],
["src/views/card/components/MemberSelector.tsx", 114],
["src/views/members/components/InviteMemberForm.tsx", 367]
],
"translation": "Invitar miembro"
@@ -3479,7 +3372,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextLabelsModal.tsx", 31],
["src/views/board/components/Filters.tsx", 155],
["src/views/board/components/NewCardForm.tsx", 421],
["src/views/card/index.tsx", 133]
@@ -3506,16 +3398,9 @@
"message": "Language",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 50]],
"origin": [["src/views/settings/AccountSettings.tsx", 49]],
"translation": "Idioma"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 9]],
"translation": "Grande"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -3591,7 +3476,6 @@
"comments": [],
"origin": [
["src/views/board/components/UpdateBoardSlugButton.tsx", 80],
["src/views/board/index.tsx", 305],
["src/views/card/components/Dropdown.tsx", 42],
["src/views/public/board/CardModal.tsx", 36],
["src/views/public/board/index.tsx", 77]
@@ -3602,10 +3486,7 @@
"message": "List",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 117],
["src/views/card/index.tsx", 124]
],
"origin": [["src/views/card/index.tsx", 124]],
"translation": "Lista"
},
"zQX5Dj": {
@@ -3694,16 +3575,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 91]],
"translation": "Crear plantilla"
},
"hB02vO": {
"translation": "Gestionar miembros",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMembersModal.tsx", 51],
["src/views/board/components/CardContextMenu.tsx", 38]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -3720,13 +3591,6 @@
"origin": [["src/views/card/components/ActivityList.tsx", 258]],
"translation": "marcó el elemento de la lista de verificación <0>{0}</0> como incompleto"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 8]],
"translation": "Mediano"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -3778,13 +3642,11 @@
"translation": "Menciones"
},
"hty0d5": {
"translation": "Lunes",
"message": "Monday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]
],
"translation": "Lunes"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]]
},
"+8Nek/": {
"message": "Monthly",
@@ -3803,20 +3665,6 @@
"origin": [["src/views/members/components/InviteMemberForm.tsx", 159]],
"translation": "facturación mensual"
},
"51UCsN": {
"translation": "Mover a otra lista",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 44]]
},
"J4+OTA": {
"translation": "Mover a lista",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 70]]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -3928,7 +3776,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewListForm.tsx", 113],
["src/views/board/index.tsx", 619]
["src/views/board/index.tsx", 503]
],
"translation": "Nueva lista"
},
@@ -4054,14 +3902,14 @@
"message": "No lists",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 651]],
"origin": [["src/views/board/index.tsx", 535]],
"translation": "Sin listas"
},
"fvLNDy": {
"message": "No lists have been created yet",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 656]],
"origin": [["src/views/board/index.tsx", 540]],
"translation": "Aún no se han creado listas"
},
"i30J2U": {
@@ -4077,7 +3925,7 @@
"debouncedQuery": ["debouncedQuery"]
},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 183]],
"origin": [["src/components/CommandPallette.tsx", 184]],
"translation": "No se encontraron resultados para \"{debouncedQuery}\"."
},
"Q9pQaX": {
@@ -4128,7 +3976,7 @@
"message": "Once you delete your account, there is no going back. This action cannot be undone.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 73]],
"origin": [["src/views/settings/AccountSettings.tsx", 62]],
"translation": "Una vez que elimines tu cuenta, no hay vuelta atrás. Esta acción no se puede deshacer."
},
"dmKdk0": {
@@ -4453,13 +4301,13 @@
["src/views/board/components/NewTemplateForm.tsx", 77],
["src/views/board/components/UpdateBoardSlugForm.tsx", 73],
["src/views/board/components/VisibilityButton.tsx", 57],
["src/views/board/index.tsx", 217],
["src/views/board/index.tsx", 273],
["src/views/board/index.tsx", 194],
["src/views/board/index.tsx", 250],
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -4509,24 +4357,12 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 76],
["src/views/board/components/CardContextMoveListModal.tsx", 42],
["src/views/board/index.tsx", 314],
["src/views/card/components/Dropdown.tsx", 51],
["src/views/public/board/CardModal.tsx", 45],
["src/views/public/board/index.tsx", 86]
],
"translation": "Por favor, inténtalo de nuevo."
},
"nYNMZZ": {
"translation": "Posición en la lista (0 = primero, dejar vacío para el final)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 193]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -4602,7 +4438,7 @@
"message": "Profile picture",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 30]],
"origin": [["src/views/settings/AccountSettings.tsx", 29]],
"translation": "Foto de perfil"
},
"7d1a0d": {
@@ -4871,13 +4707,11 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Sábado",
"message": "Saturday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]
],
"translation": "Sábado"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]]
},
"tfDRzk": {
"message": "Save",
@@ -4948,15 +4782,6 @@
"origin": [["src/views/home/components/Testimonials.tsx", 239]],
"translation": "Mira lo que nuestros usuarios dicen sobre Kan"
},
"zYRVNp": {
"translation": "Seleccionar una lista",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 131]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -5027,11 +4852,7 @@
"message": "Set due date",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 20],
["src/views/board/components/CardContextMenu.tsx", 56],
["src/views/card/components/DueDateSelector.tsx", 121]
],
"origin": [["src/views/card/components/DueDateSelector.tsx", 121]],
"translation": "Establecer fecha de vencimiento"
},
"Eit43O": {
@@ -5055,7 +4876,7 @@
"message": "Settings | Account",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 26]],
"origin": [["src/views/settings/AccountSettings.tsx", 25]],
"translation": "Configuración | Cuenta"
},
"iy1wb+": {
@@ -5185,13 +5006,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 7]],
"translation": "Pequeño"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -5264,13 +5078,11 @@
"translation": "Éxito"
},
"DBC3t5": {
"translation": "Domingo",
"message": "Sunday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]
],
"translation": "Domingo"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -5331,8 +5143,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 565]
["src/views/board/index.tsx", 413],
["src/views/board/index.tsx", 449]
],
"translation": "Plantilla"
},
@@ -5426,13 +5238,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 47]],
"translation": "El tablero ha sido desarchivado."
},
"nKBUeF": {
"translation": "La tarjeta ha sido duplicada.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 69]]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -5615,15 +5420,6 @@
"origin": [["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 127]],
"translation": "Título"
},
"wK4OTM": {
"translation": "Título (opcional)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 180]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -5726,7 +5522,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 312],
["src/views/card/components/Dropdown.tsx", 49],
["src/views/public/board/CardModal.tsx", 43],
["src/views/public/board/index.tsx", 84]
@@ -5798,7 +5593,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"translation": "No se pudo eliminar el elemento de la lista de verificación"
},
"2QGEbi": {
@@ -5808,20 +5603,6 @@
"origin": [["src/views/card/components/DeleteCommentConfirmation.tsx", 45]],
"translation": "No se pudo eliminar el comentario"
},
"Oo4E6p": {
"translation": "No se puede duplicar la tarjeta",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 75]]
},
"K7k9u3": {
"translation": "No se puede mover la tarjeta",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 41]]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -5880,7 +5661,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "No se pudo actualizar la tarjeta"
@@ -5896,7 +5677,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"translation": "No se pudo actualizar el elemento de la lista de verificación"
},
"fYVH36": {
@@ -5925,7 +5706,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "No se pudo actualizar la lista"
@@ -6095,7 +5876,7 @@
"message": "Update label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Actualizar etiqueta"
},
"L5ZPjz": {
@@ -6387,11 +6168,11 @@
"translation": "Webhooks"
},
"xLTZVf": {
"translation": "Día de inicio de semana",
"message": "Week start day",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]],
"translation": "Día de inicio de semana"
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]]
},
"9eF5oV": {
"message": "Welcome back",
@@ -6434,7 +6215,7 @@
"comments": [],
"origin": [
["src/components/SettingsLayout.tsx", 47],
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/boards/index.tsx", 48],
["src/views/members/index.tsx", 258],
["src/views/public/board/index.tsx", 125],
@@ -6613,7 +6394,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 80]],
"translation": "Estás a punto de cambiar tu contraseña."
},
"aelko+": {
@@ -6645,8 +6426,8 @@
["src/views/board/components/List.tsx", 117],
["src/views/board/components/UpdateBoardSlugButton.tsx", 58],
["src/views/board/components/VisibilityButton.tsx", 72],
["src/views/board/index.tsx", 603],
["src/views/board/index.tsx", 661],
["src/views/board/index.tsx", 487],
["src/views/board/index.tsx", 545],
["src/views/boards/components/BoardsList.tsx", 71],
["src/views/boards/index.tsx", 59],
["src/views/boards/index.tsx", 80]

File diff suppressed because one or more lines are too long

View File

@@ -71,7 +71,7 @@
"0": ["isTemplate ? \"Template\" : \"Board\""]
},
"comments": [],
"origin": [["src/views/board/index.tsx", 556]],
"origin": [["src/views/board/index.tsx", 440]],
"translation": "{0} introuvable"
},
"0xWkkH": {
@@ -238,13 +238,6 @@
"origin": [["src/views/home/components/Features.tsx", 110]],
"translation": "Journaux d'activité"
},
"UGCIzB": {
"translation": "Ajouter / modifier l'étiquette",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 50]]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -287,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"translation": "Ajouter des détails..."
},
"lyqwgn": {
@@ -295,8 +288,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/LabelSelector.tsx", 114],
["src/views/card/components/LabelSelector.tsx", 119]
["src/views/card/components/LabelSelector.tsx", 113],
["src/views/card/components/LabelSelector.tsx", 118]
],
"translation": "Ajouter une étiquette"
},
@@ -305,7 +298,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 135],
["src/views/card/components/MemberSelector.tsx", 134],
["src/views/members/components/InviteMemberForm.tsx", 257]
],
"translation": "Ajouter un membre"
@@ -849,7 +842,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/card/index.tsx", 317],
["src/views/public/board/index.tsx", 125]
],
@@ -1242,7 +1235,6 @@
"origin": [
["src/components/DeleteLabelConfirmation.tsx", 49],
["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 176],
["src/views/board/components/CardContextDuplicateModal.tsx", 267],
["src/views/board/components/DeleteBoardConfirmation.tsx", 44],
["src/views/card/components/Comment.tsx", 195],
["src/views/card/components/DeleteCardConfirmation.tsx", 86],
@@ -1269,13 +1261,6 @@
"origin": [["src/views/card/index.tsx", 317]],
"translation": "Carte"
},
"sU2uWc": {
"translation": "Carte dupliquée",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 67]]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -1298,7 +1283,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 307],
["src/views/card/components/Dropdown.tsx", 44],
["src/views/public/board/CardModal.tsx", 38]
],
@@ -1309,25 +1293,18 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/AccountSettings.tsx", 77],
["src/views/settings/AccountSettings.tsx", 87],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
],
"translation": "Changer le mot de passe"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 63]],
"translation": "Modifier la taille de police de l'application."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 53]],
"origin": [["src/views/settings/AccountSettings.tsx", 52]],
"translation": "Modifiez vos préférences de langue."
},
"gZxH/p": {
@@ -1677,40 +1654,6 @@
"origin": [["src/views/card/components/Dropdown.tsx", 58]],
"translation": "Copier le lien de la carte"
},
"0utuU6": {
"translation": "Copier les listes de contrôle",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 257]
]
},
"fDQ7hA": {
"translation": "Copier les étiquettes",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 221]
]
},
"1bJKhj": {
"translation": "Copier le lien vers la carte",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 62]]
},
"uvH2xS": {
"translation": "Copier les membres",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 239]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -1772,7 +1715,7 @@
"message": "Create label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Créer une étiquette"
},
"XTKtey": {
@@ -1807,7 +1750,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewCardForm.tsx", 417],
["src/views/card/components/LabelSelector.tsx", 101]
["src/views/card/components/LabelSelector.tsx", 100]
],
"translation": "Créer une nouvelle étiquette"
},
@@ -1816,8 +1759,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 91],
["src/views/board/index.tsx", 670]
["src/views/board/index.tsx", 76],
["src/views/board/index.tsx", 554]
],
"translation": "Créer une nouvelle liste"
},
@@ -2018,8 +1961,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 70],
["src/views/settings/AccountSettings.tsx", 80],
["src/views/settings/AccountSettings.tsx", 59],
["src/views/settings/AccountSettings.tsx", 69],
["src/views/settings/components/DeleteAccountConfirmation.tsx", 96]
],
"translation": "Supprimer le compte"
@@ -2035,10 +1978,7 @@
"message": "Delete card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMenu.tsx", 74],
["src/views/card/components/Dropdown.tsx", 76]
],
"origin": [["src/views/card/components/Dropdown.tsx", 76]],
"translation": "Supprimer la carte"
},
"njJFtc": {
@@ -2146,7 +2086,7 @@
"message": "Display name",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 36]],
"origin": [["src/views/settings/AccountSettings.tsx", 35]],
"translation": "Nom d'affichage"
},
"MlyjJu": {
@@ -2224,9 +2164,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 36],
["src/views/board/components/CardContextLabelsModal.tsx", 48],
["src/views/board/components/CardContextMembersModal.tsx", 68],
["src/views/boards/components/TemplateBoards.tsx", 17],
["src/views/boards/components/TemplateBoards.tsx", 23],
["src/views/boards/components/TemplateBoards.tsx", 29],
@@ -2282,34 +2219,6 @@
"origin": [["src/views/board/components/Filters.tsx", 122]],
"translation": "Échéance demain"
},
"euc6Ns": {
"translation": "Dupliquer",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"BTDPLo": {
"translation": "Dupliquer la carte",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 111],
["src/views/board/components/CardContextMenu.tsx", 68]
]
},
"06EQqT": {
"translation": "Duplication en cours…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -2395,7 +2304,7 @@
"comments": [],
"origin": [
["src/views/members/components/InviteMemberForm.tsx", 272],
["src/views/settings/AccountSettings.tsx", 43]
["src/views/settings/AccountSettings.tsx", 42]
],
"translation": "E-mail"
},
@@ -2422,15 +2331,6 @@
"origin": [["src/views/settings/WorkspaceSettings.tsx", 100]],
"translation": "Visibilité de l'e-mail"
},
"bFREhu": {
"translation": "Fin de la liste",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 200]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -2868,13 +2768,6 @@
"origin": [["src/views/home/components/Faqs.tsx", 150]],
"translation": "Trouvez des réponses aux questions courantes sur Kan. Vous ne trouvez pas ce que vous cherchez ? N'hésitez pas à <0>nous contacter</0>."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 60]],
"translation": "Taille de police"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -2987,7 +2880,7 @@
"message": "Get started by creating a new list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 655]],
"origin": [["src/views/board/index.tsx", 539]],
"translation": "Commencez en créant une nouvelle liste"
},
"oW13KZ": {
@@ -3294,7 +3187,7 @@
"message": "in",
"placeholders": {},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 167]],
"origin": [["src/components/CommandPallette.tsx", 168]],
"translation": "dans"
},
"WbTDwg": {
@@ -3407,7 +3300,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 115],
["src/views/card/components/MemberSelector.tsx", 114],
["src/views/members/components/InviteMemberForm.tsx", 367]
],
"translation": "Inviter un membre"
@@ -3479,7 +3372,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextLabelsModal.tsx", 31],
["src/views/board/components/Filters.tsx", 155],
["src/views/board/components/NewCardForm.tsx", 421],
["src/views/card/index.tsx", 133]
@@ -3506,16 +3398,9 @@
"message": "Language",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 50]],
"origin": [["src/views/settings/AccountSettings.tsx", 49]],
"translation": "Langue"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 9]],
"translation": "Grande"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -3591,7 +3476,6 @@
"comments": [],
"origin": [
["src/views/board/components/UpdateBoardSlugButton.tsx", 80],
["src/views/board/index.tsx", 305],
["src/views/card/components/Dropdown.tsx", 42],
["src/views/public/board/CardModal.tsx", 36],
["src/views/public/board/index.tsx", 77]
@@ -3602,10 +3486,7 @@
"message": "List",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 117],
["src/views/card/index.tsx", 124]
],
"origin": [["src/views/card/index.tsx", 124]],
"translation": "Liste"
},
"zQX5Dj": {
@@ -3694,16 +3575,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 91]],
"translation": "Créer un modèle"
},
"hB02vO": {
"translation": "Gérer les membres",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMembersModal.tsx", 51],
["src/views/board/components/CardContextMenu.tsx", 38]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -3720,13 +3591,6 @@
"origin": [["src/views/card/components/ActivityList.tsx", 258]],
"translation": "a marqué l'élément de checklist <0>{0}</0> comme incomplet"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 8]],
"translation": "Moyenne"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -3778,13 +3642,11 @@
"translation": "Mentions"
},
"hty0d5": {
"translation": "Lundi",
"message": "Monday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]
],
"translation": "Lundi"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]]
},
"+8Nek/": {
"message": "Monthly",
@@ -3803,20 +3665,6 @@
"origin": [["src/views/members/components/InviteMemberForm.tsx", 159]],
"translation": "facturation mensuelle"
},
"51UCsN": {
"translation": "Déplacer vers une autre liste",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 44]]
},
"J4+OTA": {
"translation": "Déplacer vers la liste",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 70]]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -3928,7 +3776,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewListForm.tsx", 113],
["src/views/board/index.tsx", 619]
["src/views/board/index.tsx", 503]
],
"translation": "Nouvelle liste"
},
@@ -4054,14 +3902,14 @@
"message": "No lists",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 651]],
"origin": [["src/views/board/index.tsx", 535]],
"translation": "Aucune liste"
},
"fvLNDy": {
"message": "No lists have been created yet",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 656]],
"origin": [["src/views/board/index.tsx", 540]],
"translation": "Aucune liste n'a encore été créée"
},
"i30J2U": {
@@ -4077,7 +3925,7 @@
"debouncedQuery": ["debouncedQuery"]
},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 183]],
"origin": [["src/components/CommandPallette.tsx", 184]],
"translation": "Aucun résultat trouvé pour « {debouncedQuery} »."
},
"Q9pQaX": {
@@ -4128,7 +3976,7 @@
"message": "Once you delete your account, there is no going back. This action cannot be undone.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 73]],
"origin": [["src/views/settings/AccountSettings.tsx", 62]],
"translation": "Une fois votre compte supprimé, il n'y a pas de retour en arrière possible. Cette action ne peut pas être annulée."
},
"dmKdk0": {
@@ -4453,13 +4301,13 @@
["src/views/board/components/NewTemplateForm.tsx", 77],
["src/views/board/components/UpdateBoardSlugForm.tsx", 73],
["src/views/board/components/VisibilityButton.tsx", 57],
["src/views/board/index.tsx", 217],
["src/views/board/index.tsx", 273],
["src/views/board/index.tsx", 194],
["src/views/board/index.tsx", 250],
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -4509,24 +4357,12 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 76],
["src/views/board/components/CardContextMoveListModal.tsx", 42],
["src/views/board/index.tsx", 314],
["src/views/card/components/Dropdown.tsx", 51],
["src/views/public/board/CardModal.tsx", 45],
["src/views/public/board/index.tsx", 86]
],
"translation": "Veuillez réessayer."
},
"nYNMZZ": {
"translation": "Position dans la liste (0 = premier, laisser vide pour la fin)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 193]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -4602,7 +4438,7 @@
"message": "Profile picture",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 30]],
"origin": [["src/views/settings/AccountSettings.tsx", 29]],
"translation": "Photo de profil"
},
"7d1a0d": {
@@ -4871,13 +4707,11 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Samedi",
"message": "Saturday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]
],
"translation": "Samedi"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]]
},
"tfDRzk": {
"message": "Save",
@@ -4948,15 +4782,6 @@
"origin": [["src/views/home/components/Testimonials.tsx", 239]],
"translation": "Découvrez ce que nos utilisateurs disent de Kan"
},
"zYRVNp": {
"translation": "Sélectionner une liste",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 131]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -5027,11 +4852,7 @@
"message": "Set due date",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 20],
["src/views/board/components/CardContextMenu.tsx", 56],
["src/views/card/components/DueDateSelector.tsx", 121]
],
"origin": [["src/views/card/components/DueDateSelector.tsx", 121]],
"translation": "Définir la date d'échéance"
},
"Eit43O": {
@@ -5055,7 +4876,7 @@
"message": "Settings | Account",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 26]],
"origin": [["src/views/settings/AccountSettings.tsx", 25]],
"translation": "Paramètres | Compte"
},
"iy1wb+": {
@@ -5185,13 +5006,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 7]],
"translation": "Petite"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -5264,13 +5078,11 @@
"translation": "Succès"
},
"DBC3t5": {
"translation": "Dimanche",
"message": "Sunday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]
],
"translation": "Dimanche"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -5331,8 +5143,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 565]
["src/views/board/index.tsx", 413],
["src/views/board/index.tsx", 449]
],
"translation": "Modèle"
},
@@ -5426,13 +5238,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 47]],
"translation": "Le tableau a été désarchivé."
},
"nKBUeF": {
"translation": "La carte a été dupliquée.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 69]]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -5615,15 +5420,6 @@
"origin": [["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 127]],
"translation": "Titre"
},
"wK4OTM": {
"translation": "Titre (facultatif)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 180]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -5726,7 +5522,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 312],
["src/views/card/components/Dropdown.tsx", 49],
["src/views/public/board/CardModal.tsx", 43],
["src/views/public/board/index.tsx", 84]
@@ -5798,7 +5593,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"translation": "Impossible de supprimer l'élément de liste de contrôle"
},
"2QGEbi": {
@@ -5808,20 +5603,6 @@
"origin": [["src/views/card/components/DeleteCommentConfirmation.tsx", 45]],
"translation": "Impossible de supprimer le commentaire"
},
"Oo4E6p": {
"translation": "Impossible de dupliquer la carte",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 75]]
},
"K7k9u3": {
"translation": "Impossible de déplacer la carte",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 41]]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -5880,7 +5661,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "Impossible de mettre à jour la carte"
@@ -5896,7 +5677,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"translation": "Impossible de mettre à jour l'élément de la checklist"
},
"fYVH36": {
@@ -5925,7 +5706,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "Impossible de mettre à jour la liste"
@@ -6095,7 +5876,7 @@
"message": "Update label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Mettre à jour l'étiquette"
},
"L5ZPjz": {
@@ -6387,11 +6168,11 @@
"translation": "Webhooks"
},
"xLTZVf": {
"translation": "Jour de début de semaine",
"message": "Week start day",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]],
"translation": "Jour de début de semaine"
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]]
},
"9eF5oV": {
"message": "Welcome back",
@@ -6434,7 +6215,7 @@
"comments": [],
"origin": [
["src/components/SettingsLayout.tsx", 47],
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/boards/index.tsx", 48],
["src/views/members/index.tsx", 258],
["src/views/public/board/index.tsx", 125],
@@ -6613,7 +6394,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 80]],
"translation": "Vous êtes sur le point de modifier votre mot de passe."
},
"aelko+": {
@@ -6645,8 +6426,8 @@
["src/views/board/components/List.tsx", 117],
["src/views/board/components/UpdateBoardSlugButton.tsx", 58],
["src/views/board/components/VisibilityButton.tsx", 72],
["src/views/board/index.tsx", 603],
["src/views/board/index.tsx", 661],
["src/views/board/index.tsx", 487],
["src/views/board/index.tsx", 545],
["src/views/boards/components/BoardsList.tsx", 71],
["src/views/boards/index.tsx", 59],
["src/views/boards/index.tsx", 80]

File diff suppressed because one or more lines are too long

View File

@@ -71,7 +71,7 @@
"0": ["isTemplate ? \"Template\" : \"Board\""]
},
"comments": [],
"origin": [["src/views/board/index.tsx", 556]],
"origin": [["src/views/board/index.tsx", 440]],
"translation": "{0} non trovato"
},
"0xWkkH": {
@@ -238,13 +238,6 @@
"origin": [["src/views/home/components/Features.tsx", 110]],
"translation": "Registri attività"
},
"UGCIzB": {
"translation": "Aggiungi / modifica etichetta",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 50]]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -287,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"translation": "Aggiungi dettagli..."
},
"lyqwgn": {
@@ -295,8 +288,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/LabelSelector.tsx", 114],
["src/views/card/components/LabelSelector.tsx", 119]
["src/views/card/components/LabelSelector.tsx", 113],
["src/views/card/components/LabelSelector.tsx", 118]
],
"translation": "Aggiungi etichetta"
},
@@ -305,7 +298,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 135],
["src/views/card/components/MemberSelector.tsx", 134],
["src/views/members/components/InviteMemberForm.tsx", 257]
],
"translation": "Aggiungi membro"
@@ -849,7 +842,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/card/index.tsx", 317],
["src/views/public/board/index.tsx", 125]
],
@@ -1242,7 +1235,6 @@
"origin": [
["src/components/DeleteLabelConfirmation.tsx", 49],
["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 176],
["src/views/board/components/CardContextDuplicateModal.tsx", 267],
["src/views/board/components/DeleteBoardConfirmation.tsx", 44],
["src/views/card/components/Comment.tsx", 195],
["src/views/card/components/DeleteCardConfirmation.tsx", 86],
@@ -1269,13 +1261,6 @@
"origin": [["src/views/card/index.tsx", 317]],
"translation": "Card"
},
"sU2uWc": {
"translation": "Scheda duplicata",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 67]]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -1298,7 +1283,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 307],
["src/views/card/components/Dropdown.tsx", 44],
["src/views/public/board/CardModal.tsx", 38]
],
@@ -1309,25 +1293,18 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/AccountSettings.tsx", 77],
["src/views/settings/AccountSettings.tsx", 87],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
],
"translation": "Cambia password"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 63]],
"translation": "Modifica la dimensione del carattere dell'applicazione."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 53]],
"origin": [["src/views/settings/AccountSettings.tsx", 52]],
"translation": "Modifica le tue preferenze di lingua."
},
"gZxH/p": {
@@ -1677,40 +1654,6 @@
"origin": [["src/views/card/components/Dropdown.tsx", 58]],
"translation": "Copia link card"
},
"0utuU6": {
"translation": "Copia checklist",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 257]
]
},
"fDQ7hA": {
"translation": "Copia etichette",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 221]
]
},
"1bJKhj": {
"translation": "Copia link alla scheda",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 62]]
},
"uvH2xS": {
"translation": "Copia membri",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 239]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -1772,7 +1715,7 @@
"message": "Create label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Crea etichetta"
},
"XTKtey": {
@@ -1807,7 +1750,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewCardForm.tsx", 417],
["src/views/card/components/LabelSelector.tsx", 101]
["src/views/card/components/LabelSelector.tsx", 100]
],
"translation": "Crea nuova etichetta"
},
@@ -1816,8 +1759,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 91],
["src/views/board/index.tsx", 670]
["src/views/board/index.tsx", 76],
["src/views/board/index.tsx", 554]
],
"translation": "Crea nuova lista"
},
@@ -2018,8 +1961,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 70],
["src/views/settings/AccountSettings.tsx", 80],
["src/views/settings/AccountSettings.tsx", 59],
["src/views/settings/AccountSettings.tsx", 69],
["src/views/settings/components/DeleteAccountConfirmation.tsx", 96]
],
"translation": "Elimina account"
@@ -2035,10 +1978,7 @@
"message": "Delete card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMenu.tsx", 74],
["src/views/card/components/Dropdown.tsx", 76]
],
"origin": [["src/views/card/components/Dropdown.tsx", 76]],
"translation": "Elimina card"
},
"njJFtc": {
@@ -2146,7 +2086,7 @@
"message": "Display name",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 36]],
"origin": [["src/views/settings/AccountSettings.tsx", 35]],
"translation": "Nome visualizzato"
},
"MlyjJu": {
@@ -2224,9 +2164,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 36],
["src/views/board/components/CardContextLabelsModal.tsx", 48],
["src/views/board/components/CardContextMembersModal.tsx", 68],
["src/views/boards/components/TemplateBoards.tsx", 17],
["src/views/boards/components/TemplateBoards.tsx", 23],
["src/views/boards/components/TemplateBoards.tsx", 29],
@@ -2282,34 +2219,6 @@
"origin": [["src/views/board/components/Filters.tsx", 122]],
"translation": "Scadenza domani"
},
"euc6Ns": {
"translation": "Duplica",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"BTDPLo": {
"translation": "Duplica scheda",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 111],
["src/views/board/components/CardContextMenu.tsx", 68]
]
},
"06EQqT": {
"translation": "Duplicazione in corso…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -2395,7 +2304,7 @@
"comments": [],
"origin": [
["src/views/members/components/InviteMemberForm.tsx", 272],
["src/views/settings/AccountSettings.tsx", 43]
["src/views/settings/AccountSettings.tsx", 42]
],
"translation": "Email"
},
@@ -2422,15 +2331,6 @@
"origin": [["src/views/settings/WorkspaceSettings.tsx", 100]],
"translation": "Visibilità email"
},
"bFREhu": {
"translation": "Fine dell'elenco",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 200]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -2868,13 +2768,6 @@
"origin": [["src/views/home/components/Faqs.tsx", 150]],
"translation": "Trova risposte alle domande più comuni su Kan. Non trovi quello che cerchi? Sentiti libero di <0>contattarci</0>."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 60]],
"translation": "Dimensione del carattere"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -2987,7 +2880,7 @@
"message": "Get started by creating a new list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 655]],
"origin": [["src/views/board/index.tsx", 539]],
"translation": "Inizia creando una nuova lista"
},
"oW13KZ": {
@@ -3294,7 +3187,7 @@
"message": "in",
"placeholders": {},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 167]],
"origin": [["src/components/CommandPallette.tsx", 168]],
"translation": "in"
},
"WbTDwg": {
@@ -3407,7 +3300,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 115],
["src/views/card/components/MemberSelector.tsx", 114],
["src/views/members/components/InviteMemberForm.tsx", 367]
],
"translation": "Invita membro"
@@ -3479,7 +3372,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextLabelsModal.tsx", 31],
["src/views/board/components/Filters.tsx", 155],
["src/views/board/components/NewCardForm.tsx", 421],
["src/views/card/index.tsx", 133]
@@ -3506,16 +3398,9 @@
"message": "Language",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 50]],
"origin": [["src/views/settings/AccountSettings.tsx", 49]],
"translation": "Lingua"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 9]],
"translation": "Grande"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -3591,7 +3476,6 @@
"comments": [],
"origin": [
["src/views/board/components/UpdateBoardSlugButton.tsx", 80],
["src/views/board/index.tsx", 305],
["src/views/card/components/Dropdown.tsx", 42],
["src/views/public/board/CardModal.tsx", 36],
["src/views/public/board/index.tsx", 77]
@@ -3602,10 +3486,7 @@
"message": "List",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 117],
["src/views/card/index.tsx", 124]
],
"origin": [["src/views/card/index.tsx", 124]],
"translation": "Lista"
},
"zQX5Dj": {
@@ -3694,16 +3575,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 91]],
"translation": "Crea template"
},
"hB02vO": {
"translation": "Gestisci membri",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMembersModal.tsx", 51],
["src/views/board/components/CardContextMenu.tsx", 38]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -3720,13 +3591,6 @@
"origin": [["src/views/card/components/ActivityList.tsx", 258]],
"translation": "ha contrassegnato l'elemento della checklist <0>{0}</0> come incompleto"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 8]],
"translation": "Medio"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -3778,13 +3642,11 @@
"translation": "Menzioni"
},
"hty0d5": {
"translation": "Lunedì",
"message": "Monday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]
],
"translation": "Lunedì"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]]
},
"+8Nek/": {
"message": "Monthly",
@@ -3803,20 +3665,6 @@
"origin": [["src/views/members/components/InviteMemberForm.tsx", 159]],
"translation": "fatturazione mensile"
},
"51UCsN": {
"translation": "Sposta in un altro elenco",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 44]]
},
"J4+OTA": {
"translation": "Sposta nell'elenco",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 70]]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -3928,7 +3776,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewListForm.tsx", 113],
["src/views/board/index.tsx", 619]
["src/views/board/index.tsx", 503]
],
"translation": "Nuova lista"
},
@@ -4054,14 +3902,14 @@
"message": "No lists",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 651]],
"origin": [["src/views/board/index.tsx", 535]],
"translation": "Nessuna lista"
},
"fvLNDy": {
"message": "No lists have been created yet",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 656]],
"origin": [["src/views/board/index.tsx", 540]],
"translation": "Nessuna lista è stata ancora creata"
},
"i30J2U": {
@@ -4077,7 +3925,7 @@
"debouncedQuery": ["debouncedQuery"]
},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 183]],
"origin": [["src/components/CommandPallette.tsx", 184]],
"translation": "Nessun risultato trovato per \"{debouncedQuery}\"."
},
"Q9pQaX": {
@@ -4128,7 +3976,7 @@
"message": "Once you delete your account, there is no going back. This action cannot be undone.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 73]],
"origin": [["src/views/settings/AccountSettings.tsx", 62]],
"translation": "Una volta eliminato il tuo account, non si può tornare indietro. Questa azione non può essere annullata."
},
"dmKdk0": {
@@ -4453,13 +4301,13 @@
["src/views/board/components/NewTemplateForm.tsx", 77],
["src/views/board/components/UpdateBoardSlugForm.tsx", 73],
["src/views/board/components/VisibilityButton.tsx", 57],
["src/views/board/index.tsx", 217],
["src/views/board/index.tsx", 273],
["src/views/board/index.tsx", 194],
["src/views/board/index.tsx", 250],
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -4509,24 +4357,12 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 76],
["src/views/board/components/CardContextMoveListModal.tsx", 42],
["src/views/board/index.tsx", 314],
["src/views/card/components/Dropdown.tsx", 51],
["src/views/public/board/CardModal.tsx", 45],
["src/views/public/board/index.tsx", 86]
],
"translation": "Riprova."
},
"nYNMZZ": {
"translation": "Posizione nell'elenco (0 = primo, lascia vuoto per la fine)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 193]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -4602,7 +4438,7 @@
"message": "Profile picture",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 30]],
"origin": [["src/views/settings/AccountSettings.tsx", 29]],
"translation": "Immagine del profilo"
},
"7d1a0d": {
@@ -4871,13 +4707,11 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Sabato",
"message": "Saturday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]
],
"translation": "Sabato"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]]
},
"tfDRzk": {
"message": "Save",
@@ -4948,15 +4782,6 @@
"origin": [["src/views/home/components/Testimonials.tsx", 239]],
"translation": "Scopri cosa dicono i nostri utenti su Kan"
},
"zYRVNp": {
"translation": "Seleziona un elenco",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 131]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -5027,11 +4852,7 @@
"message": "Set due date",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 20],
["src/views/board/components/CardContextMenu.tsx", 56],
["src/views/card/components/DueDateSelector.tsx", 121]
],
"origin": [["src/views/card/components/DueDateSelector.tsx", 121]],
"translation": "Imposta scadenza"
},
"Eit43O": {
@@ -5055,7 +4876,7 @@
"message": "Settings | Account",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 26]],
"origin": [["src/views/settings/AccountSettings.tsx", 25]],
"translation": "Impostazioni | Account"
},
"iy1wb+": {
@@ -5185,13 +5006,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 7]],
"translation": "Piccolo"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -5264,13 +5078,11 @@
"translation": "Successo"
},
"DBC3t5": {
"translation": "Domenica",
"message": "Sunday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]
],
"translation": "Domenica"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -5331,8 +5143,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 565]
["src/views/board/index.tsx", 413],
["src/views/board/index.tsx", 449]
],
"translation": "Template"
},
@@ -5426,13 +5238,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 47]],
"translation": "La bacheca è stata ripristinata."
},
"nKBUeF": {
"translation": "La scheda è stata duplicata.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 69]]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -5615,15 +5420,6 @@
"origin": [["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 127]],
"translation": "Titolo"
},
"wK4OTM": {
"translation": "Titolo (facoltativo)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 180]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -5726,7 +5522,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 312],
["src/views/card/components/Dropdown.tsx", 49],
["src/views/public/board/CardModal.tsx", 43],
["src/views/public/board/index.tsx", 84]
@@ -5798,7 +5593,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"translation": "Impossibile eliminare l'elemento della checklist"
},
"2QGEbi": {
@@ -5808,20 +5603,6 @@
"origin": [["src/views/card/components/DeleteCommentConfirmation.tsx", 45]],
"translation": "Impossibile eliminare il commento"
},
"Oo4E6p": {
"translation": "Impossibile duplicare la scheda",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 75]]
},
"K7k9u3": {
"translation": "Impossibile spostare la scheda",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 41]]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -5880,7 +5661,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "Impossibile aggiornare la card"
@@ -5896,7 +5677,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"translation": "Impossibile aggiornare l'elemento della checklist"
},
"fYVH36": {
@@ -5925,7 +5706,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "Impossibile aggiornare la lista"
@@ -6095,7 +5876,7 @@
"message": "Update label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Aggiorna etichetta"
},
"L5ZPjz": {
@@ -6387,11 +6168,11 @@
"translation": "Webhook"
},
"xLTZVf": {
"translation": "Giorno di inizio settimana",
"message": "Week start day",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]],
"translation": "Giorno di inizio settimana"
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]]
},
"9eF5oV": {
"message": "Welcome back",
@@ -6434,7 +6215,7 @@
"comments": [],
"origin": [
["src/components/SettingsLayout.tsx", 47],
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/boards/index.tsx", 48],
["src/views/members/index.tsx", 258],
["src/views/public/board/index.tsx", 125],
@@ -6613,7 +6394,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 80]],
"translation": "Stai per modificare la tua password."
},
"aelko+": {
@@ -6645,8 +6426,8 @@
["src/views/board/components/List.tsx", 117],
["src/views/board/components/UpdateBoardSlugButton.tsx", 58],
["src/views/board/components/VisibilityButton.tsx", 72],
["src/views/board/index.tsx", 603],
["src/views/board/index.tsx", 661],
["src/views/board/index.tsx", 487],
["src/views/board/index.tsx", 545],
["src/views/boards/components/BoardsList.tsx", 71],
["src/views/boards/index.tsx", 59],
["src/views/boards/index.tsx", 80]

File diff suppressed because one or more lines are too long

View File

@@ -71,7 +71,7 @@
"0": ["isTemplate ? \"Template\" : \"Board\""]
},
"comments": [],
"origin": [["src/views/board/index.tsx", 556]],
"origin": [["src/views/board/index.tsx", 440]],
"translation": "{0} niet gevonden"
},
"0xWkkH": {
@@ -238,13 +238,6 @@
"origin": [["src/views/home/components/Features.tsx", 110]],
"translation": "Activiteitenlogboeken"
},
"UGCIzB": {
"translation": "Label toevoegen / bewerken",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 50]]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -287,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"translation": "Voeg details toe..."
},
"lyqwgn": {
@@ -295,8 +288,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/LabelSelector.tsx", 114],
["src/views/card/components/LabelSelector.tsx", 119]
["src/views/card/components/LabelSelector.tsx", 113],
["src/views/card/components/LabelSelector.tsx", 118]
],
"translation": "Label toevoegen"
},
@@ -305,7 +298,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 135],
["src/views/card/components/MemberSelector.tsx", 134],
["src/views/members/components/InviteMemberForm.tsx", 257]
],
"translation": "Lid toevoegen"
@@ -849,7 +842,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/card/index.tsx", 317],
["src/views/public/board/index.tsx", 125]
],
@@ -1242,7 +1235,6 @@
"origin": [
["src/components/DeleteLabelConfirmation.tsx", 49],
["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 176],
["src/views/board/components/CardContextDuplicateModal.tsx", 267],
["src/views/board/components/DeleteBoardConfirmation.tsx", 44],
["src/views/card/components/Comment.tsx", 195],
["src/views/card/components/DeleteCardConfirmation.tsx", 86],
@@ -1269,13 +1261,6 @@
"origin": [["src/views/card/index.tsx", 317]],
"translation": "Kaart"
},
"sU2uWc": {
"translation": "Kaart gedupliceerd",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 67]]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -1298,7 +1283,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 307],
["src/views/card/components/Dropdown.tsx", 44],
["src/views/public/board/CardModal.tsx", 38]
],
@@ -1309,25 +1293,18 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/AccountSettings.tsx", 77],
["src/views/settings/AccountSettings.tsx", 87],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
],
"translation": "Wachtwoord wijzigen"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 63]],
"translation": "Pas de lettergrootte van de applicatie aan."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 53]],
"origin": [["src/views/settings/AccountSettings.tsx", 52]],
"translation": "Wijzig je taalvoorkeuren."
},
"gZxH/p": {
@@ -1677,40 +1654,6 @@
"origin": [["src/views/card/components/Dropdown.tsx", 58]],
"translation": "Kopieer kaartlink"
},
"0utuU6": {
"translation": "Checklists kopiëren",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 257]
]
},
"fDQ7hA": {
"translation": "Labels kopiëren",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 221]
]
},
"1bJKhj": {
"translation": "Link naar kaart kopiëren",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 62]]
},
"uvH2xS": {
"translation": "Leden kopiëren",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 239]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -1772,7 +1715,7 @@
"message": "Create label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Maak label"
},
"XTKtey": {
@@ -1807,7 +1750,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewCardForm.tsx", 417],
["src/views/card/components/LabelSelector.tsx", 101]
["src/views/card/components/LabelSelector.tsx", 100]
],
"translation": "Maak nieuw label"
},
@@ -1816,8 +1759,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 91],
["src/views/board/index.tsx", 670]
["src/views/board/index.tsx", 76],
["src/views/board/index.tsx", 554]
],
"translation": "Maak nieuwe lijst"
},
@@ -2018,8 +1961,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 70],
["src/views/settings/AccountSettings.tsx", 80],
["src/views/settings/AccountSettings.tsx", 59],
["src/views/settings/AccountSettings.tsx", 69],
["src/views/settings/components/DeleteAccountConfirmation.tsx", 96]
],
"translation": "Account verwijderen"
@@ -2035,10 +1978,7 @@
"message": "Delete card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMenu.tsx", 74],
["src/views/card/components/Dropdown.tsx", 76]
],
"origin": [["src/views/card/components/Dropdown.tsx", 76]],
"translation": "Kaart verwijderen"
},
"njJFtc": {
@@ -2146,7 +2086,7 @@
"message": "Display name",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 36]],
"origin": [["src/views/settings/AccountSettings.tsx", 35]],
"translation": "Weergavenaam"
},
"MlyjJu": {
@@ -2224,9 +2164,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 36],
["src/views/board/components/CardContextLabelsModal.tsx", 48],
["src/views/board/components/CardContextMembersModal.tsx", 68],
["src/views/boards/components/TemplateBoards.tsx", 17],
["src/views/boards/components/TemplateBoards.tsx", 23],
["src/views/boards/components/TemplateBoards.tsx", 29],
@@ -2282,34 +2219,6 @@
"origin": [["src/views/board/components/Filters.tsx", 122]],
"translation": "Verloopt morgen"
},
"euc6Ns": {
"translation": "Dupliceren",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"BTDPLo": {
"translation": "Kaart dupliceren",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 111],
["src/views/board/components/CardContextMenu.tsx", 68]
]
},
"06EQqT": {
"translation": "Dupliceren…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -2395,7 +2304,7 @@
"comments": [],
"origin": [
["src/views/members/components/InviteMemberForm.tsx", 272],
["src/views/settings/AccountSettings.tsx", 43]
["src/views/settings/AccountSettings.tsx", 42]
],
"translation": "E-mail"
},
@@ -2422,15 +2331,6 @@
"origin": [["src/views/settings/WorkspaceSettings.tsx", 100]],
"translation": "E-mailzichtbaarheid"
},
"bFREhu": {
"translation": "Einde van lijst",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 200]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -2868,13 +2768,6 @@
"origin": [["src/views/home/components/Faqs.tsx", 150]],
"translation": "Vind antwoorden op veelgestelde vragen over Kan. Kun je niet vinden wat je zoekt? Neem gerust <0>contact met ons op</0>."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 60]],
"translation": "Lettergrootte"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -2987,7 +2880,7 @@
"message": "Get started by creating a new list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 655]],
"origin": [["src/views/board/index.tsx", 539]],
"translation": "Ga aan de slag door een nieuwe lijst te maken"
},
"oW13KZ": {
@@ -3294,7 +3187,7 @@
"message": "in",
"placeholders": {},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 167]],
"origin": [["src/components/CommandPallette.tsx", 168]],
"translation": "in"
},
"WbTDwg": {
@@ -3407,7 +3300,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 115],
["src/views/card/components/MemberSelector.tsx", 114],
["src/views/members/components/InviteMemberForm.tsx", 367]
],
"translation": "Lid uitnodigen"
@@ -3479,7 +3372,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextLabelsModal.tsx", 31],
["src/views/board/components/Filters.tsx", 155],
["src/views/board/components/NewCardForm.tsx", 421],
["src/views/card/index.tsx", 133]
@@ -3506,16 +3398,9 @@
"message": "Language",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 50]],
"origin": [["src/views/settings/AccountSettings.tsx", 49]],
"translation": "Taal"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 9]],
"translation": "Groot"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -3591,7 +3476,6 @@
"comments": [],
"origin": [
["src/views/board/components/UpdateBoardSlugButton.tsx", 80],
["src/views/board/index.tsx", 305],
["src/views/card/components/Dropdown.tsx", 42],
["src/views/public/board/CardModal.tsx", 36],
["src/views/public/board/index.tsx", 77]
@@ -3602,10 +3486,7 @@
"message": "List",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 117],
["src/views/card/index.tsx", 124]
],
"origin": [["src/views/card/index.tsx", 124]],
"translation": "Lijst"
},
"zQX5Dj": {
@@ -3694,16 +3575,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 91]],
"translation": "Sjabloon maken"
},
"hB02vO": {
"translation": "Leden beheren",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMembersModal.tsx", 51],
["src/views/board/components/CardContextMenu.tsx", 38]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -3720,13 +3591,6 @@
"origin": [["src/views/card/components/ActivityList.tsx", 258]],
"translation": "heeft checklistitem <0>{0}</0> als onvolledig gemarkeerd"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 8]],
"translation": "Gemiddeld"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -3778,13 +3642,11 @@
"translation": "Vermeldingen"
},
"hty0d5": {
"translation": "Maandag",
"message": "Monday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]
],
"translation": "Maandag"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]]
},
"+8Nek/": {
"message": "Monthly",
@@ -3803,20 +3665,6 @@
"origin": [["src/views/members/components/InviteMemberForm.tsx", 159]],
"translation": "maandelijkse facturering"
},
"51UCsN": {
"translation": "Verplaats naar andere lijst",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 44]]
},
"J4+OTA": {
"translation": "Verplaats naar lijst",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 70]]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -3928,7 +3776,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewListForm.tsx", 113],
["src/views/board/index.tsx", 619]
["src/views/board/index.tsx", 503]
],
"translation": "Nieuwe lijst"
},
@@ -4054,14 +3902,14 @@
"message": "No lists",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 651]],
"origin": [["src/views/board/index.tsx", 535]],
"translation": "Geen lijsten"
},
"fvLNDy": {
"message": "No lists have been created yet",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 656]],
"origin": [["src/views/board/index.tsx", 540]],
"translation": "Er zijn nog geen lijsten aangemaakt"
},
"i30J2U": {
@@ -4077,7 +3925,7 @@
"debouncedQuery": ["debouncedQuery"]
},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 183]],
"origin": [["src/components/CommandPallette.tsx", 184]],
"translation": "Geen resultaten gevonden voor \"{debouncedQuery}\"."
},
"Q9pQaX": {
@@ -4128,7 +3976,7 @@
"message": "Once you delete your account, there is no going back. This action cannot be undone.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 73]],
"origin": [["src/views/settings/AccountSettings.tsx", 62]],
"translation": "Zodra je je account verwijdert, is er geen weg terug. Deze actie kan niet ongedaan worden gemaakt."
},
"dmKdk0": {
@@ -4453,13 +4301,13 @@
["src/views/board/components/NewTemplateForm.tsx", 77],
["src/views/board/components/UpdateBoardSlugForm.tsx", 73],
["src/views/board/components/VisibilityButton.tsx", 57],
["src/views/board/index.tsx", 217],
["src/views/board/index.tsx", 273],
["src/views/board/index.tsx", 194],
["src/views/board/index.tsx", 250],
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -4509,24 +4357,12 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 76],
["src/views/board/components/CardContextMoveListModal.tsx", 42],
["src/views/board/index.tsx", 314],
["src/views/card/components/Dropdown.tsx", 51],
["src/views/public/board/CardModal.tsx", 45],
["src/views/public/board/index.tsx", 86]
],
"translation": "Probeer het opnieuw."
},
"nYNMZZ": {
"translation": "Positie in lijst (0 = eerste, leeg laten voor het einde)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 193]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -4602,7 +4438,7 @@
"message": "Profile picture",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 30]],
"origin": [["src/views/settings/AccountSettings.tsx", 29]],
"translation": "Profielfoto"
},
"7d1a0d": {
@@ -4871,13 +4707,11 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Zaterdag",
"message": "Saturday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]
],
"translation": "Zaterdag"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]]
},
"tfDRzk": {
"message": "Save",
@@ -4948,15 +4782,6 @@
"origin": [["src/views/home/components/Testimonials.tsx", 239]],
"translation": "Bekijk wat onze gebruikers over Kan zeggen"
},
"zYRVNp": {
"translation": "Selecteer een lijst",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 131]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -5027,11 +4852,7 @@
"message": "Set due date",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 20],
["src/views/board/components/CardContextMenu.tsx", 56],
["src/views/card/components/DueDateSelector.tsx", 121]
],
"origin": [["src/views/card/components/DueDateSelector.tsx", 121]],
"translation": "Vervaldatum instellen"
},
"Eit43O": {
@@ -5055,7 +4876,7 @@
"message": "Settings | Account",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 26]],
"origin": [["src/views/settings/AccountSettings.tsx", 25]],
"translation": "Instellingen | Account"
},
"iy1wb+": {
@@ -5185,13 +5006,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 7]],
"translation": "Klein"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -5264,13 +5078,11 @@
"translation": "Succes"
},
"DBC3t5": {
"translation": "Zondag",
"message": "Sunday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]
],
"translation": "Zondag"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -5331,8 +5143,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 565]
["src/views/board/index.tsx", 413],
["src/views/board/index.tsx", 449]
],
"translation": "Sjabloon"
},
@@ -5426,13 +5238,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 47]],
"translation": "Het board is gedearchiveerd."
},
"nKBUeF": {
"translation": "De kaart is gedupliceerd.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 69]]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -5615,15 +5420,6 @@
"origin": [["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 127]],
"translation": "Titel"
},
"wK4OTM": {
"translation": "Titel (optioneel)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 180]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -5726,7 +5522,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 312],
["src/views/card/components/Dropdown.tsx", 49],
["src/views/public/board/CardModal.tsx", 43],
["src/views/public/board/index.tsx", 84]
@@ -5798,7 +5593,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"translation": "Kan checklistitem niet verwijderen"
},
"2QGEbi": {
@@ -5808,20 +5603,6 @@
"origin": [["src/views/card/components/DeleteCommentConfirmation.tsx", 45]],
"translation": "Kan opmerking niet verwijderen"
},
"Oo4E6p": {
"translation": "Kan kaart niet dupliceren",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 75]]
},
"K7k9u3": {
"translation": "Kan kaart niet verplaatsen",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 41]]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -5880,7 +5661,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "Kan kaart niet bijwerken"
@@ -5896,7 +5677,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"translation": "Kan checklistitem niet bijwerken"
},
"fYVH36": {
@@ -5925,7 +5706,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "Kan lijst niet bijwerken"
@@ -6095,7 +5876,7 @@
"message": "Update label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Label bijwerken"
},
"L5ZPjz": {
@@ -6387,11 +6168,11 @@
"translation": "Webhooks"
},
"xLTZVf": {
"translation": "Eerste dag van de week",
"message": "Week start day",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]],
"translation": "Eerste dag van de week"
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]]
},
"9eF5oV": {
"message": "Welcome back",
@@ -6434,7 +6215,7 @@
"comments": [],
"origin": [
["src/components/SettingsLayout.tsx", 47],
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/boards/index.tsx", 48],
["src/views/members/index.tsx", 258],
["src/views/public/board/index.tsx", 125],
@@ -6613,7 +6394,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 80]],
"translation": "Je staat op het punt je wachtwoord te wijzigen."
},
"aelko+": {
@@ -6645,8 +6426,8 @@
["src/views/board/components/List.tsx", 117],
["src/views/board/components/UpdateBoardSlugButton.tsx", 58],
["src/views/board/components/VisibilityButton.tsx", 72],
["src/views/board/index.tsx", 603],
["src/views/board/index.tsx", 661],
["src/views/board/index.tsx", 487],
["src/views/board/index.tsx", 545],
["src/views/boards/components/BoardsList.tsx", 71],
["src/views/boards/index.tsx", 59],
["src/views/boards/index.tsx", 80]

File diff suppressed because one or more lines are too long

View File

@@ -71,7 +71,7 @@
"0": ["isTemplate ? \"Template\" : \"Board\""]
},
"comments": [],
"origin": [["src/views/board/index.tsx", 556]],
"origin": [["src/views/board/index.tsx", 440]],
"translation": "{0} nie znaleziono"
},
"0xWkkH": {
@@ -238,13 +238,6 @@
"origin": [["src/views/home/components/Features.tsx", 110]],
"translation": "Dzienniki aktywności"
},
"UGCIzB": {
"translation": "Dodaj / edytuj etykietę",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 50]]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -287,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"translation": "Dodaj szczegóły..."
},
"lyqwgn": {
@@ -295,8 +288,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/LabelSelector.tsx", 114],
["src/views/card/components/LabelSelector.tsx", 119]
["src/views/card/components/LabelSelector.tsx", 113],
["src/views/card/components/LabelSelector.tsx", 118]
],
"translation": "Dodaj etykietę"
},
@@ -305,7 +298,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 135],
["src/views/card/components/MemberSelector.tsx", 134],
["src/views/members/components/InviteMemberForm.tsx", 257]
],
"translation": "Dodaj członka"
@@ -849,7 +842,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/card/index.tsx", 317],
["src/views/public/board/index.tsx", 125]
],
@@ -1242,7 +1235,6 @@
"origin": [
["src/components/DeleteLabelConfirmation.tsx", 49],
["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 176],
["src/views/board/components/CardContextDuplicateModal.tsx", 267],
["src/views/board/components/DeleteBoardConfirmation.tsx", 44],
["src/views/card/components/Comment.tsx", 195],
["src/views/card/components/DeleteCardConfirmation.tsx", 86],
@@ -1269,13 +1261,6 @@
"origin": [["src/views/card/index.tsx", 317]],
"translation": "Karta"
},
"sU2uWc": {
"translation": "Karta zduplikowana",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 67]]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -1298,7 +1283,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 307],
["src/views/card/components/Dropdown.tsx", 44],
["src/views/public/board/CardModal.tsx", 38]
],
@@ -1309,25 +1293,18 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/AccountSettings.tsx", 77],
["src/views/settings/AccountSettings.tsx", 87],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
],
"translation": "Zmień hasło"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 63]],
"translation": "Zmień rozmiar czcionki aplikacji."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 53]],
"origin": [["src/views/settings/AccountSettings.tsx", 52]],
"translation": "Zmień preferencje językowe."
},
"gZxH/p": {
@@ -1677,40 +1654,6 @@
"origin": [["src/views/card/components/Dropdown.tsx", 58]],
"translation": "Kopiuj link do karty"
},
"0utuU6": {
"translation": "Kopiuj listy kontrolne",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 257]
]
},
"fDQ7hA": {
"translation": "Kopiuj etykiety",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 221]
]
},
"1bJKhj": {
"translation": "Kopiuj link do karty",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 62]]
},
"uvH2xS": {
"translation": "Kopiuj członków",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 239]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -1772,7 +1715,7 @@
"message": "Create label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Utwórz etykietę"
},
"XTKtey": {
@@ -1807,7 +1750,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewCardForm.tsx", 417],
["src/views/card/components/LabelSelector.tsx", 101]
["src/views/card/components/LabelSelector.tsx", 100]
],
"translation": "Utwórz nową etykietę"
},
@@ -1816,8 +1759,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 91],
["src/views/board/index.tsx", 670]
["src/views/board/index.tsx", 76],
["src/views/board/index.tsx", 554]
],
"translation": "Utwórz nową listę"
},
@@ -2018,8 +1961,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 70],
["src/views/settings/AccountSettings.tsx", 80],
["src/views/settings/AccountSettings.tsx", 59],
["src/views/settings/AccountSettings.tsx", 69],
["src/views/settings/components/DeleteAccountConfirmation.tsx", 96]
],
"translation": "Usuń konto"
@@ -2035,10 +1978,7 @@
"message": "Delete card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMenu.tsx", 74],
["src/views/card/components/Dropdown.tsx", 76]
],
"origin": [["src/views/card/components/Dropdown.tsx", 76]],
"translation": "Usuń kartę"
},
"njJFtc": {
@@ -2146,7 +2086,7 @@
"message": "Display name",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 36]],
"origin": [["src/views/settings/AccountSettings.tsx", 35]],
"translation": "Nazwa wyświetlana"
},
"MlyjJu": {
@@ -2224,9 +2164,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 36],
["src/views/board/components/CardContextLabelsModal.tsx", 48],
["src/views/board/components/CardContextMembersModal.tsx", 68],
["src/views/boards/components/TemplateBoards.tsx", 17],
["src/views/boards/components/TemplateBoards.tsx", 23],
["src/views/boards/components/TemplateBoards.tsx", 29],
@@ -2282,34 +2219,6 @@
"origin": [["src/views/board/components/Filters.tsx", 122]],
"translation": "Termin jutro"
},
"euc6Ns": {
"translation": "Duplikuj",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"BTDPLo": {
"translation": "Duplikuj kartę",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 111],
["src/views/board/components/CardContextMenu.tsx", 68]
]
},
"06EQqT": {
"translation": "Duplikowanie…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -2395,7 +2304,7 @@
"comments": [],
"origin": [
["src/views/members/components/InviteMemberForm.tsx", 272],
["src/views/settings/AccountSettings.tsx", 43]
["src/views/settings/AccountSettings.tsx", 42]
],
"translation": "E-mail"
},
@@ -2422,15 +2331,6 @@
"origin": [["src/views/settings/WorkspaceSettings.tsx", 100]],
"translation": "Widoczność e-maila"
},
"bFREhu": {
"translation": "Koniec listy",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 200]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -2868,13 +2768,6 @@
"origin": [["src/views/home/components/Faqs.tsx", 150]],
"translation": "Znajdź odpowiedzi na najczęstsze pytania dotyczące Kan. Nie możesz znaleźć tego, czego szukasz? Śmiało <0>skontaktuj się z nami</0>."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 60]],
"translation": "Rozmiar czcionki"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -2987,7 +2880,7 @@
"message": "Get started by creating a new list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 655]],
"origin": [["src/views/board/index.tsx", 539]],
"translation": "Rozpocznij, tworząc nową listę"
},
"oW13KZ": {
@@ -3294,7 +3187,7 @@
"message": "in",
"placeholders": {},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 167]],
"origin": [["src/components/CommandPallette.tsx", 168]],
"translation": "w"
},
"WbTDwg": {
@@ -3407,7 +3300,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 115],
["src/views/card/components/MemberSelector.tsx", 114],
["src/views/members/components/InviteMemberForm.tsx", 367]
],
"translation": "Zaproś członka"
@@ -3479,7 +3372,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextLabelsModal.tsx", 31],
["src/views/board/components/Filters.tsx", 155],
["src/views/board/components/NewCardForm.tsx", 421],
["src/views/card/index.tsx", 133]
@@ -3506,16 +3398,9 @@
"message": "Language",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 50]],
"origin": [["src/views/settings/AccountSettings.tsx", 49]],
"translation": "Język"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 9]],
"translation": "Duży"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -3591,7 +3476,6 @@
"comments": [],
"origin": [
["src/views/board/components/UpdateBoardSlugButton.tsx", 80],
["src/views/board/index.tsx", 305],
["src/views/card/components/Dropdown.tsx", 42],
["src/views/public/board/CardModal.tsx", 36],
["src/views/public/board/index.tsx", 77]
@@ -3602,10 +3486,7 @@
"message": "List",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 117],
["src/views/card/index.tsx", 124]
],
"origin": [["src/views/card/index.tsx", 124]],
"translation": "Lista"
},
"zQX5Dj": {
@@ -3694,16 +3575,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 91]],
"translation": "Utwórz szablon"
},
"hB02vO": {
"translation": "Zarządzaj członkami",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMembersModal.tsx", 51],
["src/views/board/components/CardContextMenu.tsx", 38]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -3720,13 +3591,6 @@
"origin": [["src/views/card/components/ActivityList.tsx", 258]],
"translation": "oznaczył(a) element listy kontrolnej <0>{0}</0> jako nieukończony"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 8]],
"translation": "Średni"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -3778,13 +3642,11 @@
"translation": "Wzmianki"
},
"hty0d5": {
"translation": "Poniedziałek",
"message": "Monday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]
],
"translation": "Poniedziałek"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]]
},
"+8Nek/": {
"message": "Monthly",
@@ -3803,20 +3665,6 @@
"origin": [["src/views/members/components/InviteMemberForm.tsx", 159]],
"translation": "rozliczenie miesięczne"
},
"51UCsN": {
"translation": "Przenieś do innej listy",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 44]]
},
"J4+OTA": {
"translation": "Przenieś do listy",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 70]]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -3928,7 +3776,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewListForm.tsx", 113],
["src/views/board/index.tsx", 619]
["src/views/board/index.tsx", 503]
],
"translation": "Nowa lista"
},
@@ -4054,14 +3902,14 @@
"message": "No lists",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 651]],
"origin": [["src/views/board/index.tsx", 535]],
"translation": "Brak list"
},
"fvLNDy": {
"message": "No lists have been created yet",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 656]],
"origin": [["src/views/board/index.tsx", 540]],
"translation": "Nie utworzono jeszcze żadnych list"
},
"i30J2U": {
@@ -4077,7 +3925,7 @@
"debouncedQuery": ["debouncedQuery"]
},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 183]],
"origin": [["src/components/CommandPallette.tsx", 184]],
"translation": "Nie znaleziono wyników dla „{debouncedQuery}”."
},
"Q9pQaX": {
@@ -4128,7 +3976,7 @@
"message": "Once you delete your account, there is no going back. This action cannot be undone.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 73]],
"origin": [["src/views/settings/AccountSettings.tsx", 62]],
"translation": "Po usunięciu konta nie będzie można tego cofnąć. Tej operacji nie da się odwrócić."
},
"dmKdk0": {
@@ -4453,13 +4301,13 @@
["src/views/board/components/NewTemplateForm.tsx", 77],
["src/views/board/components/UpdateBoardSlugForm.tsx", 73],
["src/views/board/components/VisibilityButton.tsx", 57],
["src/views/board/index.tsx", 217],
["src/views/board/index.tsx", 273],
["src/views/board/index.tsx", 194],
["src/views/board/index.tsx", 250],
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -4509,24 +4357,12 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 76],
["src/views/board/components/CardContextMoveListModal.tsx", 42],
["src/views/board/index.tsx", 314],
["src/views/card/components/Dropdown.tsx", 51],
["src/views/public/board/CardModal.tsx", 45],
["src/views/public/board/index.tsx", 86]
],
"translation": "Spróbuj ponownie."
},
"nYNMZZ": {
"translation": "Pozycja na liście (0 = pierwsza, puste pole = koniec)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 193]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -4602,7 +4438,7 @@
"message": "Profile picture",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 30]],
"origin": [["src/views/settings/AccountSettings.tsx", 29]],
"translation": "Zdjęcie profilowe"
},
"7d1a0d": {
@@ -4871,13 +4707,11 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Sobota",
"message": "Saturday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]
],
"translation": "Sobota"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]]
},
"tfDRzk": {
"message": "Save",
@@ -4948,15 +4782,6 @@
"origin": [["src/views/home/components/Testimonials.tsx", 239]],
"translation": "Zobacz, co nasi użytkownicy mówią o Kan"
},
"zYRVNp": {
"translation": "Wybierz listę",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 131]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -5027,11 +4852,7 @@
"message": "Set due date",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 20],
["src/views/board/components/CardContextMenu.tsx", 56],
["src/views/card/components/DueDateSelector.tsx", 121]
],
"origin": [["src/views/card/components/DueDateSelector.tsx", 121]],
"translation": "Ustaw termin"
},
"Eit43O": {
@@ -5055,7 +4876,7 @@
"message": "Settings | Account",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 26]],
"origin": [["src/views/settings/AccountSettings.tsx", 25]],
"translation": "Ustawienia | Konto"
},
"iy1wb+": {
@@ -5185,13 +5006,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 7]],
"translation": "Mały"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -5264,13 +5078,11 @@
"translation": "Sukces"
},
"DBC3t5": {
"translation": "Niedziela",
"message": "Sunday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]
],
"translation": "Niedziela"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -5331,8 +5143,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 565]
["src/views/board/index.tsx", 413],
["src/views/board/index.tsx", 449]
],
"translation": "Szablon"
},
@@ -5426,13 +5238,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 47]],
"translation": "Tablica została przywrócona z archiwum."
},
"nKBUeF": {
"translation": "Karta została zduplikowana.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 69]]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -5615,15 +5420,6 @@
"origin": [["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 127]],
"translation": "Tytuł"
},
"wK4OTM": {
"translation": "Tytuł (opcjonalnie)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 180]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -5726,7 +5522,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 312],
["src/views/card/components/Dropdown.tsx", 49],
["src/views/public/board/CardModal.tsx", 43],
["src/views/public/board/index.tsx", 84]
@@ -5798,7 +5593,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"translation": "Nie można usunąć elementu listy kontrolnej"
},
"2QGEbi": {
@@ -5808,20 +5603,6 @@
"origin": [["src/views/card/components/DeleteCommentConfirmation.tsx", 45]],
"translation": "Nie można usunąć komentarza"
},
"Oo4E6p": {
"translation": "Nie można zduplikować karty",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 75]]
},
"K7k9u3": {
"translation": "Nie można przenieść karty",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 41]]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -5880,7 +5661,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "Nie można zaktualizować karty"
@@ -5896,7 +5677,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"translation": "Nie można zaktualizować elementu listy kontrolnej"
},
"fYVH36": {
@@ -5925,7 +5706,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "Nie można zaktualizować listy"
@@ -6095,7 +5876,7 @@
"message": "Update label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Aktualizuj etykietę"
},
"L5ZPjz": {
@@ -6387,11 +6168,11 @@
"translation": "Webhooki"
},
"xLTZVf": {
"translation": "Pierwszy dzień tygodnia",
"message": "Week start day",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]],
"translation": "Pierwszy dzień tygodnia"
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]]
},
"9eF5oV": {
"message": "Welcome back",
@@ -6434,7 +6215,7 @@
"comments": [],
"origin": [
["src/components/SettingsLayout.tsx", 47],
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/boards/index.tsx", 48],
["src/views/members/index.tsx", 258],
["src/views/public/board/index.tsx", 125],
@@ -6613,7 +6394,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 80]],
"translation": "Zaraz zmienisz swoje hasło."
},
"aelko+": {
@@ -6645,8 +6426,8 @@
["src/views/board/components/List.tsx", 117],
["src/views/board/components/UpdateBoardSlugButton.tsx", 58],
["src/views/board/components/VisibilityButton.tsx", 72],
["src/views/board/index.tsx", 603],
["src/views/board/index.tsx", 661],
["src/views/board/index.tsx", 487],
["src/views/board/index.tsx", 545],
["src/views/boards/components/BoardsList.tsx", 71],
["src/views/boards/index.tsx", 59],
["src/views/boards/index.tsx", 80]

File diff suppressed because one or more lines are too long

View File

@@ -71,7 +71,7 @@
"0": ["isTemplate ? \"Template\" : \"Board\""]
},
"comments": [],
"origin": [["src/views/board/index.tsx", 556]],
"origin": [["src/views/board/index.tsx", 440]],
"translation": "{0} não encontrado"
},
"0xWkkH": {
@@ -238,13 +238,6 @@
"origin": [["src/views/home/components/Features.tsx", 110]],
"translation": "Registros de atividades"
},
"UGCIzB": {
"translation": "Adicionar / editar etiqueta",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 50]]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -287,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"translation": "Adicionar detalhes..."
},
"lyqwgn": {
@@ -295,8 +288,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/LabelSelector.tsx", 114],
["src/views/card/components/LabelSelector.tsx", 119]
["src/views/card/components/LabelSelector.tsx", 113],
["src/views/card/components/LabelSelector.tsx", 118]
],
"translation": "Adicionar etiqueta"
},
@@ -305,7 +298,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 135],
["src/views/card/components/MemberSelector.tsx", 134],
["src/views/members/components/InviteMemberForm.tsx", 257]
],
"translation": "Adicionar membro"
@@ -849,7 +842,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/card/index.tsx", 317],
["src/views/public/board/index.tsx", 125]
],
@@ -1242,7 +1235,6 @@
"origin": [
["src/components/DeleteLabelConfirmation.tsx", 49],
["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 176],
["src/views/board/components/CardContextDuplicateModal.tsx", 267],
["src/views/board/components/DeleteBoardConfirmation.tsx", 44],
["src/views/card/components/Comment.tsx", 195],
["src/views/card/components/DeleteCardConfirmation.tsx", 86],
@@ -1269,13 +1261,6 @@
"origin": [["src/views/card/index.tsx", 317]],
"translation": "Cartão"
},
"sU2uWc": {
"translation": "Cartão duplicado",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 67]]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -1298,7 +1283,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 307],
["src/views/card/components/Dropdown.tsx", 44],
["src/views/public/board/CardModal.tsx", 38]
],
@@ -1309,25 +1293,18 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/AccountSettings.tsx", 77],
["src/views/settings/AccountSettings.tsx", 87],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
],
"translation": "Alterar senha"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 63]],
"translation": "Altere o tamanho da fonte do aplicativo."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 53]],
"origin": [["src/views/settings/AccountSettings.tsx", 52]],
"translation": "Altere suas preferências de idioma."
},
"gZxH/p": {
@@ -1677,40 +1654,6 @@
"origin": [["src/views/card/components/Dropdown.tsx", 58]],
"translation": "Copiar link do cartão"
},
"0utuU6": {
"translation": "Copiar checklists",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 257]
]
},
"fDQ7hA": {
"translation": "Copiar etiquetas",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 221]
]
},
"1bJKhj": {
"translation": "Copiar link do cartão",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 62]]
},
"uvH2xS": {
"translation": "Copiar membros",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 239]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -1772,7 +1715,7 @@
"message": "Create label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Criar etiqueta"
},
"XTKtey": {
@@ -1807,7 +1750,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewCardForm.tsx", 417],
["src/views/card/components/LabelSelector.tsx", 101]
["src/views/card/components/LabelSelector.tsx", 100]
],
"translation": "Criar nova etiqueta"
},
@@ -1816,8 +1759,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 91],
["src/views/board/index.tsx", 670]
["src/views/board/index.tsx", 76],
["src/views/board/index.tsx", 554]
],
"translation": "Criar nova lista"
},
@@ -2018,8 +1961,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 70],
["src/views/settings/AccountSettings.tsx", 80],
["src/views/settings/AccountSettings.tsx", 59],
["src/views/settings/AccountSettings.tsx", 69],
["src/views/settings/components/DeleteAccountConfirmation.tsx", 96]
],
"translation": "Excluir conta"
@@ -2035,10 +1978,7 @@
"message": "Delete card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMenu.tsx", 74],
["src/views/card/components/Dropdown.tsx", 76]
],
"origin": [["src/views/card/components/Dropdown.tsx", 76]],
"translation": "Excluir cartão"
},
"njJFtc": {
@@ -2146,7 +2086,7 @@
"message": "Display name",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 36]],
"origin": [["src/views/settings/AccountSettings.tsx", 35]],
"translation": "Nome de exibição"
},
"MlyjJu": {
@@ -2224,9 +2164,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 36],
["src/views/board/components/CardContextLabelsModal.tsx", 48],
["src/views/board/components/CardContextMembersModal.tsx", 68],
["src/views/boards/components/TemplateBoards.tsx", 17],
["src/views/boards/components/TemplateBoards.tsx", 23],
["src/views/boards/components/TemplateBoards.tsx", 29],
@@ -2282,34 +2219,6 @@
"origin": [["src/views/board/components/Filters.tsx", 122]],
"translation": "Vence amanhã"
},
"euc6Ns": {
"translation": "Duplicar",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"BTDPLo": {
"translation": "Duplicar cartão",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 111],
["src/views/board/components/CardContextMenu.tsx", 68]
]
},
"06EQqT": {
"translation": "Duplicando…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -2395,7 +2304,7 @@
"comments": [],
"origin": [
["src/views/members/components/InviteMemberForm.tsx", 272],
["src/views/settings/AccountSettings.tsx", 43]
["src/views/settings/AccountSettings.tsx", 42]
],
"translation": "Email"
},
@@ -2422,15 +2331,6 @@
"origin": [["src/views/settings/WorkspaceSettings.tsx", 100]],
"translation": "Visibilidade do email"
},
"bFREhu": {
"translation": "Fim da lista",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 200]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -2868,13 +2768,6 @@
"origin": [["src/views/home/components/Faqs.tsx", 150]],
"translation": "Encontre respostas para perguntas comuns sobre o Kan. Não encontrou o que procura? Sinta-se à vontade para <0>entrar em contato</0>."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 60]],
"translation": "Tamanho da fonte"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -2987,7 +2880,7 @@
"message": "Get started by creating a new list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 655]],
"origin": [["src/views/board/index.tsx", 539]],
"translation": "Comece criando uma nova lista"
},
"oW13KZ": {
@@ -3294,7 +3187,7 @@
"message": "in",
"placeholders": {},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 167]],
"origin": [["src/components/CommandPallette.tsx", 168]],
"translation": "em"
},
"WbTDwg": {
@@ -3407,7 +3300,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 115],
["src/views/card/components/MemberSelector.tsx", 114],
["src/views/members/components/InviteMemberForm.tsx", 367]
],
"translation": "Convidar membro"
@@ -3479,7 +3372,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextLabelsModal.tsx", 31],
["src/views/board/components/Filters.tsx", 155],
["src/views/board/components/NewCardForm.tsx", 421],
["src/views/card/index.tsx", 133]
@@ -3506,16 +3398,9 @@
"message": "Language",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 50]],
"origin": [["src/views/settings/AccountSettings.tsx", 49]],
"translation": "Idioma"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 9]],
"translation": "Grande"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -3591,7 +3476,6 @@
"comments": [],
"origin": [
["src/views/board/components/UpdateBoardSlugButton.tsx", 80],
["src/views/board/index.tsx", 305],
["src/views/card/components/Dropdown.tsx", 42],
["src/views/public/board/CardModal.tsx", 36],
["src/views/public/board/index.tsx", 77]
@@ -3602,10 +3486,7 @@
"message": "List",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 117],
["src/views/card/index.tsx", 124]
],
"origin": [["src/views/card/index.tsx", 124]],
"translation": "Lista"
},
"zQX5Dj": {
@@ -3694,16 +3575,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 91]],
"translation": "Criar modelo"
},
"hB02vO": {
"translation": "Gerenciar membros",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMembersModal.tsx", 51],
["src/views/board/components/CardContextMenu.tsx", 38]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -3720,13 +3591,6 @@
"origin": [["src/views/card/components/ActivityList.tsx", 258]],
"translation": "marcou o item da checklist <0>{0}</0> como incompleto"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 8]],
"translation": "Médio"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -3778,13 +3642,11 @@
"translation": "Menções"
},
"hty0d5": {
"translation": "Segunda-feira",
"message": "Monday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]
],
"translation": "Segunda-feira"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]]
},
"+8Nek/": {
"message": "Monthly",
@@ -3803,20 +3665,6 @@
"origin": [["src/views/members/components/InviteMemberForm.tsx", 159]],
"translation": "cobrança mensal"
},
"51UCsN": {
"translation": "Mover para outra lista",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 44]]
},
"J4+OTA": {
"translation": "Mover para lista",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 70]]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -3928,7 +3776,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewListForm.tsx", 113],
["src/views/board/index.tsx", 619]
["src/views/board/index.tsx", 503]
],
"translation": "Nova lista"
},
@@ -4054,14 +3902,14 @@
"message": "No lists",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 651]],
"origin": [["src/views/board/index.tsx", 535]],
"translation": "Nenhuma lista"
},
"fvLNDy": {
"message": "No lists have been created yet",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 656]],
"origin": [["src/views/board/index.tsx", 540]],
"translation": "Nenhuma lista foi criada ainda"
},
"i30J2U": {
@@ -4077,7 +3925,7 @@
"debouncedQuery": ["debouncedQuery"]
},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 183]],
"origin": [["src/components/CommandPallette.tsx", 184]],
"translation": "Nenhum resultado encontrado para \"{debouncedQuery}\"."
},
"Q9pQaX": {
@@ -4128,7 +3976,7 @@
"message": "Once you delete your account, there is no going back. This action cannot be undone.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 73]],
"origin": [["src/views/settings/AccountSettings.tsx", 62]],
"translation": "Depois de excluir sua conta, não há como voltar atrás. Esta ação não pode ser desfeita."
},
"dmKdk0": {
@@ -4453,13 +4301,13 @@
["src/views/board/components/NewTemplateForm.tsx", 77],
["src/views/board/components/UpdateBoardSlugForm.tsx", 73],
["src/views/board/components/VisibilityButton.tsx", 57],
["src/views/board/index.tsx", 217],
["src/views/board/index.tsx", 273],
["src/views/board/index.tsx", 194],
["src/views/board/index.tsx", 250],
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -4509,24 +4357,12 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 76],
["src/views/board/components/CardContextMoveListModal.tsx", 42],
["src/views/board/index.tsx", 314],
["src/views/card/components/Dropdown.tsx", 51],
["src/views/public/board/CardModal.tsx", 45],
["src/views/public/board/index.tsx", 86]
],
"translation": "Por favor, tente novamente."
},
"nYNMZZ": {
"translation": "Posição na lista (0 = primeira, deixe vazio para o final)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 193]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -4602,7 +4438,7 @@
"message": "Profile picture",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 30]],
"origin": [["src/views/settings/AccountSettings.tsx", 29]],
"translation": "Foto de perfil"
},
"7d1a0d": {
@@ -4871,13 +4707,11 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Sábado",
"message": "Saturday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]
],
"translation": "Sábado"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]]
},
"tfDRzk": {
"message": "Save",
@@ -4948,15 +4782,6 @@
"origin": [["src/views/home/components/Testimonials.tsx", 239]],
"translation": "Veja o que nossos usuários estão dizendo sobre o Kan"
},
"zYRVNp": {
"translation": "Selecione uma lista",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 131]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -5027,11 +4852,7 @@
"message": "Set due date",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 20],
["src/views/board/components/CardContextMenu.tsx", 56],
["src/views/card/components/DueDateSelector.tsx", 121]
],
"origin": [["src/views/card/components/DueDateSelector.tsx", 121]],
"translation": "Definir data de vencimento"
},
"Eit43O": {
@@ -5055,7 +4876,7 @@
"message": "Settings | Account",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 26]],
"origin": [["src/views/settings/AccountSettings.tsx", 25]],
"translation": "Configurações | Conta"
},
"iy1wb+": {
@@ -5185,13 +5006,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 7]],
"translation": "Pequeno"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -5264,13 +5078,11 @@
"translation": "Sucesso"
},
"DBC3t5": {
"translation": "Domingo",
"message": "Sunday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]
],
"translation": "Domingo"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -5331,8 +5143,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 565]
["src/views/board/index.tsx", 413],
["src/views/board/index.tsx", 449]
],
"translation": "Modelo"
},
@@ -5426,13 +5238,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 47]],
"translation": "O quadro foi desarquivado."
},
"nKBUeF": {
"translation": "O cartão foi duplicado.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 69]]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -5615,15 +5420,6 @@
"origin": [["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 127]],
"translation": "Título"
},
"wK4OTM": {
"translation": "Título (opcional)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 180]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -5726,7 +5522,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 312],
["src/views/card/components/Dropdown.tsx", 49],
["src/views/public/board/CardModal.tsx", 43],
["src/views/public/board/index.tsx", 84]
@@ -5798,7 +5593,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"translation": "Não foi possível excluir item da checklist"
},
"2QGEbi": {
@@ -5808,20 +5603,6 @@
"origin": [["src/views/card/components/DeleteCommentConfirmation.tsx", 45]],
"translation": "Não foi possível excluir o comentário"
},
"Oo4E6p": {
"translation": "Não foi possível duplicar o cartão",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 75]]
},
"K7k9u3": {
"translation": "Não foi possível mover o cartão",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 41]]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -5880,7 +5661,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "Não foi possível atualizar o cartão"
@@ -5896,7 +5677,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"translation": "Não foi possível atualizar o item da checklist"
},
"fYVH36": {
@@ -5925,7 +5706,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "Não foi possível atualizar a lista"
@@ -6095,7 +5876,7 @@
"message": "Update label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Atualizar etiqueta"
},
"L5ZPjz": {
@@ -6387,11 +6168,11 @@
"translation": "Webhooks"
},
"xLTZVf": {
"translation": "Dia de início da semana",
"message": "Week start day",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]],
"translation": "Dia de início da semana"
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]]
},
"9eF5oV": {
"message": "Welcome back",
@@ -6434,7 +6215,7 @@
"comments": [],
"origin": [
["src/components/SettingsLayout.tsx", 47],
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/boards/index.tsx", 48],
["src/views/members/index.tsx", 258],
["src/views/public/board/index.tsx", 125],
@@ -6613,7 +6394,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 80]],
"translation": "Você está prestes a alterar sua senha."
},
"aelko+": {
@@ -6645,8 +6426,8 @@
["src/views/board/components/List.tsx", 117],
["src/views/board/components/UpdateBoardSlugButton.tsx", 58],
["src/views/board/components/VisibilityButton.tsx", 72],
["src/views/board/index.tsx", 603],
["src/views/board/index.tsx", 661],
["src/views/board/index.tsx", 487],
["src/views/board/index.tsx", 545],
["src/views/boards/components/BoardsList.tsx", 71],
["src/views/boards/index.tsx", 59],
["src/views/boards/index.tsx", 80]

File diff suppressed because one or more lines are too long

View File

@@ -71,7 +71,7 @@
"0": ["isTemplate ? \"Template\" : \"Board\""]
},
"comments": [],
"origin": [["src/views/board/index.tsx", 556]],
"origin": [["src/views/board/index.tsx", 440]],
"translation": "{0} не найдено"
},
"0xWkkH": {
@@ -238,13 +238,6 @@
"origin": [["src/views/home/components/Features.tsx", 110]],
"translation": "Журналы активности"
},
"UGCIzB": {
"translation": "Добавить / изменить метку",
"message": "Add / edit label",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 50]]
},
"B3xxao": {
"message": "Add a card",
"placeholders": {},
@@ -287,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"translation": "Добавить детали..."
},
"lyqwgn": {
@@ -295,8 +288,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/LabelSelector.tsx", 114],
["src/views/card/components/LabelSelector.tsx", 119]
["src/views/card/components/LabelSelector.tsx", 113],
["src/views/card/components/LabelSelector.tsx", 118]
],
"translation": "Добавить метку"
},
@@ -305,7 +298,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 135],
["src/views/card/components/MemberSelector.tsx", 134],
["src/views/members/components/InviteMemberForm.tsx", 257]
],
"translation": "Добавить участника"
@@ -849,7 +842,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/card/index.tsx", 317],
["src/views/public/board/index.tsx", 125]
],
@@ -1242,7 +1235,6 @@
"origin": [
["src/components/DeleteLabelConfirmation.tsx", 49],
["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 176],
["src/views/board/components/CardContextDuplicateModal.tsx", 267],
["src/views/board/components/DeleteBoardConfirmation.tsx", 44],
["src/views/card/components/Comment.tsx", 195],
["src/views/card/components/DeleteCardConfirmation.tsx", 86],
@@ -1269,13 +1261,6 @@
"origin": [["src/views/card/index.tsx", 317]],
"translation": "Карточка"
},
"sU2uWc": {
"translation": "Карточка продублирована",
"message": "Card duplicated",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 67]]
},
"fEY2vP": {
"message": "Card not found",
"placeholders": {},
@@ -1298,7 +1283,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 307],
["src/views/card/components/Dropdown.tsx", 44],
["src/views/public/board/CardModal.tsx", 38]
],
@@ -1309,25 +1293,18 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 88],
["src/views/settings/AccountSettings.tsx", 98],
["src/views/settings/AccountSettings.tsx", 77],
["src/views/settings/AccountSettings.tsx", 87],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 109],
["src/views/settings/components/ChangePasswordConfirmation.tsx", 178]
],
"translation": "Сменить пароль"
},
"fzGyWs": {
"message": "Change the application font size.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 63]],
"translation": "Изменить размер шрифта приложения."
},
"yD3ZSw": {
"message": "Change your language preferences.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 53]],
"origin": [["src/views/settings/AccountSettings.tsx", 52]],
"translation": "Измените языковые настройки."
},
"gZxH/p": {
@@ -1677,40 +1654,6 @@
"origin": [["src/views/card/components/Dropdown.tsx", 58]],
"translation": "Скопировать ссылку на карточку"
},
"0utuU6": {
"translation": "Копировать чек-листы",
"message": "Copy checklists",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 257]
]
},
"fDQ7hA": {
"translation": "Копировать метки",
"message": "Copy labels",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 221]
]
},
"1bJKhj": {
"translation": "Копировать ссылку на карточку",
"message": "Copy link to card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 62]]
},
"uvH2xS": {
"translation": "Копировать участников",
"message": "Copy members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 239]
]
},
"9PaIxv": {
"message": "Core features",
"placeholders": {},
@@ -1772,7 +1715,7 @@
"message": "Create label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Создать метку"
},
"XTKtey": {
@@ -1807,7 +1750,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewCardForm.tsx", 417],
["src/views/card/components/LabelSelector.tsx", 101]
["src/views/card/components/LabelSelector.tsx", 100]
],
"translation": "Создать новую метку"
},
@@ -1816,8 +1759,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 91],
["src/views/board/index.tsx", 670]
["src/views/board/index.tsx", 76],
["src/views/board/index.tsx", 554]
],
"translation": "Создать новый список"
},
@@ -2018,8 +1961,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/AccountSettings.tsx", 70],
["src/views/settings/AccountSettings.tsx", 80],
["src/views/settings/AccountSettings.tsx", 59],
["src/views/settings/AccountSettings.tsx", 69],
["src/views/settings/components/DeleteAccountConfirmation.tsx", 96]
],
"translation": "Удалить аккаунт"
@@ -2035,10 +1978,7 @@
"message": "Delete card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMenu.tsx", 74],
["src/views/card/components/Dropdown.tsx", 76]
],
"origin": [["src/views/card/components/Dropdown.tsx", 76]],
"translation": "Удалить карточку"
},
"njJFtc": {
@@ -2146,7 +2086,7 @@
"message": "Display name",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 36]],
"origin": [["src/views/settings/AccountSettings.tsx", 35]],
"translation": "Отображаемое имя"
},
"MlyjJu": {
@@ -2224,9 +2164,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 36],
["src/views/board/components/CardContextLabelsModal.tsx", 48],
["src/views/board/components/CardContextMembersModal.tsx", 68],
["src/views/boards/components/TemplateBoards.tsx", 17],
["src/views/boards/components/TemplateBoards.tsx", 23],
["src/views/boards/components/TemplateBoards.tsx", 29],
@@ -2282,34 +2219,6 @@
"origin": [["src/views/board/components/Filters.tsx", 122]],
"translation": "Срок — завтра"
},
"euc6Ns": {
"translation": "Дублировать",
"message": "Duplicate",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"BTDPLo": {
"translation": "Дублировать карточку",
"message": "Duplicate card",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 111],
["src/views/board/components/CardContextMenu.tsx", 68]
]
},
"06EQqT": {
"translation": "Дублирование…",
"message": "Duplicating…",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 279]
]
},
"ePK91l": {
"message": "Edit",
"placeholders": {},
@@ -2395,7 +2304,7 @@
"comments": [],
"origin": [
["src/views/members/components/InviteMemberForm.tsx", 272],
["src/views/settings/AccountSettings.tsx", 43]
["src/views/settings/AccountSettings.tsx", 42]
],
"translation": "Email"
},
@@ -2422,15 +2331,6 @@
"origin": [["src/views/settings/WorkspaceSettings.tsx", 100]],
"translation": "Видимость email"
},
"bFREhu": {
"translation": "Конец списка",
"message": "End of list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 200]
]
},
"0+ewPp": {
"message": "Enhancement",
"placeholders": {},
@@ -2868,13 +2768,6 @@
"origin": [["src/views/home/components/Faqs.tsx", 150]],
"translation": "Найдите ответы на часто задаваемые вопросы о Kan. Не нашли то, что искали? Не стесняйтесь <0>связаться с нами</0>."
},
"q3il6U": {
"message": "Font size",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 60]],
"translation": "Размер шрифта"
},
"+3TYXa": {
"message": "For long-term sustainability, we recognise all good open source projects need a source of revenue. Ours comes from the paid cloud offering for workspaces with multiple users and for custom workspace slugs. There's no obligation to use it, and you can self-host the software on your own infrastructure free of charge.",
"placeholders": {},
@@ -2987,7 +2880,7 @@
"message": "Get started by creating a new list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 655]],
"origin": [["src/views/board/index.tsx", 539]],
"translation": "Начните с создания нового списка"
},
"oW13KZ": {
@@ -3294,7 +3187,7 @@
"message": "in",
"placeholders": {},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 167]],
"origin": [["src/components/CommandPallette.tsx", 168]],
"translation": "в"
},
"WbTDwg": {
@@ -3407,7 +3300,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/card/components/MemberSelector.tsx", 115],
["src/views/card/components/MemberSelector.tsx", 114],
["src/views/members/components/InviteMemberForm.tsx", 367]
],
"translation": "Пригласить участника"
@@ -3479,7 +3372,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextLabelsModal.tsx", 31],
["src/views/board/components/Filters.tsx", 155],
["src/views/board/components/NewCardForm.tsx", 421],
["src/views/card/index.tsx", 133]
@@ -3506,16 +3398,9 @@
"message": "Language",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 50]],
"origin": [["src/views/settings/AccountSettings.tsx", 49]],
"translation": "Язык"
},
"k7rCa/": {
"message": "Large",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 9]],
"translation": "Крупный"
},
"8Is1ih": {
"message": "Launch offer",
"placeholders": {},
@@ -3591,7 +3476,6 @@
"comments": [],
"origin": [
["src/views/board/components/UpdateBoardSlugButton.tsx", 80],
["src/views/board/index.tsx", 305],
["src/views/card/components/Dropdown.tsx", 42],
["src/views/public/board/CardModal.tsx", 36],
["src/views/public/board/index.tsx", 77]
@@ -3602,10 +3486,7 @@
"message": "List",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 117],
["src/views/card/index.tsx", 124]
],
"origin": [["src/views/card/index.tsx", 124]],
"translation": "Список"
},
"zQX5Dj": {
@@ -3694,16 +3575,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 91]],
"translation": "Создать шаблон"
},
"hB02vO": {
"translation": "Управление участниками",
"message": "Manage members",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextMembersModal.tsx", 51],
["src/views/board/components/CardContextMenu.tsx", 38]
]
},
"IDvohQ": {
"message": "marked a checklist item as incomplete",
"placeholders": {},
@@ -3720,13 +3591,6 @@
"origin": [["src/views/card/components/ActivityList.tsx", 258]],
"translation": "отметил элемент контрольного списка <0>{0}</0> как невыполненный"
},
"agPptk": {
"message": "Medium",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 8]],
"translation": "Средний"
},
"W/Elkg": {
"message": "Medium Priority",
"placeholders": {},
@@ -3778,13 +3642,11 @@
"translation": "Упоминания"
},
"hty0d5": {
"translation": "Понедельник",
"message": "Monday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]
],
"translation": "Понедельник"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 53]]
},
"+8Nek/": {
"message": "Monthly",
@@ -3803,20 +3665,6 @@
"origin": [["src/views/members/components/InviteMemberForm.tsx", 159]],
"translation": "ежемесячная оплата"
},
"51UCsN": {
"translation": "Переместить в другой список",
"message": "Move to another list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMenu.tsx", 44]]
},
"J4+OTA": {
"translation": "Переместить в список",
"message": "Move to list",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 70]]
},
"BOqTi5": {
"message": "moved the card from <0>{0}</0> to<1>{1}</1>",
"placeholders": {
@@ -3928,7 +3776,7 @@
"comments": [],
"origin": [
["src/views/board/components/NewListForm.tsx", 113],
["src/views/board/index.tsx", 619]
["src/views/board/index.tsx", 503]
],
"translation": "Новый список"
},
@@ -4054,14 +3902,14 @@
"message": "No lists",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 651]],
"origin": [["src/views/board/index.tsx", 535]],
"translation": "Нет списков"
},
"fvLNDy": {
"message": "No lists have been created yet",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/index.tsx", 656]],
"origin": [["src/views/board/index.tsx", 540]],
"translation": "Списки ещё не созданы"
},
"i30J2U": {
@@ -4077,7 +3925,7 @@
"debouncedQuery": ["debouncedQuery"]
},
"comments": [],
"origin": [["src/components/CommandPallette.tsx", 183]],
"origin": [["src/components/CommandPallette.tsx", 184]],
"translation": "По запросу «{debouncedQuery}» ничего не найдено."
},
"Q9pQaX": {
@@ -4128,7 +3976,7 @@
"message": "Once you delete your account, there is no going back. This action cannot be undone.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 73]],
"origin": [["src/views/settings/AccountSettings.tsx", 62]],
"translation": "После удаления аккаунта возврата не будет. Это действие необратимо."
},
"dmKdk0": {
@@ -4453,13 +4301,13 @@
["src/views/board/components/NewTemplateForm.tsx", 77],
["src/views/board/components/UpdateBoardSlugForm.tsx", 73],
["src/views/board/components/VisibilityButton.tsx", 57],
["src/views/board/index.tsx", 217],
["src/views/board/index.tsx", 273],
["src/views/board/index.tsx", 194],
["src/views/board/index.tsx", 250],
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -4509,24 +4357,12 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 76],
["src/views/board/components/CardContextMoveListModal.tsx", 42],
["src/views/board/index.tsx", 314],
["src/views/card/components/Dropdown.tsx", 51],
["src/views/public/board/CardModal.tsx", 45],
["src/views/public/board/index.tsx", 86]
],
"translation": "Пожалуйста, попробуйте ещё раз."
},
"nYNMZZ": {
"translation": "Позиция в списке (0 = первая, оставьте пустым для конца)",
"message": "Position in list (0 = first, leave empty for end)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 193]
]
},
"aHCEmh": {
"message": "Pricing",
"placeholders": {},
@@ -4602,7 +4438,7 @@
"message": "Profile picture",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 30]],
"origin": [["src/views/settings/AccountSettings.tsx", 29]],
"translation": "Фото профиля"
},
"7d1a0d": {
@@ -4871,13 +4707,11 @@
"translation": "SAML SSO"
},
"+5kO8P": {
"translation": "Суббота",
"message": "Saturday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]
],
"translation": "Суббота"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 54]]
},
"tfDRzk": {
"message": "Save",
@@ -4948,15 +4782,6 @@
"origin": [["src/views/home/components/Testimonials.tsx", 239]],
"translation": "Посмотрите, что наши пользователи говорят о Kan"
},
"zYRVNp": {
"translation": "Выберите список",
"message": "Select a list",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 131]
]
},
"wgNoIs": {
"message": "Select all",
"placeholders": {},
@@ -5027,11 +4852,7 @@
"message": "Set due date",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDueDateModal.tsx", 20],
["src/views/board/components/CardContextMenu.tsx", 56],
["src/views/card/components/DueDateSelector.tsx", 121]
],
"origin": [["src/views/card/components/DueDateSelector.tsx", 121]],
"translation": "Установить срок"
},
"Eit43O": {
@@ -5055,7 +4876,7 @@
"message": "Settings | Account",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 26]],
"origin": [["src/views/settings/AccountSettings.tsx", 25]],
"translation": "Настройки | Аккаунт"
},
"iy1wb+": {
@@ -5185,13 +5006,6 @@
],
"translation": "SLA"
},
"4Revpc": {
"message": "Small",
"placeholders": {},
"comments": [],
"origin": [["src/components/FontSizeSelector.tsx", 7]],
"translation": "Мелкий"
},
"++rVAm": {
"message": "Social Media",
"placeholders": {},
@@ -5264,13 +5078,11 @@
"translation": "Успех"
},
"DBC3t5": {
"translation": "Воскресенье",
"message": "Sunday",
"placeholders": {},
"comments": [],
"origin": [
["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]
],
"translation": "Воскресенье"
"origin": [["src/views/settings/components/UpdateWeekStartDayForm.tsx", 52]]
},
"uZg2+w": {
"message": "Supercharge your workspace for just $29/month. Here's what you'll get:",
@@ -5331,8 +5143,8 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 565]
["src/views/board/index.tsx", 413],
["src/views/board/index.tsx", 449]
],
"translation": "Шаблон"
},
@@ -5426,13 +5238,6 @@
"origin": [["src/views/board/components/BoardDropdown.tsx", 47]],
"translation": "Доска была восстановлена из архива."
},
"nKBUeF": {
"translation": "Карточка была продублирована.",
"message": "The card has been duplicated.",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 69]]
},
"jlB2RY": {
"message": "The current password you entered is incorrect.",
"placeholders": {},
@@ -5615,15 +5420,6 @@
"origin": [["src/components/YouTubeEmbed/EditYouTubeModal.tsx", 127]],
"translation": "Заголовок"
},
"wK4OTM": {
"translation": "Название (необязательно)",
"message": "Title (optional)",
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/components/CardContextDuplicateModal.tsx", 180]
]
},
"Se/UIp": {
"message": "To Do",
"placeholders": {},
@@ -5726,7 +5522,6 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 312],
["src/views/card/components/Dropdown.tsx", 49],
["src/views/public/board/CardModal.tsx", 43],
["src/views/public/board/index.tsx", 84]
@@ -5798,7 +5593,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"translation": "Не удалось удалить элемент контрольного списка"
},
"2QGEbi": {
@@ -5808,20 +5603,6 @@
"origin": [["src/views/card/components/DeleteCommentConfirmation.tsx", 45]],
"translation": "Не удалось удалить комментарий"
},
"Oo4E6p": {
"translation": "Невозможно дублировать карточку",
"message": "Unable to duplicate card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextDuplicateModal.tsx", 75]]
},
"K7k9u3": {
"translation": "Невозможно переместить карточку",
"message": "Unable to move card",
"placeholders": {},
"comments": [],
"origin": [["src/views/board/components/CardContextMoveListModal.tsx", 41]]
},
"8yFGGF": {
"message": "Unable to remove member",
"placeholders": {},
@@ -5880,7 +5661,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "Не удалось обновить карточку"
@@ -5896,7 +5677,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"translation": "Не удалось обновить элемент контрольного списка"
},
"fYVH36": {
@@ -5925,7 +5706,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "Не удалось обновить список"
@@ -6095,7 +5876,7 @@
"message": "Update label",
"placeholders": {},
"comments": [],
"origin": [["src/components/LabelForm.tsx", 236]],
"origin": [["src/components/LabelForm.tsx", 238]],
"translation": "Обновить метку"
},
"L5ZPjz": {
@@ -6387,11 +6168,11 @@
"translation": "Вебхуки"
},
"xLTZVf": {
"translation": "День начала недели",
"message": "Week start day",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]],
"translation": "День начала недели"
"origin": [["src/views/settings/WorkspaceSettings.tsx", 91]]
},
"9eF5oV": {
"message": "Welcome back",
@@ -6434,7 +6215,7 @@
"comments": [],
"origin": [
["src/components/SettingsLayout.tsx", 47],
["src/views/board/index.tsx", 529],
["src/views/board/index.tsx", 413],
["src/views/boards/index.tsx", 48],
["src/views/members/index.tsx", 258],
["src/views/public/board/index.tsx", 125],
@@ -6613,7 +6394,7 @@
"message": "You are about to change your password.",
"placeholders": {},
"comments": [],
"origin": [["src/views/settings/AccountSettings.tsx", 91]],
"origin": [["src/views/settings/AccountSettings.tsx", 80]],
"translation": "Вы собираетесь изменить свой пароль."
},
"aelko+": {
@@ -6645,8 +6426,8 @@
["src/views/board/components/List.tsx", 117],
["src/views/board/components/UpdateBoardSlugButton.tsx", 58],
["src/views/board/components/VisibilityButton.tsx", 72],
["src/views/board/index.tsx", 603],
["src/views/board/index.tsx", 661],
["src/views/board/index.tsx", 487],
["src/views/board/index.tsx", 545],
["src/views/boards/components/BoardsList.tsx", 71],
["src/views/boards/index.tsx", 59],
["src/views/boards/index.tsx", 80]

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -1,13 +1,12 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { withRateLimit } from "@kan/api/utils/rateLimit";
import { env } from "~/env";
export default withRateLimit(
{ points: 100, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "GET") {
return res.status(405).json({ message: "Method not allowed" });
}
@@ -33,9 +32,7 @@ export default withRateLimit(
try {
allowedHost = new URL(s3Endpoint).hostname.toLowerCase();
} catch {
return res
.status(500)
.json({ message: "Storage endpoint misconfigured" });
return res.status(500).json({ message: "Storage endpoint misconfigured" });
}
if (hostname !== allowedHost && !hostname.endsWith(`.${allowedHost}`)) {
@@ -69,7 +66,10 @@ export default withRateLimit(
const buffer = await upstream.arrayBuffer();
return res.send(Buffer.from(buffer));
} catch (error) {
return res.status(500).json({ message: "Failed to download attachment" });
console.error("Error downloading attachment:", error);
return res
.status(500)
.json({ message: "Failed to download attachment" });
}
}),
},
);

View File

@@ -1,25 +1,25 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { withRateLimit } from "@kan/api/utils/rateLimit";
export default withRateLimit(
{ points: 100, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "GET") {
return res.status(405).json({ message: "Method not allowed" });
}
async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "GET") {
return res.status(405).json({ message: "Method not allowed" });
}
try {
const response = await fetch("https://formbricks.com/api/oss-friends");
if (!response.ok) {
throw new Error("Failed to fetch from Formbricks");
}
const data = await response.json();
return res.status(200).json(data);
} catch (error) {
return res.status(500).json({ message: "Failed to fetch OSS friends" });
try {
const response = await fetch("https://formbricks.com/api/oss-friends");
if (!response.ok) {
throw new Error("Failed to fetch from Formbricks");
}
}),
const data = await response.json();
return res.status(200).json(data);
} catch (error) {
console.error("Error fetching OSS friends:", error);
return res.status(500).json({ message: "Failed to fetch OSS friends" });
}
},
);

View File

@@ -2,34 +2,34 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { env } from "next-runtime-env";
import { createNextApiContext } from "@kan/api/trpc";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { withRateLimit } from "@kan/api/utils/rateLimit";
import { createStripeClient } from "@kan/stripe";
import { withRateLimit } from "@kan/api/utils/rateLimit";
export default withRateLimit(
{ points: 100, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
const stripe = createStripeClient();
async (req: NextApiRequest, res: NextApiResponse) => {
const stripe = createStripeClient();
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
try {
const { user } = await createNextApiContext(req);
if (!user?.stripeCustomerId) {
return res.status(404).json({ error: "No billing account found" });
}
try {
const { user } = await createNextApiContext(req);
const session = await stripe.billingPortal.sessions.create({
customer: user.stripeCustomerId,
return_url: `${env("NEXT_PUBLIC_BASE_URL")}/settings`,
});
if (!user?.stripeCustomerId) {
return res.status(404).json({ error: "No billing account found" });
}
const session = await stripe.billingPortal.sessions.create({
customer: user.stripeCustomerId,
return_url: `${env("NEXT_PUBLIC_BASE_URL")}/settings`,
});
return res.status(200).json({ url: session.url });
} catch (error) {
return res.status(500).json({ error: "Error creating portal session" });
}
}),
return res.status(200).json({ url: session.url });
} catch (error) {
console.error("Error:", error);
return res.status(500).json({ error: "Error creating portal session" });
}
},
);

View File

@@ -3,13 +3,10 @@ import { env } from "next-runtime-env";
import { z } from "zod";
import { createNextApiContext } from "@kan/api/trpc";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { assertPermission } from "@kan/api/utils/permissions";
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 { generateUID } from "@kan/shared/utils";
import { createStripeClient } from "@kan/stripe";
import { withRateLimit } from "@kan/api/utils/rateLimit";
const workspaceSlugSchema = z
.string()
@@ -20,135 +17,103 @@ const workspaceSlugSchema = z
interface CheckoutSessionRequest {
successUrl: string;
cancelUrl: string;
billing?: string;
workspacePublicId?: string;
slug?: string;
workspaceName?: string;
workspaceDescription?: string;
workspaceSlug?: string;
plan?: string;
slug: string;
workspacePublicId: string;
stripeCustomerId: string;
}
export default withRateLimit(
{ points: 100, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
const stripe = createStripeClient();
async (req: NextApiRequest, res: NextApiResponse) => {
const stripe = createStripeClient();
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
try {
const { user, db } = await createNextApiContext(req);
if (!user) {
return res.status(404).json({ error: "User not found" });
}
try {
const { user, db } = await createNextApiContext(req);
const body = req.body as CheckoutSessionRequest;
const { successUrl, cancelUrl, slug, workspacePublicId } = body;
if (!user) {
return res.status(404).json({ error: "User not found" });
}
if (!successUrl || !cancelUrl || !workspacePublicId) {
return res.status(400).json({ error: "Missing required fields" });
}
const body = req.body as CheckoutSessionRequest;
const {
successUrl,
cancelUrl,
billing,
workspacePublicId,
slug,
workspaceName,
workspaceDescription,
workspaceSlug,
} = body;
if (slug) {
const slugResult = workspaceSlugSchema.safeParse(slug);
if (!successUrl || !cancelUrl) {
return res.status(400).json({ error: "Missing required fields" });
}
if (!workspacePublicId && !workspaceName) {
return res
.status(400)
.json({ error: "Must provide workspacePublicId or workspaceName" });
}
const resolvedSlug = slug ?? workspaceSlug;
if (resolvedSlug) {
const slugResult = workspaceSlugSchema.safeParse(resolvedSlug);
if (!slugResult.success) {
return res.status(400).json({ error: "Invalid workspace slug" });
}
}
let resolvedWorkspacePublicId = workspacePublicId;
let subscriptionId: number | undefined;
if (workspacePublicId) {
// Existing workspace upgrade
const workspace = await workspaceRepo.getByPublicId(
db,
workspacePublicId,
if (!slugResult.success) {
return new Response(
JSON.stringify({ error: "Invalid workspace slug" }),
{
status: 400,
headers: { "Content-Type": "application/json" },
},
);
if (!workspace) {
return res.status(404).json({ error: "Workspace not found" });
}
try {
await assertPermission(db, user.id, workspace.id, "workspace:manage");
} catch {
return res.status(403).json({ error: "Unauthorized" });
}
const subscription = await subscriptionRepo.create(db, {
plan: "pro",
referenceId: workspacePublicId,
userId: user.id,
stripeCustomerId: user.stripeCustomerId ?? "",
status: "incomplete",
});
subscriptionId = subscription?.id;
if (!subscriptionId) {
return res.status(500).json({ error: "Error creating subscription" });
}
} else {
resolvedWorkspacePublicId = generateUID();
}
const isTeam = body.plan === "team";
const annualPriceId = isTeam
? (process.env.STRIPE_TEAM_PLAN_ANNUAL_PRICE_ID ??
process.env.STRIPE_TEAM_PLAN_MONTHLY_PRICE_ID)
: (process.env.STRIPE_PRO_PLAN_ANNUAL_PRICE_ID ??
process.env.STRIPE_PRO_PLAN_MONTHLY_PRICE_ID);
const monthlyPriceId = isTeam
? process.env.STRIPE_TEAM_PLAN_MONTHLY_PRICE_ID
: process.env.STRIPE_PRO_PLAN_MONTHLY_PRICE_ID;
const priceId = billing === "annual" ? annualPriceId : monthlyPriceId;
const session = await stripe.checkout.sessions.create({
mode: "subscription",
payment_method_collection: "always",
line_items: [{ price: priceId, quantity: 1 }],
subscription_data: { trial_period_days: 14 },
success_url: `${env("NEXT_PUBLIC_BASE_URL")}${successUrl}?workspacePublicId=${resolvedWorkspacePublicId}`,
cancel_url: `${env("NEXT_PUBLIC_BASE_URL")}${cancelUrl}`,
client_reference_id: resolvedWorkspacePublicId,
customer: user.stripeCustomerId ?? undefined,
metadata: {
workspacePublicId: resolvedWorkspacePublicId!,
userId: user.id,
userEmail: user.email ?? "",
...(resolvedSlug && { workspaceSlug: resolvedSlug }),
...(workspaceName && { workspaceName }),
...(workspaceDescription && { workspaceDescription }),
...(subscriptionId && { subscriptionId: String(subscriptionId) }),
plan: body.plan ?? "pro",
isNewWorkspace: workspaceName ? "true" : "false",
},
});
return res.status(200).json({ url: session.url });
} catch (error) {
return res.status(500).json({ error: "Error creating checkout session" });
}
}),
const workspace = await workspaceRepo.getAllByUserId(db, user.id);
const isMemberOfWorkspace = workspace.some(
({ workspace }) => workspace.publicId === body.workspacePublicId,
);
if (!isMemberOfWorkspace) {
return new Response(JSON.stringify({ error: "Unauthorized" }), {
status: 403,
headers: { "Content-Type": "application/json" },
});
}
const subscription = await subscriptionRepo.create(db, {
plan: "pro",
referenceId: workspacePublicId,
userId: user.id,
stripeCustomerId: user.stripeCustomerId ?? "",
status: "incomplete",
});
const subscriptionId = subscription?.id;
if (!subscriptionId) {
return res.status(500).json({ error: "Error creating subscription" });
}
const session = await stripe.checkout.sessions.create({
mode: "subscription",
payment_method_collection: "always",
line_items: [
{
price: process.env.STRIPE_PRO_PLAN_MONTHLY_PRICE_ID,
quantity: 1,
},
],
subscription_data: {
trial_period_days: 14,
},
success_url: `${env("NEXT_PUBLIC_BASE_URL")}${successUrl}`,
cancel_url: `${env("NEXT_PUBLIC_BASE_URL")}${cancelUrl}`,
client_reference_id: workspacePublicId,
customer: user.stripeCustomerId ?? undefined,
metadata: {
...(slug && { workspaceSlug: slug }),
workspacePublicId,
userId: user.id,
subscriptionId,
},
});
return res.status(200).json({ url: session.url });
} catch (error) {
console.error("Error:", error);
return res.status(500).json({ error: "Error creating checkout session" });
}
},
);

View File

@@ -1,14 +1,12 @@
import { randomUUID } from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";
import type { Readable } from "node:stream";
import { createNextApiContext } from "@kan/api/trpc";
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { createLogger } from "@kan/logger";
import { createStripeClient } from "@kan/stripe";
const log = createLogger("api");
const log = createLogger("stripe-webhook");
async function buffer(readable: Readable) {
const chunks = [];
@@ -23,9 +21,6 @@ export default async function handler(
res: NextApiResponse,
) {
const stripe = createStripeClient();
const start = Date.now();
const requestId = randomUUID();
const procedure = req.url?.split("?")[0] ?? "/api/stripe/webhook";
if (req.method !== "POST") {
return res.status(405).json({ message: "Method not allowed" });
@@ -49,85 +44,29 @@ export default async function handler(
const { db } = await createNextApiContext(req);
log.info({ eventType: event.type, eventId: event.id }, "Stripe webhook received");
switch (event.type) {
case "checkout.session.completed": {
const checkoutSession = event.data.object;
const meta = checkoutSession.metadata;
if (!meta?.workspacePublicId) break;
const metaData = checkoutSession.metadata;
const plan = meta.plan === "team" ? "team" : ("pro" as const);
if (
meta.isNewWorkspace === "true" &&
meta.workspaceName &&
meta.userId &&
meta.userEmail
) {
const existing = await workspaceRepo.getByPublicId(db, meta.workspacePublicId);
if (!existing) {
const slug = meta.workspaceSlug ?? meta.workspacePublicId;
await workspaceRepo.create(db, {
publicId: meta.workspacePublicId,
name: meta.workspaceName,
slug,
plan,
createdBy: meta.userId,
createdByEmail: meta.userEmail,
...(meta.workspaceDescription && {
description: meta.workspaceDescription,
}),
});
await subscriptionRepo.create(db, {
plan,
referenceId: meta.workspacePublicId,
userId: meta.userId,
stripeCustomerId: checkoutSession.customer as string,
status: "active",
});
}
} else {
// Existing workspace upgrade — update plan (and slug for pro)
await workspaceRepo.update(db, meta.workspacePublicId, {
plan,
...(plan === "pro" && meta.workspaceSlug && { slug: meta.workspaceSlug }),
if (metaData?.workspacePublicId) {
await workspaceRepo.update(db, metaData.workspacePublicId, {
...(metaData.workspaceSlug && { slug: metaData.workspaceSlug }),
plan: "pro",
});
}
break;
}
default:
log.warn({ eventType: event.type }, "Unhandled Stripe event type");
}
log.info(
{
requestId,
procedure,
transport: "rest",
duration: Date.now() - start,
status: 200,
input: { eventType: event.type, eventId: event.id },
},
"API OK",
);
return res.status(200).json({ received: true });
} catch (err) {
log.error(
{
requestId,
procedure,
transport: "rest",
duration: Date.now() - start,
status: 400,
err,
},
"API error",
);
log.error({ err }, "Stripe webhook handler failed");
return res.status(400).json({ message: "Webhook handler failed" });
}
}

View File

@@ -1,58 +1,52 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { addYears } from "date-fns";
import { createNextApiContext } from "@kan/api/trpc";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { withRateLimit } from "@kan/api/utils/rateLimit";
import { integrations } from "@kan/db/schema";
import { addYears } from "date-fns";
import { withRateLimit } from "@kan/api/utils/rateLimit";
export default withRateLimit(
{ points: 100, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "POST") {
return res.status(405).json({ message: "Method not allowed" });
}
async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "POST") {
return res.status(405).json({ message: "Method not allowed" });
}
const { user } = await createNextApiContext(req);
const { user } = await createNextApiContext(req);
if (!user)
return res.status(401).json({ message: "User not authenticated" });
if (!user)
return res.status(401).json({ message: "User not authenticated" });
const apiKey = process.env.TRELLO_APP_API_KEY;
const apiKey = process.env.TRELLO_APP_API_KEY;
if (!apiKey)
return res
.status(500)
.json({ message: "Trello API key not set in Environment Variables" });
if (!apiKey)
return res.status(500).json({ message: "Trello API key not set in Environment Variables" });
const token = req.body.token;
const token = req.body.token;
if (!token) return res.status(400).json({ message: "No token found" });
if (!token)
return res.status(400).json({ message: "No token found" });
try {
const { db } = await createNextApiContext(req);
try {
const { db } = await createNextApiContext(req);
await db
.insert(integrations)
.values({
provider: "trello",
userId: user.id,
accessToken: token,
expiresAt: addYears(new Date(), 1),
})
.onConflictDoUpdate({
set: {
accessToken: token,
expiresAt: addYears(new Date(), 1),
},
target: [integrations.userId, integrations.provider],
});
await db.insert(integrations).values({
provider: "trello",
userId: user.id,
accessToken: token,
expiresAt: addYears(new Date(), 1),
}).onConflictDoUpdate({
set: {
accessToken: token,
expiresAt: addYears(new Date(), 1),
},
target: [integrations.userId, integrations.provider],
});
return res
.status(200)
.json({ message: "Trello authentication successful" });
} catch (err) {
return res.status(400).json({ message: "Trello authentication failed" });
}
}),
);
return res.status(200).json({ message: "Trello authentication successful" });
} catch (err) {
console.error("Trello authentication error:", err);
return res.status(400).json({ message: "Trello authentication failed" });
}
},
);

View File

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

View File

@@ -2,7 +2,6 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { Upload } from "@aws-sdk/lib-storage";
import { createNextApiContext } from "@kan/api/trpc";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { assertPermission } from "@kan/api/utils/permissions";
import { withRateLimit } from "@kan/api/utils/rateLimit";
import * as cardRepo from "@kan/db/repository/card.repo";
@@ -23,7 +22,7 @@ export const config = {
export default withRateLimit(
{ points: 100, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
@@ -37,9 +36,7 @@ export default withRateLimit(
const bucket = env.NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME;
if (!bucket) {
return res
.status(500)
.json({ error: "Attachments bucket not configured" });
return res.status(500).json({ error: "Attachments bucket not configured" });
}
const cardPublicId = req.query.cardPublicId;
@@ -58,9 +55,7 @@ export default withRateLimit(
}
if (!Number.isFinite(contentLength) || contentLength <= 0) {
return res
.status(400)
.json({ error: "Missing or invalid content length" });
return res.status(400).json({ error: "Missing or invalid content length" });
}
if (contentLength > MAX_SIZE_BYTES) {
@@ -134,7 +129,8 @@ export default withRateLimit(
return res.status(200).json({ attachment });
} catch (error) {
console.error("Attachment upload failed", error);
return res.status(500).json({ error: "Internal server error" });
}
}),
},
);

View File

@@ -2,17 +2,13 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { createNextApiContext } from "@kan/api/trpc";
import { withApiLogging } from "@kan/api/utils/apiLogging";
import { withRateLimit } from "@kan/api/utils/rateLimit";
import * as userRepo from "@kan/db/repository/user.repo";
import { createS3Client } from "@kan/shared/utils";
import { env } from "~/env";
import { withRateLimit } from "@kan/api/utils/rateLimit";
import { createS3Client } from "@kan/shared/utils";
const MAX_SIZE_BYTES = parseInt(
process.env.S3_AVATAR_UPLOAD_LIMIT || "2097152",
10,
); // Default 2MB
const MAX_SIZE_BYTES = parseInt(process.env.S3_AVATAR_UPLOAD_LIMIT || '2097152', 10); // Default 2MB
const allowedContentTypes = ["image/jpeg", "image/png", "image/webp"];
export const config = {
@@ -23,7 +19,7 @@ export const config = {
export default withRateLimit(
{ points: 100, duration: 60 },
withApiLogging(async (req: NextApiRequest, res: NextApiResponse) => {
async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
@@ -55,9 +51,7 @@ export default withRateLimit(
}
if (!Number.isFinite(contentLength) || contentLength <= 0) {
return res
.status(400)
.json({ error: "Missing or invalid content length" });
return res.status(400).json({ error: "Missing or invalid content length" });
}
if (contentLength > MAX_SIZE_BYTES) {
@@ -99,7 +93,9 @@ export default withRateLimit(
user: updatedUser,
});
} catch (error) {
console.error("Avatar upload failed", error);
return res.status(500).json({ error: "Internal server error" });
}
}),
},
);

View File

@@ -1,31 +0,0 @@
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { env } from "next-runtime-env";
import { authClient } from "@kan/auth/client";
import { PageHead } from "~/components/PageHead";
import SelectPlanView from "~/views/onboarding/select-plan";
export default function SelectPlanPage() {
const router = useRouter();
const { data: session, isPending } = authClient.useSession();
useEffect(() => {
if (!isPending && !session?.user) {
router.push("/login");
}
if (!isPending && env("NEXT_PUBLIC_KAN_ENV") !== "cloud") {
router.push("/boards");
}
}, [session, isPending, router]);
if (isPending || !session?.user) return null;
return (
<>
<PageHead title="Select plan | kan.bn" />
<SelectPlanView />
</>
);
}

View File

@@ -1,28 +0,0 @@
import { useRouter } from "next/navigation";
import { env } from "next-runtime-env";
import { useEffect } from "react";
import { authClient } from "@kan/auth/client";
import { PageHead } from "~/components/PageHead";
import WorkspaceDetailsView from "~/views/onboarding/workspace-details";
export default function WorkspaceDetailsPage() {
const router = useRouter();
const { data: session, isPending } = authClient.useSession();
useEffect(() => {
if (!isPending && !session?.user) router.push("/login");
if (!isPending && env("NEXT_PUBLIC_KAN_ENV") !== "cloud")
router.push("/boards");
}, [session, isPending, router]);
if (isPending || !session?.user) return null;
return (
<>
<PageHead title="Create workspace | kan.bn" />
<WorkspaceDetailsView />
</>
);
}

View File

@@ -1,57 +0,0 @@
import React, {
createContext,
useContext,
useEffect,
useState,
type ReactNode,
} from "react";
export type FontSize = "small" | "medium" | "large";
const fontSizeMap: Record<FontSize, string> = {
small: "14px",
medium: "16px",
large: "18px",
};
interface FontSizeContextProps {
fontSize: FontSize;
setFontSize: (size: FontSize) => void;
}
const FontSizeContext = createContext<FontSizeContextProps | undefined>(
undefined,
);
export const FontSizeProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const [fontSize, setFontSizeState] = useState<FontSize>("medium");
const setFontSize = (size: FontSize) => {
document.documentElement.style.fontSize = fontSizeMap[size];
localStorage.setItem("fontSize", size);
setFontSizeState(size);
};
useEffect(() => {
const stored = localStorage.getItem("fontSize") as FontSize | null;
const size = stored && fontSizeMap[stored] ? stored : "medium";
document.documentElement.style.fontSize = fontSizeMap[size];
setFontSizeState(size);
}, []);
return (
<FontSizeContext.Provider value={{ fontSize, setFontSize }}>
{children}
</FontSizeContext.Provider>
);
};
export const useFontSize = (): FontSizeContextProps => {
const context = useContext(FontSizeContext);
if (!context) {
throw new Error("useFontSize must be used within a FontSizeProvider");
}
return context;
};

View File

@@ -17,7 +17,7 @@ interface Workspace {
description: string | null | undefined;
publicId: string;
slug: string | undefined;
plan: "free" | "team" | "pro" | "enterprise" | undefined;
plan: "free" | "pro" | "enterprise" | undefined;
role: "admin" | "member" | "guest";
weekStartDay: 0 | 1 | 6;
}
@@ -27,7 +27,7 @@ const initialWorkspace: Workspace = {
description: null,
publicId: "",
slug: "",
plan: "free" as const,
plan: "free",
role: "member",
weekStartDay: 1,
};
@@ -50,15 +50,7 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
const workspacePublicId = useSearchParams().get("workspacePublicId");
const [pendingWorkspaceId, setPendingWorkspaceId] = useState<string | null>(
workspacePublicId,
);
const pollAttemptsRef = React.useRef(0);
const MAX_POLL_ATTEMPTS = 5;
const { data, isLoading } = api.workspace.all.useQuery(undefined, {
refetchInterval: pendingWorkspaceId ? 2000 : false,
});
const { data, isLoading } = api.workspace.all.useQuery();
const utils = api.useUtils();
const switchWorkspace = (_workspace: Workspace) => {
@@ -102,16 +94,7 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
({ workspace }) => workspace.publicId === storedWorkspaceId,
);
if (!selectedWorkspace?.workspace) {
pollAttemptsRef.current += 1;
if (pollAttemptsRef.current >= MAX_POLL_ATTEMPTS) {
setPendingWorkspaceId(null);
}
return;
}
pollAttemptsRef.current = 0;
setPendingWorkspaceId(null);
if (!selectedWorkspace?.workspace) return;
setWorkspace({
publicId: selectedWorkspace.workspace.publicId,

View File

@@ -2,10 +2,6 @@
@tailwind components;
@tailwind utilities;
html {
font-size: 14px;
}
[contenteditable="true"]:empty:before {
content: attr(placeholder);
display: block;

View File

@@ -1,5 +1,3 @@
import { env } from "next-runtime-env";
export const formatToArray = (
value: string | string[] | undefined,
): string[] => {
@@ -52,21 +50,5 @@ export const getAvatarUrl = (imageOrKey: string | null) => {
return imageOrKey;
}
// Construct URL from S3 key
const useVirtualHosted = env("NEXT_PUBLIC_USE_VIRTUAL_HOSTED_URLS") === "true";
const storageDomain = env("NEXT_PUBLIC_STORAGE_DOMAIN");
const storageUrl = env("NEXT_PUBLIC_STORAGE_URL");
const bucket = env("NEXT_PUBLIC_AVATAR_BUCKET_NAME");
if (useVirtualHosted && storageDomain && bucket) {
// Virtual-hosted style: https://{bucket}.{domain}/{key}
return `https://${bucket}.${storageDomain}/${imageOrKey}`;
}
if (storageUrl && bucket) {
// Path-style: {storageUrl}/{bucket}/{key}
return `${storageUrl}/${bucket}/${imageOrKey}`;
}
return "";
};

View File

@@ -1,41 +0,0 @@
import { t } from "@lingui/core/macro";
import { useModal } from "~/providers/modal";
import { api } from "~/utils/api";
import { DueDateSelector } from "~/views/card/components/DueDateSelector";
export function CardContextDueDateModal() {
const { entityId: cardPublicId, closeModal } = useModal();
const { data: card, isLoading } = api.card.byId.useQuery(
{ cardPublicId: cardPublicId ?? "" },
{ enabled: !!cardPublicId && cardPublicId.length >= 12 },
);
if (!cardPublicId) return null;
return (
<div className="p-4">
<h2 className="mb-4 text-lg font-semibold text-light-1000 dark:text-dark-1000">
{t`Set due date`}
</h2>
{isLoading ? (
<div className="h-10 w-full animate-pulse rounded bg-light-200 dark:bg-dark-300" />
) : (
<DueDateSelector
cardPublicId={cardPublicId}
dueDate={card?.dueDate ?? null}
/>
)}
<div className="mt-4 flex justify-end">
<button
type="button"
onClick={closeModal}
className="rounded-md border border-light-300 bg-light-50 px-3 py-1.5 text-sm font-medium text-light-1000 hover:bg-light-200 dark:border-dark-400 dark:bg-dark-200 dark:text-dark-1000 dark:hover:bg-dark-300"
>
{t`Done`}
</button>
</div>
</div>
);
}

View File

@@ -1,284 +0,0 @@
import {
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
Transition,
} from "@headlessui/react";
import { t } from "@lingui/core/macro";
import { useState } from "react";
import { HiChevronDown } from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
import Button from "~/components/Button";
import Input from "~/components/Input";
import { useModal } from "~/providers/modal";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
interface CardContextDuplicateModalProps {
boardPublicId?: string;
isTemplate?: boolean;
}
export function CardContextDuplicateModal({
boardPublicId: boardPublicIdProp,
isTemplate: isTemplateProp,
}: CardContextDuplicateModalProps = {}) {
const { entityId: cardPublicId, closeModal, getModalState } = useModal();
const { showPopup } = usePopup();
const utils = api.useUtils();
const modalState = getModalState("CARD_CONTEXT_DUPLICATE") as
| { boardPublicId: string; isTemplate?: boolean }
| undefined;
const boardPublicId =
boardPublicIdProp ?? modalState?.boardPublicId ?? "";
const isTemplate = isTemplateProp ?? modalState?.isTemplate ?? false;
const [listPublicId, setListPublicId] = useState("");
const [copyLabels, setCopyLabels] = useState(true);
const [copyMembers, setCopyMembers] = useState(true);
const [copyChecklists, setCopyChecklists] = useState(true);
const [position, setPosition] = useState<string>("");
const [title, setTitle] = useState("");
const { data: card, isLoading: isCardLoading } = api.card.byId.useQuery(
{ cardPublicId: cardPublicId ?? "" },
{ enabled: !!cardPublicId && cardPublicId.length >= 12 },
);
const boardType = isTemplate ? "template" : "regular";
const { data: board } = api.board.byId.useQuery(
{ boardPublicId, type: boardType },
{ enabled: !!boardPublicId },
);
const lists = board?.lists ?? [];
const listOptions = lists.map((l) => ({ publicId: l.publicId, name: l.name }));
const currentListPublicId = card?.list?.publicId;
const hasLabels = (card?.labels?.length ?? 0) > 0;
const hasMembers = (card?.members?.length ?? 0) > 0;
const hasChecklists = (card?.checklists?.length ?? 0) > 0;
const hasAnyCopyOption = hasLabels || hasMembers || hasChecklists;
const duplicateCard = api.card.duplicate.useMutation({
onSuccess: () => {
showPopup({
header: t`Card duplicated`,
icon: "success",
message: t`The card has been duplicated.`,
});
closeModal();
},
onError: () => {
showPopup({
header: t`Unable to duplicate card`,
message: t`Please try again.`,
icon: "error",
});
},
onSettled: async () => {
await utils.board.byId.invalidate();
},
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!cardPublicId || !listPublicId) return;
const indexNum =
position === "" ? undefined : parseInt(position, 10);
if (
position !== "" &&
(indexNum === undefined || isNaN(indexNum) || indexNum < 0)
)
return;
duplicateCard.mutate({
cardPublicId,
listPublicId,
copyLabels,
copyMembers,
copyChecklists,
...(typeof indexNum === "number" && { index: indexNum }),
title: title.trim() || undefined,
});
};
if (!cardPublicId) return null;
return (
<form onSubmit={handleSubmit} className="p-4">
<h2 className="mb-4 text-lg font-semibold text-light-1000 dark:text-dark-1000">
{t`Duplicate card`}
</h2>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-light-900 dark:text-dark-900">
{t`List`}
</label>
<Listbox value={listPublicId} onChange={setListPublicId}>
<div className="relative">
<ListboxButton
className={twMerge(
"relative w-full cursor-pointer rounded-md border border-light-300 bg-white py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-light-400 focus:ring-offset-0 dark:border-dark-400 dark:bg-dark-200 dark:text-dark-1000 dark:focus:ring-dark-500",
!listPublicId && "text-light-600 dark:text-dark-600",
)}
>
<span className="block truncate">
{listPublicId
? listOptions.find((o) => o.publicId === listPublicId)
?.name
: t`Select a list`}
</span>
<span className="pointer-events-none absolute right-2 top-1/2 flex -translate-y-1/2 items-center">
<HiChevronDown
className="h-4 w-4 text-light-600 dark:text-dark-600"
aria-hidden
/>
</span>
</ListboxButton>
<Transition
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<ListboxOptions className="absolute z-50 mt-1 w-full overflow-hidden rounded-md border border-light-200 bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-400 dark:bg-dark-200">
<div className="max-h-60 overflow-y-auto py-1 pr-1 scrollbar scrollbar-track-rounded-[4px] scrollbar-thumb-rounded-[4px] scrollbar-w-[8px] scrollbar-track-light-200 scrollbar-thumb-light-400 dark:scrollbar-track-dark-100 dark:scrollbar-thumb-dark-600">
{listOptions.map((option) => {
const isCurrentList =
option.publicId === currentListPublicId;
return (
<ListboxOption
key={option.publicId}
value={option.publicId}
disabled={isCurrentList}
className={({ focus }) =>
twMerge(
"relative select-none py-2 pl-3 pr-9 text-sm",
isCurrentList
? "cursor-default opacity-50"
: "cursor-pointer",
!isCurrentList && focus
? "bg-light-200 text-light-1000 dark:bg-dark-400 dark:text-dark-1000"
: "text-light-900 dark:text-dark-900",
)
}
>
{option.name}
</ListboxOption>
);
})}
</div>
</ListboxOptions>
</Transition>
</div>
</Listbox>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-light-900 dark:text-dark-900">
{t`Title (optional)`}
</label>
<Input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder={card?.title ?? ""}
className="w-full"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-light-900 dark:text-dark-900">
{t`Position in list (0 = first, leave empty for end)`}
</label>
<Input
type="number"
min={0}
value={position}
onChange={(e) => setPosition(e.target.value)}
placeholder={t`End of list`}
className="w-full"
/>
</div>
{hasAnyCopyOption && (
<div className="flex flex-col gap-2">
{hasLabels && (
<div className="flex items-center gap-3">
<label className="relative mt-[2px] inline-flex h-[16px] w-[16px] flex-shrink-0 cursor-pointer items-center justify-center">
<input
type="checkbox"
checked={copyLabels}
onChange={(e) => setCopyLabels(e.target.checked)}
className={twMerge(
"h-[16px] w-[16px] appearance-none rounded-md border border-light-500 bg-transparent outline-none ring-0 checked:bg-blue-600 focus:shadow-none focus:ring-0 focus:ring-offset-0 focus-visible:outline-none dark:border-dark-500 dark:hover:border-dark-500",
"cursor-pointer",
)}
/>
</label>
<span className="text-sm text-light-900 dark:text-dark-900">
{t`Copy labels`}
</span>
</div>
)}
{hasMembers && (
<div className="flex items-center gap-3">
<label className="relative mt-[2px] inline-flex h-[16px] w-[16px] flex-shrink-0 cursor-pointer items-center justify-center">
<input
type="checkbox"
checked={copyMembers}
onChange={(e) => setCopyMembers(e.target.checked)}
className={twMerge(
"h-[16px] w-[16px] appearance-none rounded-md border border-light-500 bg-transparent outline-none ring-0 checked:bg-blue-600 focus:shadow-none focus:ring-0 focus:ring-offset-0 focus-visible:outline-none dark:border-dark-500 dark:hover:border-dark-500",
"cursor-pointer",
)}
/>
</label>
<span className="text-sm text-light-900 dark:text-dark-900">
{t`Copy members`}
</span>
</div>
)}
{hasChecklists && (
<div className="flex items-center gap-3">
<label className="relative mt-[2px] inline-flex h-[16px] w-[16px] flex-shrink-0 cursor-pointer items-center justify-center">
<input
type="checkbox"
checked={copyChecklists}
onChange={(e) => setCopyChecklists(e.target.checked)}
className={twMerge(
"h-[16px] w-[16px] appearance-none rounded-md border border-light-500 bg-transparent outline-none ring-0 checked:bg-blue-600 focus:shadow-none focus:ring-0 focus:ring-offset-0 focus-visible:outline-none dark:border-dark-500 dark:hover:border-dark-500",
"cursor-pointer",
)}
/>
</label>
<span className="text-sm text-light-900 dark:text-dark-900">
{t`Copy checklists`}
</span>
</div>
)}
</div>
)}
</div>
<div className="mt-6 flex justify-end gap-2">
<Button type="button" variant="secondary" onClick={closeModal}>
{t`Cancel`}
</Button>
<Button
type="submit"
variant="primary"
disabled={
!listPublicId ||
duplicateCard.isPending ||
isCardLoading ||
lists.length === 0
}
>
{duplicateCard.isPending ? t`Duplicating…` : t`Duplicate`}
</Button>
</div>
</form>
);
}

View File

@@ -1,53 +0,0 @@
import { t } from "@lingui/core/macro";
import { useModal } from "~/providers/modal";
import { api } from "~/utils/api";
import LabelIcon from "~/components/LabelIcon";
import LabelSelector from "~/views/card/components/LabelSelector";
export function CardContextLabelsModal() {
const { entityId: cardPublicId, closeModal } = useModal();
const { data: card, isLoading } = api.card.byId.useQuery(
{ cardPublicId: cardPublicId ?? "" },
{ enabled: !!cardPublicId && cardPublicId.length >= 12 },
);
const boardLabels = card?.list?.board?.labels ?? [];
const selectedLabels = card?.labels ?? [];
const formattedLabels = boardLabels.map((label) => ({
key: label.publicId,
value: label.name,
selected: selectedLabels.some((l) => l.publicId === label.publicId),
leftIcon: <LabelIcon colourCode={label.colourCode} />,
}));
if (!cardPublicId) return null;
return (
<div className="p-4">
<h2 className="mb-4 text-lg font-semibold text-light-1000 dark:text-dark-1000">
{t`Labels`}
</h2>
{isLoading ? (
<div className="h-10 w-full animate-pulse rounded bg-light-200 dark:bg-dark-300" />
) : (
<LabelSelector
cardPublicId={cardPublicId}
labels={formattedLabels}
isLoading={isLoading}
/>
)}
<div className="mt-4 flex justify-end">
<button
type="button"
onClick={closeModal}
className="rounded-md border border-light-300 bg-light-50 px-3 py-1.5 text-sm font-medium text-light-1000 hover:bg-light-200 dark:border-dark-400 dark:bg-dark-200 dark:text-dark-1000 dark:hover:bg-dark-300"
>
{t`Done`}
</button>
</div>
</div>
);
}

View File

@@ -1,73 +0,0 @@
import { t } from "@lingui/core/macro";
import { useModal } from "~/providers/modal";
import { api } from "~/utils/api";
import { formatMemberDisplayName, getAvatarUrl } from "~/utils/helpers";
import Avatar from "~/components/Avatar";
import MemberSelector from "~/views/card/components/MemberSelector";
export function CardContextMembersModal() {
const { entityId: cardPublicId, closeModal } = useModal();
const { data: card, isLoading } = api.card.byId.useQuery(
{ cardPublicId: cardPublicId ?? "" },
{ enabled: !!cardPublicId && cardPublicId.length >= 12 },
);
const board = card?.list?.board;
const workspaceMembers = board?.workspace?.members ?? [];
const selectedMembers = card?.members ?? [];
const formattedMembers = workspaceMembers.map((member) => {
const isSelected = selectedMembers.some(
(m) => m.publicId === member.publicId,
);
return {
key: member.publicId,
value: formatMemberDisplayName(
member.user?.name ?? null,
member.user?.email ?? member.email,
),
imageUrl: member.user?.image ? getAvatarUrl(member.user.image) : undefined,
selected: isSelected,
leftIcon: (
<Avatar
size="xs"
name={member.user?.name ?? ""}
imageUrl={
member.user?.image ? getAvatarUrl(member.user.image) : undefined
}
email={member.user?.email ?? member.email}
/>
),
};
});
if (!cardPublicId) return null;
return (
<div className="p-4">
<h2 className="mb-4 text-lg font-semibold text-light-1000 dark:text-dark-1000">
{t`Manage members`}
</h2>
{isLoading ? (
<div className="h-10 w-full animate-pulse rounded bg-light-200 dark:bg-dark-300" />
) : (
<MemberSelector
cardPublicId={cardPublicId}
members={formattedMembers}
isLoading={isLoading}
/>
)}
<div className="mt-4 flex justify-end">
<button
type="button"
onClick={closeModal}
className="rounded-md border border-light-300 bg-light-50 px-3 py-1.5 text-sm font-medium text-light-1000 hover:bg-light-200 dark:border-dark-400 dark:bg-dark-200 dark:text-dark-1000 dark:hover:bg-dark-300"
>
{t`Done`}
</button>
</div>
</div>
);
}

View File

@@ -1,130 +0,0 @@
import { t } from "@lingui/core/macro";
import { useEffect, useRef } from "react";
import {
HiLink,
HiOutlineCalendar,
HiOutlineDocumentDuplicate,
HiOutlineTag,
HiOutlineTrash,
HiOutlineUserGroup,
HiOutlineArrowRightCircle,
} from "react-icons/hi2";
export type CardContextMenuAction =
| "members"
| "move"
| "labels"
| "dueDate"
| "copyLink"
| "duplicate"
| "delete";
interface CardContextMenuProps {
x: number;
y: number;
onClose: () => void;
onAction: (action: CardContextMenuAction) => void;
canEdit: boolean;
}
const MENU_ITEMS: {
action: CardContextMenuAction;
label: string;
icon: React.ReactNode;
requiresEdit: boolean;
}[] = [
{
action: "members",
label: t`Manage members`,
icon: <HiOutlineUserGroup className="h-4 w-4 shrink-0" />,
requiresEdit: true,
},
{
action: "move",
label: t`Move to another list`,
icon: <HiOutlineArrowRightCircle className="h-4 w-4 shrink-0" />,
requiresEdit: true,
},
{
action: "labels",
label: t`Add / edit label`,
icon: <HiOutlineTag className="h-4 w-4 shrink-0" />,
requiresEdit: true,
},
{
action: "dueDate",
label: t`Set due date`,
icon: <HiOutlineCalendar className="h-4 w-4 shrink-0" />,
requiresEdit: true,
},
{
action: "copyLink",
label: t`Copy link to card`,
icon: <HiLink className="h-4 w-4 shrink-0" />,
requiresEdit: false,
},
{
action: "duplicate",
label: t`Duplicate card`,
icon: <HiOutlineDocumentDuplicate className="h-4 w-4 shrink-0" />,
requiresEdit: true,
},
{
action: "delete",
label: t`Delete card`,
icon: <HiOutlineTrash className="h-4 w-4 shrink-0" />,
requiresEdit: true,
},
];
export function CardContextMenu({
x,
y,
onClose,
onAction,
canEdit,
}: CardContextMenuProps) {
const menuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
onClose();
}
};
const handleEscape = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("keydown", handleEscape);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("keydown", handleEscape);
};
}, [onClose]);
const items = MENU_ITEMS.filter((item) => !item.requiresEdit || canEdit);
return (
<div
ref={menuRef}
className="fixed z-[200] min-w-[200px] rounded-md border border-light-200 bg-white py-1 shadow-lg dark:border-dark-400 dark:bg-dark-200"
style={{ left: x, top: y }}
>
{items.map(({ action, label, icon }) => (
<button
key={action}
type="button"
onClick={() => {
onAction(action);
onClose();
}}
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
>
{icon}
{label}
</button>
))}
</div>
);
}

View File

@@ -1,101 +0,0 @@
import { t } from "@lingui/core/macro";
import { useModal } from "~/providers/modal";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
import { invalidateCard } from "~/utils/cardInvalidation";
export function CardContextMoveListModal() {
const { entityId: cardPublicId, closeModal } = useModal();
const { showPopup } = usePopup();
const utils = api.useUtils();
const { data: card, isLoading } = api.card.byId.useQuery(
{ cardPublicId: cardPublicId ?? "" },
{ enabled: !!cardPublicId && cardPublicId.length >= 12 },
);
const updateCardList = api.card.update.useMutation({
onMutate: async (vars) => {
if (!cardPublicId) return undefined;
await utils.card.byId.cancel();
const previous = utils.card.byId.getData({ cardPublicId });
utils.card.byId.setData({ cardPublicId }, (old) => {
if (!old) return old;
const list = old.list.board?.lists?.find(
(l) => l.publicId === vars.listPublicId,
);
if (!list) return old;
return {
...old,
list: { ...old.list, publicId: list.publicId, name: list.name },
};
});
return { previous };
},
onError: (_err, _vars, ctx) => {
if (cardPublicId && ctx?.previous) {
utils.card.byId.setData({ cardPublicId }, ctx.previous);
}
showPopup({
header: t`Unable to move card`,
message: t`Please try again.`,
icon: "error",
});
},
onSettled: async () => {
if (cardPublicId) {
await invalidateCard(utils, cardPublicId);
await utils.board.byId.invalidate();
}
},
});
const lists = card?.list?.board?.lists ?? [];
const currentListPublicId = card?.list?.publicId;
const handleSelectList = (listPublicId: string) => {
if (listPublicId === currentListPublicId || !cardPublicId) return;
updateCardList.mutate(
{ cardPublicId, listPublicId, index: 0 },
{ onSuccess: closeModal },
);
};
if (!cardPublicId) return null;
return (
<div className="p-4">
<h2 className="mb-4 text-lg font-semibold text-light-1000 dark:text-dark-1000">
{t`Move to list`}
</h2>
{isLoading ? (
<div className="space-y-2">
{[1, 2, 3].map((i) => (
<div
key={i}
className="h-10 w-full animate-pulse rounded bg-light-200 dark:bg-dark-300"
/>
))}
</div>
) : (
<div className="max-h-[60vh] overflow-y-auto pr-1 scrollbar scrollbar-track-rounded-[4px] scrollbar-thumb-rounded-[4px] scrollbar-w-[8px] scrollbar-track-light-200 scrollbar-thumb-light-400 dark:scrollbar-track-dark-100 dark:scrollbar-thumb-dark-600">
<ul className="space-y-1">
{lists.map((list) => (
<li key={list.publicId}>
<button
type="button"
onClick={() => handleSelectList(list.publicId)}
disabled={list.publicId === currentListPublicId}
className="w-full rounded-md px-3 py-2 text-left text-sm hover:bg-light-200 disabled:opacity-50 dark:hover:bg-dark-400"
>
{list.name}
</button>
</li>
))}
</ul>
</div>
)}
</div>
);
}

View File

@@ -15,7 +15,6 @@ import {
import type { UpdateBoardInput } from "@kan/api/types";
import type { CardContextMenuAction } from "./components/CardContextMenu";
import Button from "~/components/Button";
import { DeleteLabelConfirmation } from "~/components/DeleteLabelConfirmation";
import { LabelForm } from "~/components/LabelForm";
@@ -27,23 +26,16 @@ import { StrictModeDroppable as Droppable } from "~/components/StrictModeDroppab
import { Tooltip } from "~/components/Tooltip";
import { EditYouTubeModal } from "~/components/YouTubeEmbed/EditYouTubeModal";
import { useDragToScroll } from "~/hooks/useDragToScroll";
import { usePermissions } from "~/hooks/usePermissions";
import { useScrollRestore } from "~/hooks/useScrollRestore";
import { usePermissions } from "~/hooks/usePermissions";
import { useKeyboardShortcut } from "~/providers/keyboard-shortcuts";
import { useModal } from "~/providers/modal";
import { usePopup } from "~/providers/popup";
import { useWorkspace } from "~/providers/workspace";
import { api } from "~/utils/api";
import { formatToArray } from "~/utils/helpers";
import { DeleteCardConfirmation } from "~/views/card/components/DeleteCardConfirmation";
import BoardDropdown from "./components/BoardDropdown";
import Card from "./components/Card";
import { CardContextDueDateModal } from "./components/CardContextDueDateModal";
import { CardContextDuplicateModal } from "./components/CardContextDuplicateModal";
import { CardContextLabelsModal } from "./components/CardContextLabelsModal";
import { CardContextMembersModal } from "./components/CardContextMembersModal";
import { CardContextMenu } from "./components/CardContextMenu";
import { CardContextMoveListModal } from "./components/CardContextMoveListModal";
import { DeleteBoardConfirmation } from "./components/DeleteBoardConfirmation";
import { DeleteListConfirmation } from "./components/DeleteListConfirmation";
import Filters from "./components/Filters";
@@ -63,18 +55,11 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
const utils = api.useUtils();
const { showPopup } = usePopup();
const { workspace } = useWorkspace();
const { openModal, modalContentType, entityId, isOpen, setModalState } =
useModal();
const { openModal, modalContentType, entityId, isOpen } = useModal();
const [selectedPublicListId, setSelectedPublicListId] =
useState<PublicListId>("");
const [isInitialLoading, setIsInitialLoading] = useState(true);
const [contextMenu, setContextMenu] = useState<{
x: number;
y: number;
cardPublicId: string;
} | null>(null);
const { ref: scrollRef, onMouseDown } = useDragToScroll({
enabled: true,
direction: "horizontal",
@@ -149,10 +134,7 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
// Redirect to 404 if board doesn't exist
useEffect(() => {
if (router.isReady && boardId && !isQueryLoading) {
if (
error?.data?.code === "NOT_FOUND" ||
(!boardData && !isQueryLoading)
) {
if (error?.data?.code === "NOT_FOUND" || (!boardData && !isQueryLoading)) {
router.replace("/404");
}
}
@@ -170,12 +152,7 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
const isLoading = isInitialLoading || isQueryLoading;
useScrollRestore(
boardId,
scrollRef,
router,
!isLoading && (boardData?.lists.length ?? 0) > 0,
);
useScrollRestore(boardId, scrollRef, router, !isLoading && (boardData?.lists.length ?? 0) > 0);
const updateListMutation = api.list.update.useMutation({
onMutate: async (args) => {
@@ -290,56 +267,6 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
setSelectedPublicListId(publicBoardId);
};
const handleCardContextMenuAction = (action: CardContextMenuAction) => {
const cardPublicId = contextMenu?.cardPublicId;
if (!cardPublicId) return;
setContextMenu(null);
if (action === "copyLink") {
const path = isTemplate
? `/templates/${boardId}/cards/${cardPublicId}`
: `/cards/${cardPublicId}`;
const url = `${typeof window !== "undefined" ? window.location.origin : ""}${path}`;
void navigator.clipboard.writeText(url).then(
() => {
showPopup({
header: t`Link copied`,
icon: "success",
message: t`Card URL copied to clipboard`,
});
},
() => {
showPopup({
header: t`Unable to copy link`,
icon: "error",
message: t`Please try again.`,
});
},
);
return;
}
if (action === "duplicate") {
setModalState("CARD_CONTEXT_DUPLICATE", {
boardPublicId: boardId ?? "",
isTemplate: !!isTemplate,
});
openModal("CARD_CONTEXT_DUPLICATE", cardPublicId);
return;
}
if (action === "delete") {
openModal("DELETE_CARD", cardPublicId);
return;
}
const modalType =
action === "members"
? "CARD_CONTEXT_MEMBERS"
: action === "move"
? "CARD_CONTEXT_MOVE_LIST"
: action === "labels"
? "CARD_CONTEXT_LABELS"
: "CARD_CONTEXT_DUE_DATE";
openModal(modalType, cardPublicId);
};
const onDragEnd = ({
source: _source,
destination,
@@ -476,49 +403,6 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
>
<EditYouTubeModal />
</Modal>
<Modal
modalSize="sm"
isVisible={isOpen && modalContentType === "CARD_CONTEXT_MEMBERS"}
>
<CardContextMembersModal />
</Modal>
<Modal
modalSize="sm"
isVisible={isOpen && modalContentType === "CARD_CONTEXT_MOVE_LIST"}
>
<CardContextMoveListModal />
</Modal>
<Modal
modalSize="sm"
isVisible={isOpen && modalContentType === "CARD_CONTEXT_LABELS"}
>
<CardContextLabelsModal />
</Modal>
<Modal
modalSize="sm"
isVisible={isOpen && modalContentType === "CARD_CONTEXT_DUE_DATE"}
>
<CardContextDueDateModal />
</Modal>
<Modal
modalSize="md"
isVisible={isOpen && modalContentType === "CARD_CONTEXT_DUPLICATE"}
>
<CardContextDuplicateModal
boardPublicId={boardId ?? ""}
isTemplate={!!isTemplate}
/>
</Modal>
<Modal
modalSize="sm"
isVisible={isOpen && modalContentType === "DELETE_CARD"}
>
<DeleteCardConfirmation
cardPublicId={entityId}
boardPublicId={boardId ?? ""}
/>
</Modal>
</>
);
};
@@ -721,33 +605,18 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
)
e.preventDefault();
}}
onContextMenu={(e) => {
if (
card.publicId.startsWith(
"PLACEHOLDER",
)
)
return;
e.preventDefault();
setContextMenu({
x: e.clientX,
y: e.clientY,
cardPublicId: card.publicId,
});
}}
key={card.publicId}
href={
isTemplate
? `/templates/${boardId}/cards/${card.publicId}`
: `/cards/${card.publicId}`
}
className={`mb-2 flex !cursor-pointer flex-col ${
card.publicId.startsWith(
"PLACEHOLDER",
)
? "pointer-events-none"
: ""
}`}
className={`mb-2 flex !cursor-pointer flex-col ${card.publicId.startsWith(
"PLACEHOLDER",
)
? "pointer-events-none"
: ""
}`}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
@@ -784,15 +653,6 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
</>
) : null}
</div>
{contextMenu && (
<CardContextMenu
x={contextMenu.x}
y={contextMenu.y}
onClose={() => setContextMenu(null)}
onAction={handleCardContextMenuAction}
canEdit={!!canEditCard}
/>
)}
{renderModalContent()}
</div>
</>

View File

@@ -1,11 +1,11 @@
import type { DraggableProvided } from "react-beautiful-dnd";
import { t } from "@lingui/core/macro";
import { useState } from "react";
import { useEffect, useState } from "react";
import ContentEditable from "react-contenteditable";
import { HiXMark } from "react-icons/hi2";
import { RiDraggable } from "react-icons/ri";
import { twMerge } from "tailwind-merge";
import PlainTextEditor from "~/components/PlainTextEditor";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
import { invalidateCard } from "~/utils/cardInvalidation";
@@ -33,7 +33,9 @@ export default function ChecklistItemRow({
}: ChecklistItemRowProps) {
const utils = api.useUtils();
const { showPopup } = usePopup();
const [completed, setCompleted] = useState(item.completed);
const [title, setTitle] = useState("");
const [completed, setCompleted] = useState(false);
const updateItem = api.checklist.updateItem.useMutation({
onMutate: async (vars) => {
@@ -101,6 +103,21 @@ export default function ChecklistItemRow({
},
});
// Only resync from props when switching items to avoid clobbering edits
useEffect(() => {
setTitle(item.title);
setCompleted(item.completed);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item.publicId]);
const sanitizeHtmlToPlainText = (html: string): string =>
html
.replace(/<br\s*\/?>(\n)?/gi, "\n")
.replace(/<div><br\s*\/?><\/div>/gi, "")
.replace(/<[^>]*>/g, "")
.replace(/&nbsp;/g, " ")
.trim();
const handleToggleCompleted = () => {
if (viewOnly) return;
setCompleted((prev) => !prev);
@@ -110,8 +127,14 @@ export default function ChecklistItemRow({
});
};
const commitTitle = (plain: string) => {
if (!plain || plain === item.title) return;
const commitTitle = (rawHtml: string) => {
if (viewOnly) return;
const plain = sanitizeHtmlToPlainText(rawHtml);
if (!plain || plain === item.title) {
setTitle(item.title);
return;
}
setTitle(plain);
updateItem.mutate({
checklistItemPublicId: item.publicId,
title: plain,
@@ -160,26 +183,36 @@ export default function ChecklistItemRow({
)}
/>
</label>
<div className="flex-1 pr-7">
<PlainTextEditor
key={item.publicId}
content={item.title}
readOnly={viewOnly}
placeholder={t`Add details...`}
onBlur={commitTitle}
onEnter={(plain) => {
commitTitle(plain);
onCreateNewItem?.();
<ContentEditable
html={title}
disabled={viewOnly}
onChange={(e) => setTitle(e.target.value)}
// @ts-expect-error - valid event
onBlur={(e: Event) => {
const innerHTML = (e.target as HTMLElement).innerHTML;
commitTitle(innerHTML);
}}
onEscape={() => undefined}
className={twMerge(
"m-0 min-h-[20px] w-full p-0 text-sm leading-[20px] text-light-950 dark:text-dark-950",
"m-0 min-h-[20px] w-full p-0 text-sm leading-[20px] text-light-950 outline-none focus-visible:outline-none dark:text-dark-950",
viewOnly && "cursor-default",
)}
placeholder={t`Add details...`}
onKeyDown={(e) => {
if (viewOnly) return;
if (e.key === "Enter") {
e.preventDefault();
const innerHTML = (e.currentTarget as HTMLElement).innerHTML;
commitTitle(innerHTML);
onCreateNewItem?.();
}
if (e.key === "Escape") {
e.preventDefault();
setTitle(item.title);
}
}}
/>
</div>
{!viewOnly && (
<button
type="button"

View File

@@ -78,7 +78,6 @@ export default function LabelSelector({
},
onSettled: async () => {
await invalidateCard(utils, cardPublicId);
await utils.board.byId.invalidate();
},
});

View File

@@ -58,7 +58,6 @@ export default function ListSelector({
},
onSettled: async () => {
await invalidateCard(utils, cardPublicId);
await utils.board.byId.invalidate();
},
});

View File

@@ -85,7 +85,6 @@ export default function MemberSelector({
},
onSettled: async () => {
await invalidateCard(utils, cardPublicId);
await utils.board.byId.invalidate();
},
});

View File

@@ -1,289 +0,0 @@
import { useRouter, useSearchParams } from "next/navigation";
import { t } from "@lingui/core/macro";
import { Radio, RadioGroup } from "@headlessui/react";
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";
import { HiUser } from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
import { authClient } from "@kan/auth/client";
import Button from "~/components/Button";
import { api } from "~/utils/api";
type PlanId = "solo" | "team" | "pro";
type Billing = "monthly" | "annual";
// Each user orbits via a zero-size pivot div at center (144,144) that rotates.
// The icon is offset from the pivot by `radius` pixels along the x-axis.
// A counter-rotation on the icon keeps it upright.
const ORBIT_USERS: Record<
PlanId,
{ id: string; angle: number; radius: number; duration: number }[]
> = {
solo: [],
team: [
{ id: "t1", angle: 45, radius: 104, duration: 50 },
{ id: "t2", angle: 170, radius: 104, duration: 50 },
{ id: "t3", angle: 290, radius: 104, duration: 50 },
],
pro: [
{ id: "t1", angle: 45, radius: 104, duration: 50 },
{ id: "t2", angle: 170, radius: 104, duration: 50 },
{ id: "t3", angle: 290, radius: 104, duration: 50 },
{ id: "p1", angle: 10, radius: 144, duration: 70 },
{ id: "p2", angle: 120, radius: 144, duration: 70 },
{ id: "p4", angle: 240, radius: 144, duration: 70 },
{ id: "p3", angle: 230, radius: 64, duration: 30 },
],
};
export default function SelectPlanView() {
const router = useRouter();
const searchParams = useSearchParams();
const [selected, setSelected] = useState<PlanId>(
(searchParams.get("plan") as PlanId | null) ?? "solo",
);
const [billing, setBilling] = useState<Billing>(
(searchParams.get("billing") as Billing | null) ?? "annual",
);
const returnUrl = searchParams.get("returnUrl") ?? "/boards";
const { data: workspaces } = api.workspace.all.useQuery();
const { data: session } = authClient.useSession();
const { data: user } = api.user.getUser.useQuery(undefined, {
enabled: !!session?.user,
});
const userImage = user?.image ?? session?.user.image ?? null;
const hasExistingWorkspace = !!workspaces?.length;
const FREQUENCIES: { value: Billing; label: string }[] = [
{ value: "monthly", label: t`Monthly` },
{ value: "annual", label: t`Annual` },
];
const PLANS: {
id: PlanId;
name: string;
monthly: string;
annual: string;
description: string;
trial?: boolean;
}[] = [
{
id: "solo",
name: t`Solo`,
monthly: t`Free`,
annual: t`Free`,
description: t`Good for individuals starting out who just need the essentials.`,
},
{
id: "team",
name: t`Team`,
monthly: "$10/user/mo",
annual: "$8/user/mo",
description: t`Best for small teams who want to collaborate and move faster together.`,
trial: true,
},
{
id: "pro",
name: t`Pro`,
monthly: "$29/mo",
annual: "$23/mo",
description: t`Unlimited members and a custom workspace username for teams ready to scale.`,
trial: true,
},
];
const handleSelectPlan = (plan: PlanId) => {
setSelected(plan);
router.replace(`/onboarding/select-plan?plan=${plan}&billing=${billing}&returnUrl=${encodeURIComponent(returnUrl)}`);
};
const handleSetBilling = (b: Billing) => {
setBilling(b);
router.replace(`/onboarding/select-plan?plan=${selected}&billing=${b}&returnUrl=${encodeURIComponent(returnUrl)}`);
};
const handleContinue = () =>
router.push(`/onboarding/workspace?plan=${selected}&billing=${billing}&returnUrl=${encodeURIComponent(returnUrl)}`);
const handleCancel = () => router.push(returnUrl);
return (
<div className="flex min-h-screen flex-col items-center justify-center gap-4 bg-light-100 px-4 py-8 dark:bg-dark-50 md:px-6">
<div className="w-full max-w-3xl overflow-hidden rounded-xl border border-light-400 bg-light-200 shadow-xl dark:border-dark-400 dark:bg-dark-100">
<div className="flex flex-col md:h-[520px] md:flex-row">
{/* Left panel */}
<div className="flex flex-col p-6 md:w-[55%] md:p-8">
<div className="flex-1">
<h2 className="text-xl font-bold text-light-1000 dark:text-dark-1000">
{t`Choose a plan`}
</h2>
<p className="mt-1 text-sm text-light-800 dark:text-dark-800">
{t`Pick a plan to get started. All paid plans include a 14-day free trial.`}
</p>
{/* Billing toggle */}
<div className="mt-4 flex justify-end">
<RadioGroup
value={billing}
onChange={handleSetBilling}
className="grid grid-cols-2 gap-x-1 rounded-full p-1 text-center text-xs font-semibold ring-1 ring-inset ring-light-600 dark:ring-dark-600"
>
{FREQUENCIES.map((f) => (
<Radio
key={f.value}
value={f.value}
className={twMerge(
"cursor-pointer rounded-full px-2.5 py-0.5 text-xs transition-colors",
billing === f.value
? "bg-dark-50 text-white dark:bg-light-50 dark:text-dark-50"
: "text-light-900 hover:bg-light-200 dark:text-dark-900 dark:hover:bg-dark-200",
)}
>
{f.label}
</Radio>
))}
</RadioGroup>
</div>
<div className="mt-4 space-y-3">
{PLANS.map((plan) => {
const badge =
billing === "annual" ? plan.annual : plan.monthly;
return (
<button
key={plan.id}
type="button"
onClick={() => handleSelectPlan(plan.id)}
className={`relative w-full rounded-lg border px-4 py-3 text-left transition-colors ${
selected === plan.id
? "border-light-700 bg-light-300 dark:border-dark-600 dark:bg-dark-200"
: "border-light-500 bg-light-200 hover:border-light-600 dark:border-dark-500 dark:bg-dark-100 dark:hover:border-dark-600"
}`}
>
<div className="flex items-start justify-between">
<div className="flex-1 pr-8">
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-light-1000 dark:text-dark-1000">
{plan.name}
</span>
<span className="rounded-full bg-neutral-700 px-2 py-px text-[11px] font-medium text-neutral-200">
{badge}
</span>
{plan.trial && billing === "annual" && (
<span className="rounded-full bg-emerald-500/10 px-2 py-px text-[11px] font-medium text-emerald-600 ring-1 ring-inset ring-emerald-500/20 dark:text-emerald-400">
-20%
</span>
)}
</div>
<p className="mt-1 text-xs text-light-800 dark:text-dark-800">
{plan.description}
</p>
</div>
<div className="mt-0.5 flex-shrink-0">
<div
className={`flex h-4 w-4 items-center justify-center rounded-full border-2 ${
selected === plan.id
? "border-light-900 bg-light-900 dark:border-dark-900 dark:bg-dark-900"
: "border-light-700 dark:border-dark-700"
}`}
>
{selected === plan.id && (
<div className="h-1.5 w-1.5 rounded-full bg-light-100 dark:bg-dark-100" />
)}
</div>
</div>
</div>
</button>
);
})}
</div>
</div>
<div className="mt-6 flex justify-end gap-2 md:mt-8">
{hasExistingWorkspace && (
<Button variant="ghost" onClick={handleCancel}>
{t`Cancel`}
</Button>
)}
<Button onClick={handleContinue}>{t`Continue`}</Button>
</div>
</div>
{/* Right panel */}
<div className="hidden items-center justify-center bg-light-200 dark:bg-dark-200 md:flex md:w-[45%]">
<div className="relative h-72 w-72">
{/* Orbit rings */}
<div className="absolute inset-0 m-auto h-72 w-72 rounded-full border border-light-400 dark:border-dark-400" />
<div className="absolute inset-0 m-auto h-52 w-52 rounded-full border border-light-400 dark:border-dark-400" />
<div className="absolute inset-0 m-auto h-32 w-32 rounded-full border border-light-400 dark:border-dark-400" />
{/* Centre person */}
<div className="absolute inset-0 m-auto flex h-16 w-16 items-center justify-center overflow-hidden rounded-full bg-light-300 dark:bg-dark-300">
{userImage ? (
<img
src={userImage}
alt={t`Your avatar`}
className="h-full w-full object-cover"
/>
) : (
<HiUser className="h-7 w-7 text-light-700 dark:text-dark-700" />
)}
</div>
{/* Orbiting users — zero-size pivot at center rotates; icon offset by radius stays on the ring */}
<AnimatePresence>
{ORBIT_USERS[selected].map((u) => (
<motion.div
key={u.id}
style={{ position: "absolute", left: 144, top: 144 }}
initial={{ opacity: 0, rotate: u.angle }}
animate={{ opacity: 1, rotate: u.angle + 360 }}
exit={{ opacity: 0 }}
transition={{
opacity: { duration: 0.3 },
rotate: {
duration: u.duration,
repeat: Infinity,
ease: "linear",
repeatType: "loop",
},
}}
>
{/* Counter-rotate icon so it stays upright */}
<motion.div
style={{
position: "absolute",
left: u.radius - 12,
top: -12,
}}
initial={{ rotate: -u.angle }}
animate={{ rotate: -(u.angle + 360) }}
transition={{
duration: u.duration,
repeat: Infinity,
ease: "linear",
repeatType: "loop",
}}
className="flex h-6 w-6 items-center justify-center rounded-full bg-light-400 dark:bg-dark-400"
>
<HiUser className="h-3.5 w-3.5 text-light-800 dark:text-dark-800" />
</motion.div>
</motion.div>
))}
</AnimatePresence>
</div>
</div>
</div>
</div>
{!hasExistingWorkspace && (
<Button variant="ghost" onClick={() => authClient.signOut()}>
{t`Sign out`}
</Button>
)}
</div>
);
}

View File

@@ -1,367 +0,0 @@
import { useRouter, useSearchParams } from "next/navigation";
import { t } from "@lingui/core/macro";
import { motion } from "framer-motion";
import { useEffect, useState } from "react";
import {
HiArrowLeft,
HiArrowPath,
HiArrowRight,
HiCheck,
HiEllipsisVertical,
HiInformationCircle,
HiLockClosed,
} from "react-icons/hi2";
import { authClient } from "@kan/auth/client";
import Button from "~/components/Button";
import Input from "~/components/Input";
import LoadingSpinner from "~/components/LoadingSpinner";
import Toggle from "~/components/Toggle";
import { Tooltip } from "~/components/Tooltip";
import { useDebounce } from "~/hooks/useDebounce";
import { usePopup } from "~/providers/popup";
import { api } from "~/utils/api";
function slugify(value: string) {
return value
.toLowerCase()
.trim()
.replace(/[^a-z0-9\s-]/g, "")
.replace(/\s+/g, "-")
.replace(/-+/g, "-")
.slice(0, 60);
}
export default function WorkspaceNameView() {
const router = useRouter();
const searchParams = useSearchParams();
const plan = searchParams.get("plan") ?? "solo";
const billing = searchParams.get("billing") ?? "annual";
const returnUrl = searchParams.get("returnUrl") ?? "/boards";
const { showPopup } = usePopup();
const [isProToggle, setIsProToggle] = useState(plan === "pro");
const [name, setName] = useState("");
const [slug, setSlug] = useState("");
const [slugManuallyEdited, setSlugManuallyEdited] = useState(false);
const [description, setDescription] = useState("");
const handleNameChange = (value: string) => {
setName(value);
if (isProToggle && !slugManuallyEdited) {
setSlug(slugify(value));
}
};
const [debouncedSlug] = useDebounce(slug, 500);
const isTyping = slug !== debouncedSlug;
const slugAvailability = api.workspace.checkSlugAvailability.useQuery(
{ workspaceSlug: debouncedSlug },
{ enabled: isProToggle && debouncedSlug.length >= 3 && !isTyping },
);
const isSlugAvailable =
slugAvailability.data?.isAvailable && !slugAvailability.data?.isReserved;
const isSlugTaken =
slugAvailability.data?.isAvailable === false &&
!slugAvailability.data?.isReserved;
const isSlugReserved = slugAvailability.data?.isReserved === true;
const slugError = isSlugTaken
? t`This URL has already been taken`
: isSlugReserved
? t`This URL is reserved`
: undefined;
const { data: session } = authClient.useSession();
const { data: user } = api.user.getUser.useQuery(undefined, {
enabled: !!session?.user,
});
const { data: workspaces } = api.workspace.all.useQuery();
const hasExistingWorkspace = !!workspaces?.length;
const utils = api.useUtils();
const previewSlug = isProToggle
? slugify(slug) || slugify(name) || "your-workspace"
: "your-workspace";
const createWorkspace = api.workspace.create.useMutation({
onSuccess: async (workspace) => {
if (!workspace.publicId) return;
localStorage.setItem("workspacePublicId", workspace.publicId);
void utils.workspace.all.invalidate();
router.push("/boards");
},
onError: () => {
showPopup({
header: t`Unable to create workspace`,
message: t`Please try again later, or contact customer support.`,
icon: "error",
});
},
});
const [isRedirectingToCheckout, setIsRedirectingToCheckout] = useState(false);
const handleContinue = async () => {
if (!name.trim()) return;
if (plan === "solo") {
createWorkspace.mutate({
name: name.trim(),
...(description.trim() && { description: description.trim() }),
});
return;
}
// team/pro: redirect to Stripe — workspace created on checkout_success
setIsRedirectingToCheckout(true);
try {
const response = await fetch("/api/stripe/create_checkout_session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
workspaceName: name.trim(),
...(description.trim() && {
workspaceDescription: description.trim(),
}),
...(plan === "pro" && slug ? { workspaceSlug: slug } : {}),
cancelUrl: window.location.pathname + window.location.search,
successUrl: "/boards",
billing,
plan,
}),
});
const data = await response.json();
const url = (data as { url: string }).url;
if (url) {
window.location.href = url;
return;
}
} catch {
// fall through
}
setIsRedirectingToCheckout(false);
showPopup({
header: t`Unable to start checkout`,
message: t`Please try again later, or contact customer support.`,
icon: "error",
});
};
useEffect(() => {
document.getElementById("workspace-name-input")?.focus();
}, []);
const displayName = user?.name ?? session?.user.name ?? "";
const BOARDS = [t`Roadmap`, t`Engineering`, t`Marketing`];
const [visibleCount, setVisibleCount] = useState(1);
useEffect(() => {
const timers = BOARDS.slice(1).map((_, i) =>
setTimeout(() => setVisibleCount(i + 2), (i + 1) * 2000),
);
return () => timers.forEach(clearTimeout);
}, []);
return (
<div className="flex min-h-screen flex-col items-center justify-center gap-4 bg-light-100 px-4 py-8 dark:bg-dark-50 md:px-6">
<div className="w-full max-w-3xl overflow-hidden rounded-xl border border-light-400 bg-light-200 shadow-xl dark:border-dark-400 dark:bg-dark-100">
<div className="flex flex-col md:h-[520px] md:flex-row">
{/* Left panel */}
<div className="flex flex-col p-6 md:w-[55%] md:p-8">
<div className="flex-1">
<h2 className="text-xl font-bold text-light-1000 dark:text-dark-1000">
{t`Set up your workspace`}
</h2>
<p className="mt-1 text-sm text-light-800 dark:text-dark-800">
{t`You can always change these later in settings.`}
</p>
<div className="mt-6 space-y-3">
<Input
id="workspace-name-input"
placeholder={t`Workspace name`}
value={name}
onChange={(e) => handleNameChange(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleContinue()}
maxLength={64}
/>
<div className="space-y-2">
<Input
placeholder={t`your-workspace`}
value={isProToggle ? slug : t`your-workspace`}
onChange={(e) => {
setSlugManuallyEdited(true);
setSlug(
e.target.value
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, "")
.replace(/\s+/g, "-")
.replace(/-+/g, "-")
.slice(0, 60),
);
}}
disabled={!isProToggle}
prefix="kan.bn/"
className={
!isProToggle ? "cursor-not-allowed opacity-50" : ""
}
errorMessage={slugError}
iconRight={
!isProToggle ? (
<Tooltip
content={
<span className="text-xs">{t`Custom usernames require upgrading to a Pro plan`}</span>
}
placement="top"
delay={0}
>
<HiInformationCircle className="h-4 w-4 leading-[0] text-dark-700 dark:text-dark-700" />
</Tooltip>
) : isProToggle && slug.length >= 3 ? (
isTyping || slugAvailability.isPending ? (
<LoadingSpinner />
) : isSlugAvailable ? (
<HiCheck className="h-4 w-4 text-white" />
) : null
) : null
}
/>
</div>
{plan !== "pro" && (
<div className="pb-2">
<Toggle
isChecked={isProToggle}
onChange={() => setIsProToggle((v) => !v)}
label={t`Upgrade to Pro ($29/month)`}
labelPosition="after"
/>
</div>
)}
<div>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder={t`Workspace description`}
maxLength={280}
rows={3}
className="block w-full resize-none rounded-md border-0 bg-dark-300 bg-white/5 py-1.5 text-sm shadow-sm ring-1 ring-inset ring-light-600 placeholder:text-dark-800 focus:ring-2 focus:ring-inset focus:ring-light-700 dark:text-dark-1000 dark:ring-dark-700 dark:focus:ring-dark-700 sm:leading-6"
/>
<p className="mt-1 text-right text-[10px] text-light-700 dark:text-dark-700">
{description.length}/280
</p>
</div>
</div>
</div>
<div className="mt-6 flex flex-wrap items-center justify-between gap-3">
<div className="ml-auto flex gap-2">
<Button
variant="ghost"
onClick={() =>
router.replace(
`/onboarding/select-plan?plan=${plan}&billing=${billing}&returnUrl=${encodeURIComponent(returnUrl)}`,
)
}
>
{t`Back`}
</Button>
<Button
onClick={() => void handleContinue()}
disabled={
!name.trim() ||
createWorkspace.isPending ||
isRedirectingToCheckout ||
(isProToggle &&
slug.length >= 3 &&
(isTyping || slugAvailability.isPending || !!slugError))
}
isLoading={
createWorkspace.isPending || isRedirectingToCheckout
}
>
{t`Continue`}
</Button>
</div>
</div>
</div>
{/* Right panel — browser mockup */}
<div className="hidden flex-col bg-light-300 dark:bg-dark-200 md:flex md:w-[45%]">
{/* Browser chrome */}
<div className="flex items-center gap-2 border-b border-light-400 px-3 py-2.5 dark:border-dark-400">
<div className="flex items-center gap-1.5 text-light-700 dark:text-dark-700">
<HiArrowLeft className="h-3.5 w-3.5" />
<HiArrowRight className="h-3.5 w-3.5" />
<HiArrowPath className="h-3.5 w-3.5" />
</div>
<div className="flex flex-1 items-center gap-1.5 rounded-md bg-light-200 px-2.5 py-1 dark:bg-dark-300">
<HiLockClosed className="h-3 w-3 flex-shrink-0 text-light-700 dark:text-dark-700" />
<span className="truncate text-xs text-light-900 dark:text-dark-900">
kan.bn/
<span className="text-light-1000 dark:text-dark-1000">
{previewSlug}
</span>
</span>
</div>
<HiEllipsisVertical className="h-4 w-4 flex-shrink-0 text-light-700 dark:text-dark-700" />
</div>
{/* Browser content */}
<div className="flex flex-1 flex-col items-center overflow-hidden px-14 py-8">
{/* Workspace name + description */}
<p className="text-center text-xs font-bold text-light-1000 dark:text-dark-1000">
{name || t`Your workspace`}
</p>
<p className="mt-0.5 line-clamp-2 break-all text-center text-[10px] text-light-800 dark:text-dark-800">
{description || t`Your workspace description`}
</p>
{/* Boards area */}
<div className="mt-3 flex w-full flex-1 flex-col rounded-lg bg-light-200 p-2 dark:bg-dark-300">
<div className="flex flex-1 flex-col gap-1.5">
{BOARDS.map((board, i) => (
<motion.div
key={board}
initial={{ opacity: 0, y: -12 }}
animate={
i < visibleCount
? { opacity: 1, y: 0 }
: { opacity: 0, y: -12 }
}
transition={{
opacity: { duration: 0.2 },
y: { duration: 0.25, ease: "easeOut" },
}}
className="flex w-full flex-1 items-center justify-center rounded border border-dashed border-light-400 bg-light-50 px-3 text-[10px] font-medium text-light-900 dark:border-dark-400 dark:bg-dark-100 dark:text-dark-900"
>
{board}
</motion.div>
))}
</div>
</div>
{/* Footer */}
<p className="mt-2 text-[10px] font-semibold text-light-900 dark:text-dark-900">
kan.bn
</p>
</div>
</div>
</div>
</div>
{!hasExistingWorkspace && (
<Button variant="ghost" onClick={() => authClient.signOut()}>
{t`Sign out`}
</Button>
)}
</div>
);
}

View File

@@ -3,7 +3,6 @@ import { env } from "next-runtime-env";
import Button from "~/components/Button";
import FeedbackModal from "~/components/FeedbackModal";
import { FontSizeSelector } from "~/components/FontSizeSelector";
import { LanguageSelector } from "~/components/LanguageSelector";
import Modal from "~/components/modal";
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
@@ -55,16 +54,6 @@ export default function AccountSettings() {
<LanguageSelector />
</div>
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
{t`Font size`}
</h2>
<p className="mb-8 text-sm text-neutral-500 dark:text-dark-900">
{t`Change the application font size.`}
</p>
<FontSizeSelector />
</div>
<div className="mb-8 border-t border-light-300 dark:border-dark-300">
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
{t`Delete account`}

View File

@@ -110,17 +110,18 @@ export default function Avatar({
const cropYpx = (crop.y / 100) * image.naturalHeight;
const cropWpx = (crop.width / 100) * image.naturalWidth;
const cropHpx = (crop.height / 100) * image.naturalHeight;
// Cap output at 512x512 — avatars display at 64x64, so higher res is wasteful
// and causes "File too large" errors on HiDPI screens
const maxSize = 512;
const scale = Math.min(maxSize / cropWpx, maxSize / cropHpx, 1);
canvas.width = Math.max(1, Math.floor(cropWpx * scale));
canvas.height = Math.max(1, Math.floor(cropHpx * scale));
canvas.width = Math.max(1, Math.floor(cropWpx));
canvas.height = Math.max(1, Math.floor(cropHpx));
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Canvas not supported");
// For better quality on HiDPI screens
const pixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.width * pixelRatio;
canvas.height = canvas.height * pixelRatio;
ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
ctx.imageSmoothingQuality = "high";
ctx.drawImage(
image,
cropXpx,
@@ -129,8 +130,8 @@ export default function Avatar({
cropHpx,
0,
0,
canvas.width,
canvas.height,
canvas.width / pixelRatio,
canvas.height / pixelRatio,
);
const mime = selectedFile?.type ?? "image/jpeg";
@@ -138,7 +139,6 @@ export default function Avatar({
canvas.toBlob(
(b) => (b ? resolve(b) : reject(new Error("toBlob failed"))),
mime,
0.85,
);
});
return blob;

View File

@@ -39,8 +39,6 @@ services:
# Logging (optional - debug, info, warn, error; defaults to debug in dev, info in prod)
- LOG_LEVEL=${LOG_LEVEL}
- AXIOM_TOKEN=${AXIOM_TOKEN}
- AXIOM_DATASET=${AXIOM_DATASET}
# Stripe
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}

View File

@@ -28,10 +28,6 @@
"types": "./dist/utils/rateLimit.d.ts",
"default": "./src/utils/rateLimit.ts"
},
"./utils/apiLogging": {
"types": "./dist/utils/apiLogging.d.ts",
"default": "./src/utils/apiLogging.ts"
},
"./utils/permissions": {
"types": "./dist/utils/permissions.d.ts",
"default": "./src/utils/permissions.ts"

View File

@@ -227,7 +227,7 @@ export const boardRouter = createTRPCRouter({
boardSlug: z
.string()
.min(3)
.max(60)
.max(24)
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
members: z.array(z.string().min(12)).optional(),
labels: z.array(z.string().min(12)).optional(),
@@ -657,7 +657,7 @@ export const boardRouter = createTRPCRouter({
boardSlug: z
.string()
.min(3)
.max(60)
.max(24)
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
boardPublicId: z.string().min(12),
}),

View File

@@ -4,7 +4,6 @@ import { z } from "zod";
import * as cardRepo from "@kan/db/repository/card.repo";
import * as cardActivityRepo from "@kan/db/repository/cardActivity.repo";
import * as cardCommentRepo from "@kan/db/repository/cardComment.repo";
import * as checklistRepo from "@kan/db/repository/checklist.repo";
import * as labelRepo from "@kan/db/repository/label.repo";
import * as listRepo from "@kan/db/repository/list.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
@@ -1185,176 +1184,4 @@ export const cardRouter = createTRPCRouter({
return { success: true };
}),
duplicate: protectedProcedure
.meta({
openapi: {
summary: "Duplicate a card",
method: "POST",
path: "/cards/{cardPublicId}/duplicate",
description: "Duplicates a card to a target list with optional options",
tags: ["Cards"],
protect: true,
},
})
.input(
z.object({
cardPublicId: z.string().min(12),
listPublicId: z.string().min(12),
index: z.number().int().min(0).optional(),
title: z.string().min(1).max(2000).optional(),
copyLabels: z.boolean(),
copyMembers: z.boolean(),
copyChecklists: z.boolean(),
}),
)
.output(
z.object({
publicId: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
const userId = ctx.user?.id;
if (!userId)
throw new TRPCError({
message: `User not authenticated`,
code: "UNAUTHORIZED",
});
const sourceCardMeta = await cardRepo.getWorkspaceAndCardIdByCardPublicId(
ctx.db,
input.cardPublicId,
);
if (!sourceCardMeta)
throw new TRPCError({
message: `Card with public ID ${input.cardPublicId} not found`,
code: "NOT_FOUND",
});
await assertPermission(
ctx.db,
userId,
sourceCardMeta.workspaceId,
"card:create",
);
const targetList = await listRepo.getWorkspaceAndListIdByListPublicId(
ctx.db,
input.listPublicId,
);
if (!targetList)
throw new TRPCError({
message: `List with public ID ${input.listPublicId} not found`,
code: "NOT_FOUND",
});
if (targetList.workspaceId !== sourceCardMeta.workspaceId)
throw new TRPCError({
message: `Target list must be in the same workspace`,
code: "BAD_REQUEST",
});
const sourceCard = await cardRepo.getWithListAndMembersByPublicId(
ctx.db,
input.cardPublicId,
);
if (!sourceCard)
throw new TRPCError({
message: `Card with public ID ${input.cardPublicId} not found`,
code: "NOT_FOUND",
});
const newCard = await cardRepo.create(ctx.db, {
title: input.title ?? sourceCard.title,
description: sourceCard.description ?? "",
createdBy: userId,
listId: targetList.id,
position: "end",
dueDate: sourceCard.dueDate ?? null,
});
if (input.index !== undefined && input.index >= 0) {
await cardRepo.reorder(ctx.db, {
cardId: newCard.id,
newIndex: input.index,
newListId: targetList.id,
});
}
if (input.copyLabels && sourceCard.labels?.length) {
const labelPublicIds = sourceCard.labels.map((l) => l.publicId);
const labels = await labelRepo.getAllByPublicIds(ctx.db, labelPublicIds);
if (labels.length) {
const labelsInsert = labels.map((label) => ({
cardId: newCard.id,
labelId: label.id,
}));
await cardRepo.bulkCreateCardLabelRelationships(ctx.db, labelsInsert);
const cardActivitesInsert = labels.map((cardLabel) => ({
type: "card.updated.label.added" as const,
cardId: newCard.id,
labelId: cardLabel.id,
createdBy: userId,
}));
await cardActivityRepo.bulkCreate(ctx.db, cardActivitesInsert);
}
}
if (input.copyMembers && sourceCard.members?.length) {
const memberPublicIds = sourceCard.members.map((m) => m.publicId);
const members = await workspaceRepo.getAllMembersByPublicIds(
ctx.db,
memberPublicIds,
);
if (members.length) {
const membersInsert = members.map((member) => ({
cardId: newCard.id,
workspaceMemberId: member.id,
}));
await cardRepo.bulkCreateCardWorkspaceMemberRelationships(
ctx.db,
membersInsert,
);
const cardActivitesInsert = members.map((member) => ({
type: "card.updated.member.added" as const,
cardId: newCard.id,
workspaceMemberId: member.id,
createdBy: userId,
}));
await cardActivityRepo.bulkCreate(ctx.db, cardActivitesInsert);
}
}
if (input.copyChecklists && sourceCard.checklists?.length) {
for (const checklist of sourceCard.checklists) {
const newChecklist = await checklistRepo.create(ctx.db, {
cardId: newCard.id,
name: checklist.name,
createdBy: userId,
});
if (!newChecklist?.id) continue;
if (checklist.items?.length) {
for (const item of checklist.items) {
await checklistRepo.createItem(ctx.db, {
checklistId: newChecklist.id,
title: item.title,
createdBy: userId,
completed: item.completed ?? false,
});
}
}
await cardActivityRepo.create(ctx.db, {
type: "card.updated.checklist.added",
cardId: newCard.id,
toTitle: newChecklist.name,
createdBy: userId,
});
}
}
return { publicId: newCard.publicId };
}),
});

View File

@@ -4,7 +4,6 @@ import { z } from "zod";
import * as cardRepo from "@kan/db/repository/card.repo";
import * as cardActivityRepo from "@kan/db/repository/cardActivity.repo";
import * as checklistRepo from "@kan/db/repository/checklist.repo";
import { stripHtml } from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { assertPermission } from "../utils/permissions";
@@ -224,7 +223,7 @@ export const checklistRouter = createTRPCRouter({
.input(
z.object({
checklistPublicId: z.string().length(12),
title: z.string().min(1).max(500).transform(stripHtml),
title: z.string().min(1).max(500),
}),
)
.output(checklistItemSchema)
@@ -289,7 +288,7 @@ export const checklistRouter = createTRPCRouter({
.input(
z.object({
checklistItemPublicId: z.string().length(12),
title: z.string().min(1).max(500).transform(stripHtml).optional(),
title: z.string().min(1).max(500).optional(),
completed: z.boolean().optional(),
index: z.number().int().min(0).optional(),
}),
@@ -323,29 +322,30 @@ export const checklistRouter = createTRPCRouter({
const previousTitle = item.title;
let updatedItem;
let updatedItem;
if (input.title !== undefined || input.completed !== undefined) {
updatedItem = await checklistRepo.updateItemById(ctx.db, {
id: item.id,
title: input.title,
completed: input.completed,
});
}
if (input.title !== undefined || input.completed !== undefined) {
updatedItem = await checklistRepo.updateItemById(ctx.db, {
id: item.id,
title: input.title,
completed: input.completed,
});
}
if (input.index !== undefined) {
updatedItem = await checklistRepo.reorderItem(ctx.db, {
itemId: item.id,
newIndex: input.index,
});
}
if (input.index !== undefined) {
updatedItem = await checklistRepo.reorderItem(ctx.db, {
itemId: item.id,
newIndex: input.index,
});
}
if (!updatedItem) {
throw new TRPCError({
message: `Failed to update checklist item`,
code: "INTERNAL_SERVER_ERROR",
});
}
if (!updatedItem) {
throw new TRPCError({
message: `Failed to update checklist item`,
code: "INTERNAL_SERVER_ERROR",
});
}
// Log completion toggle
if (input.completed !== undefined) {
@@ -371,6 +371,7 @@ export const checklistRouter = createTRPCRouter({
}
return updatedItem;
}),
deleteItem: protectedProcedure
.meta({

View File

@@ -19,7 +19,6 @@ import { encryptToken } from "../utils/encryption";
export const integrationRouter = createTRPCRouter({
saveGitHubToken: protectedProcedure
.input(z.object({ token: z.string() }))
.output(z.object({ success: z.boolean() }))
.mutation(async ({ ctx, input }) => {
const user = ctx.user;
@@ -44,9 +43,7 @@ export const integrationRouter = createTRPCRouter({
return { success: true };
}),
disconnectGitHub: protectedProcedure
.output(z.object({ success: z.boolean() }))
.mutation(async ({ ctx }) => {
disconnectGitHub: protectedProcedure.mutation(async ({ ctx }) => {
const user = ctx.user;
if (!user)
@@ -59,9 +56,7 @@ export const integrationRouter = createTRPCRouter({
return { success: true };
}),
getGitHubStatus: protectedProcedure
.output(z.object({ connected: z.boolean() }))
.query(async ({ ctx }) => {
getGitHubStatus: protectedProcedure.query(async ({ ctx }) => {
const user = ctx.user;
if (!user)

View File

@@ -31,6 +31,7 @@ export const userRouter = createTRPCRouter({
.object({
id: z.number(),
prefix: z.string().nullable(),
key: z.string(),
})
.nullable(),
}),
@@ -61,7 +62,7 @@ export const userRouter = createTRPCRouter({
return {
...result,
image: imageUrl,
apiKey: apiKey ? { id: apiKey.id, prefix: apiKey.prefix } : null,
apiKey: apiKey ?? null,
};
}),
update: protectedProcedure

View File

@@ -202,7 +202,6 @@ export const workspaceRouter = createTRPCRouter({
.input(
z.object({
name: z.string().min(1).max(64),
description: z.string().max(280).optional(),
slug: z
.string()
.min(3)
@@ -261,7 +260,6 @@ export const workspaceRouter = createTRPCRouter({
slug: workspaceSlug,
createdBy: userId,
createdByEmail: userEmail,
...(input.description && { description: input.description }),
});
if (!result.publicId)

View File

@@ -1,4 +1,3 @@
import { randomUUID } from "crypto";
import type { CreateNextContextOptions } from "@trpc/server/adapters/next";
import type { NextApiRequest } from "next";
import type { OpenApiMeta } from "trpc-to-openapi";
@@ -12,28 +11,7 @@ import { initAuth } from "@kan/auth/server";
import { createDrizzleClient } from "@kan/db/client";
import { createLogger } from "@kan/logger";
const log = createLogger("api");
const TRPC_STATUS_MAP: Partial<Record<TRPCError["code"], number>> = {
PARSE_ERROR: 400,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
METHOD_NOT_SUPPORTED: 405,
TIMEOUT: 408,
CONFLICT: 409,
PRECONDITION_FAILED: 412,
PAYLOAD_TOO_LARGE: 413,
UNPROCESSABLE_CONTENT: 422,
TOO_MANY_REQUESTS: 429,
CLIENT_CLOSED_REQUEST: 499,
INTERNAL_SERVER_ERROR: 500,
NOT_IMPLEMENTED: 501,
BAD_GATEWAY: 502,
SERVICE_UNAVAILABLE: 503,
GATEWAY_TIMEOUT: 504,
};
const log = createLogger("trpc");
export interface User {
id: string;
@@ -72,7 +50,6 @@ interface CreateContextOptions {
db: dbClient;
auth: ReturnType<typeof createAuthWithHeaders>;
headers: Headers;
transport?: "trpc" | "rest";
}
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
@@ -81,8 +58,6 @@ export const createInnerTRPCContext = (opts: CreateContextOptions) => {
db: opts.db,
auth: opts.auth,
headers: opts.headers,
transport: opts.transport ?? "trpc",
requestId: randomUUID(),
};
};
@@ -94,13 +69,7 @@ export const createTRPCContext = async ({ req }: CreateNextContextOptions) => {
const session = await auth.api.getSession();
return createInnerTRPCContext({
db,
user: session?.user,
auth,
headers,
transport: "trpc",
});
return createInnerTRPCContext({ db, user: session?.user, auth, headers });
};
export const createNextApiContext = async (req: NextApiRequest) => {
@@ -111,13 +80,7 @@ export const createNextApiContext = async (req: NextApiRequest) => {
const session = await auth.api.getSession();
return createInnerTRPCContext({
db,
user: session?.user,
auth,
headers,
transport: "trpc",
});
return createInnerTRPCContext({ db, user: session?.user, auth, headers });
};
export const createRESTContext = async ({ req }: CreateNextContextOptions) => {
@@ -130,16 +93,11 @@ export const createRESTContext = async ({ req }: CreateNextContextOptions) => {
try {
session = await auth.api.getSession();
} catch (error) {
log.warn({ err: error }, "Failed to get session, treating as unauthenticated");
log.error({ err: error }, "Error getting session");
throw error;
}
return createInnerTRPCContext({
db,
user: session?.user,
auth,
headers,
transport: "rest",
});
return createInnerTRPCContext({ db, user: session?.user, auth, headers });
};
const t = initTRPC
@@ -163,45 +121,25 @@ export const createTRPCRouter = t.router;
export const createCallerFactory = t.createCallerFactory;
const loggingMiddleware = t.middleware(async ({ path, type, next, ctx, getRawInput }) => {
const loggingMiddleware = t.middleware(async ({ path, type, next, ctx }) => {
const start = Date.now();
const [result, input] = await Promise.all([next(), getRawInput().catch(() => undefined)]);
const result = await next();
const duration = Date.now() - start;
const { user, transport, requestId } = ctx as {
user?: { id: string; email: string };
transport?: string;
requestId?: string;
};
const isCloud = process.env.NEXT_PUBLIC_KAN_ENV === "cloud";
const meta = {
requestId,
procedure: path,
type,
transport,
duration,
userId: user?.id,
...(isCloud && { email: user?.email }),
input,
};
const label = transport === "rest" ? "REST" : "tRPC";
const meta = { procedure: path, type, duration, userId: (ctx as { user?: { id: string } }).user?.id };
if (result.ok) {
log.info({ ...meta, status: 200 }, `${label} OK`);
log.info(meta, "tRPC OK");
} else {
const status = TRPC_STATUS_MAP[result.error.code] ?? 500;
const errorCode = result.error.code;
log.error(
{ ...meta, status, errorCode, err: result.error },
`${label} error`,
);
log.error({ ...meta, err: result.error }, "tRPC error");
}
return result;
});
export const publicProcedure = t.procedure.use(loggingMiddleware);
export const publicProcedure = t.procedure.use(loggingMiddleware).meta({
openapi: { method: "GET", path: "/public" },
});
const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
if (!ctx.user) {
@@ -225,7 +163,13 @@ const enforceUserIsAdmin = t.middleware(async ({ ctx, next }) => {
export const protectedProcedure = t.procedure
.use(loggingMiddleware)
.use(enforceUserIsAuthed);
.use(enforceUserIsAuthed)
.meta({
openapi: {
method: "GET",
path: "/protected",
},
});
export const adminProtectedProcedure = t.procedure
.use(loggingMiddleware)

View File

@@ -1,64 +0,0 @@
import { randomUUID } from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";
import { createLogger } from "@kan/logger";
import { createNextApiContext } from "../trpc";
const log = createLogger("api");
const isCloud = process.env.NEXT_PUBLIC_KAN_ENV === "cloud";
export function withApiLogging(
handler: (
req: NextApiRequest,
res: NextApiResponse,
) => Promise<unknown> | unknown,
) {
return async (req: NextApiRequest, res: NextApiResponse) => {
const start = Date.now();
const requestId = randomUUID();
const route = req.url?.split("?")[0] ?? "unknown";
const input = {
...(req.query && Object.keys(req.query).length > 0 && { query: req.query }),
...(req.body && typeof req.body === "object" && Object.keys(req.body).length > 0 && { body: req.body }),
};
let statusCode = 200;
const originalStatus = res.status.bind(res);
res.status = (code: number) => {
statusCode = code;
return originalStatus(code);
};
let userId: string | undefined;
let email: string | undefined;
try {
const ctx = await createNextApiContext(req);
userId = ctx.user?.id;
email = ctx.user?.email ?? undefined;
} catch {
// unauthenticated or auth unavailable
}
await handler(req, res);
const duration = Date.now() - start;
const meta = {
requestId,
procedure: route,
transport: "rest",
duration,
userId,
...(isCloud && email && { email }),
...(Object.keys(input).length > 0 && { input }),
status: statusCode,
};
if (statusCode < 400) {
log.info(meta, "API OK");
} else {
log.error(meta, "API error");
}
};
}

View File

@@ -165,13 +165,6 @@ export function createPlugins(db: dbClient) {
: []),
apiKey({
enableSessionForAPIKeys: true,
customAPIKeyGetter: (ctx) => {
const authorization = ctx.headers?.get("authorization");
if (authorization?.startsWith("Bearer ")) {
return authorization.slice(7);
}
return ctx.headers?.get("x-api-key") ?? undefined;
},
rateLimit: {
enabled: true,
timeWindow: 1000 * 60, // 1 minute

View File

@@ -1 +0,0 @@
ALTER TYPE "public"."workspace_plan" ADD VALUE 'team' BEFORE 'pro';

File diff suppressed because it is too large Load Diff

View File

@@ -218,13 +218,6 @@
"when": 1773212242728,
"tag": "20260311065722_AddWeekStartDay",
"breakpoints": true
},
{
"idx": 31,
"version": "7",
"when": 1775170588247,
"tag": "20260402225628_AddTeamWorkspacePlan",
"breakpoints": true
}
]
}

View File

@@ -64,8 +64,6 @@ export const create = async (
slug: string;
createdBy: string;
createdByEmail: string;
description?: string;
plan?: "free" | "team" | "pro" | "enterprise";
},
) => {
const [workspace] = await db
@@ -75,8 +73,6 @@ export const create = async (
name: workspaceInput.name,
slug: workspaceInput.slug,
createdBy: workspaceInput.createdBy,
...(workspaceInput.description && { description: workspaceInput.description }),
...(workspaceInput.plan && { plan: workspaceInput.plan }),
})
.returning({
id: workspaces.id,
@@ -128,7 +124,7 @@ export const update = async (
workspaceInput: {
name?: string;
slug?: string;
plan?: "free" | "team" | "pro" | "enterprise";
plan?: "free" | "pro" | "enterprise";
description?: string;
showEmailsToMembers?: boolean;
weekStartDay?: number;

View File

@@ -34,7 +34,7 @@ export const slugTypes = ["reserved", "premium"] as const;
export type SlugType = (typeof slugTypes)[number];
export const slugTypeEnum = pgEnum("slug_type", slugTypes);
export const workspacePlans = ["free", "team", "pro", "enterprise"] as const;
export const workspacePlans = ["free", "pro", "enterprise"] as const;
export type WorkspacePlan = (typeof workspacePlans)[number];
export const workspacePlanEnum = pgEnum("workspace_plan", workspacePlans);

View File

@@ -19,7 +19,6 @@
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
"@axiomhq/js": "^1.4.0",
"pino": "^9.14.0",
"pino-pretty": "^13.1.3"
},

View File

@@ -1,43 +1,20 @@
import { Axiom } from "@axiomhq/js";
import pino from "pino";
const isDev = process.env.NODE_ENV !== "production";
const isCloud = process.env.NEXT_PUBLIC_KAN_ENV === "cloud";
const level = process.env.LOG_LEVEL || (isDev ? "debug" : "info");
const level = process.env.LOG_LEVEL ?? (isDev ? "debug" : "info");
const axiomToken = process.env.AXIOM_TOKEN;
const axiomDataset = process.env.AXIOM_DATASET;
const useAxiom = isCloud && !!axiomToken && !!axiomDataset;
function createAxiomStream(token: string, dataset: string): pino.DestinationStream {
const client = new Axiom({ token });
return {
write(msg: string) {
try {
client.ingest(dataset, [JSON.parse(msg) as Record<string, unknown>]);
} catch {
// ignore malformed log lines
}
export const logger = pino({
level,
...(isDev && {
transport: {
target: "pino-pretty",
options: {
colorize: true,
ignore: "pid,hostname",
translateTime: "HH:MM:ss",
},
},
};
}
export const logger = useAxiom
? pino(
{ level },
pino.multistream([
{ stream: process.stdout, level },
{ stream: createAxiomStream(axiomToken, axiomDataset), level },
]),
)
: pino({
level,
...(isDev && {
transport: {
target: "pino-pretty",
options: { colorize: true, ignore: "pid,hostname", translateTime: "HH:MM:ss" },
},
}),
});
}),
});
export const createLogger = (module: string) => logger.child({ module });

View File

@@ -5,4 +5,3 @@ export * from "./email";
export * from "./dueDateFilters";
export * from "./s3";
export * from "./mentions";
export * from "./sanitize";

View File

@@ -1,6 +0,0 @@
/**
* Strips all HTML tags from a string, returning plain text.
* Use this on any user-supplied text field before storing or displaying.
*/
export const stripHtml = (value: string): string =>
value.replace(/<[^>]*>/g, "").trim();

16
pnpm-lock.yaml generated
View File

@@ -504,9 +504,6 @@ importers:
packages/logger:
dependencies:
'@axiomhq/js':
specifier: ^1.4.0
version: 1.4.0
pino:
specifier: ^9.14.0
version: 9.14.0
@@ -894,10 +891,6 @@ packages:
resolution: {integrity: sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==}
engines: {node: '>=18.0.0'}
'@axiomhq/js@1.4.0':
resolution: {integrity: sha512-wC5x1ud/QJMstrjpicATkyY8+ZVWEl4WlXMtA5EZf7hkj0+b191yv4yynLxLEfr/MveXora9m6CWdJq4DsbcAg==}
engines: {node: '>=20'}
'@babel/code-frame@7.27.1':
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
@@ -5635,9 +5628,6 @@ packages:
picomatch:
optional: true
fetch-retry@6.0.0:
resolution: {integrity: sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==}
fflate@0.4.8:
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
@@ -9394,10 +9384,6 @@ snapshots:
'@smithy/types': 4.3.2
tslib: 2.8.1
'@axiomhq/js@1.4.0':
dependencies:
fetch-retry: 6.0.0
'@babel/code-frame@7.27.1':
dependencies:
'@babel/helper-validator-identifier': 7.27.1
@@ -14417,8 +14403,6 @@ snapshots:
optionalDependencies:
picomatch: 4.0.3
fetch-retry@6.0.0: {}
fflate@0.4.8: {}
figures@3.2.0:

View File

@@ -130,9 +130,7 @@
"NOVU_API_KEY",
"EMAIL_UNSUBSCRIBE_SECRET",
"REDIS_URL",
"LOG_LEVEL",
"AXIOM_TOKEN",
"AXIOM_DATASET"
"LOG_LEVEL"
],
"globalPassThroughEnv": [
"NODE_ENV",