Compare commits

..

2 Commits

Author SHA1 Message Date
Henry
61d26e4ed8 fix: ensure query only runs when isEdit is true 2026-03-23 21:39:53 +00:00
Henry
878ffd15fc fix: prevent invalid calls to fetch label by publicId 2026-03-23 17:29:01 +00:00
60 changed files with 566 additions and 8194 deletions

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,9 @@ checksums:
VhMDMg/origin/2/0: 0627e0040ca4939c9a57349e98c51797
VhMDMg/origin/3/0: 0627e0040ca4939c9a57349e98c51797
VhMDMg/translation: a552fc5c4189ebc3e2e6018edda7d18f
fzGyWs/translation: 25dbbca7a83f6af119179e7a56c953d4
fzGyWs/message: 25dbbca7a83f6af119179e7a56c953d4
fzGyWs/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
fzGyWs/translation: 25dbbca7a83f6af119179e7a56c953d4
yD3ZSw/message: 293d49fc3c75e9c425b64bd7126e6b46
yD3ZSw/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
yD3ZSw/translation: 293d49fc3c75e9c425b64bd7126e6b46
@@ -707,18 +699,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 +838,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 +919,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 +946,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 +992,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 +1170,9 @@ checksums:
l31EoQ/message: b4c05c9174637f4da2a41ead0d65bc01
l31EoQ/origin/0/0: 4980d8e27a628e7cd9a70b839d844083
l31EoQ/translation: b4c05c9174637f4da2a41ead0d65bc01
q3il6U/translation: 79a4fffd70bfbedb6174ea4c767f4ab9
q3il6U/message: 79a4fffd70bfbedb6174ea4c767f4ab9
q3il6U/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
q3il6U/translation: 79a4fffd70bfbedb6174ea4c767f4ab9
"%2B3TYXa/message": 9fa1c942feb40d6350322b71561df789
"%2B3TYXa/origin/0/0": 4980d8e27a628e7cd9a70b839d844083
"%2B3TYXa/translation": 9fa1c942feb40d6350322b71561df789
@@ -1463,10 +1426,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 +1439,9 @@ checksums:
vXIe7J/message: 277fd1a41cc237a437cd1d5e4a80463b
vXIe7J/origin/0/0: 627b72cd19a7154ae1c28cbd7ca0c730
vXIe7J/translation: 277fd1a41cc237a437cd1d5e4a80463b
k7rCa%2F/translation: 890b7851af05f97ac53644e4154c13b4
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 +1473,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 +1516,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 +1523,9 @@ checksums:
jl3aEJ/placeholders/0/0: 06f23520ebabfffed4999b5bcf61c9cd
jl3aEJ/origin/0/0: 1d6b5f88cdad160a67842f6b65387448
jl3aEJ/translation: 4c38799ff25321ea25017bf4cd8e2e4f
agPptk/translation: 43983f2178fb28aacaef6c5f41282c4a
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
@@ -1603,12 +1559,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 +1873,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
@@ -2113,9 +2057,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 +2086,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 +2151,9 @@ checksums:
zEcnBA/message: 513c3b4d9f0fda60fe079d42dcbd3882
zEcnBA/origin/0/0: c0b59968b2b234167c488a05cc38710b
zEcnBA/translation: 513c3b4d9f0fda60fe079d42dcbd3882
4Revpc/translation: 3adb3ae7891c5736776df840cff2360b
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
@@ -2311,9 +2250,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 +2322,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 +2364,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 +2399,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

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

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

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

@@ -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": {},
@@ -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]
],
@@ -1677,40 +1661,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": {},
@@ -1807,7 +1757,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 +1766,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"
},
@@ -2035,10 +1985,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": {
@@ -2224,9 +2171,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 +2226,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": {},
@@ -2422,15 +2338,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": {},
@@ -2987,7 +2894,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": {
@@ -3407,7 +3314,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 +3386,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]
@@ -3591,7 +3497,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 +3507,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 +3596,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": {},
@@ -3803,20 +3695,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 +3806,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 +3932,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": {
@@ -4453,8 +4331,8 @@
["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],
@@ -4509,24 +4387,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": {},
@@ -4948,15 +4814,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 +4884,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": {
@@ -5331,8 +5184,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 +5279,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 +5461,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 +5563,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]
@@ -5808,20 +5644,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 +5702,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"
@@ -5925,7 +5747,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"
@@ -6434,7 +6256,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],
@@ -6645,8 +6467,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": {},
@@ -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
@@ -2814,54 +2782,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": {},
@@ -3025,7 +2945,7 @@
],
[
"src/views/card/components/LabelSelector.tsx",
101
100
]
],
"translation": "Create new label"
@@ -3037,11 +2957,11 @@
"origin": [
[
"src/views/board/index.tsx",
91
76
],
[
"src/views/board/index.tsx",
670
554
]
],
"translation": "Create new list"
@@ -3419,10 +3339,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextMenu.tsx",
74
],
[
"src/views/card/components/Dropdown.tsx",
76
@@ -3747,18 +3663,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 +3770,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": {},
@@ -4090,18 +3954,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": {},
@@ -5029,7 +4881,7 @@
"origin": [
[
"src/views/board/index.tsx",
655
539
]
],
"translation": "Get started by creating a new list"
@@ -5729,7 +5581,7 @@
"origin": [
[
"src/views/card/components/MemberSelector.tsx",
115
114
],
[
"src/views/members/components/InviteMemberForm.tsx",
@@ -5847,10 +5699,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextLabelsModal.tsx",
31
],
[
"src/views/board/components/Filters.tsx",
155
@@ -6039,10 +5887,6 @@
"src/views/board/components/UpdateBoardSlugButton.tsx",
80
],
[
"src/views/board/index.tsx",
305
],
[
"src/views/card/components/Dropdown.tsx",
42
@@ -6063,10 +5907,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/components/CardContextDuplicateModal.tsx",
117
],
[
"src/views/card/index.tsx",
124
@@ -6218,22 +6058,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": {},
@@ -6406,30 +6230,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 +6428,7 @@
],
[
"src/views/board/index.tsx",
619
503
]
],
"translation": "New list"
@@ -6836,7 +6636,7 @@
"origin": [
[
"src/views/board/index.tsx",
651
535
]
],
"translation": "No lists"
@@ -6848,7 +6648,7 @@
"origin": [
[
"src/views/board/index.tsx",
656
540
]
],
"translation": "No lists have been created yet"
@@ -7512,11 +7312,11 @@
],
[
"src/views/board/index.tsx",
217
194
],
[
"src/views/board/index.tsx",
273
250
],
[
"src/views/boards/components/ImportBoardsForm.tsx",
@@ -7686,18 +7486,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 +7501,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": {},
@@ -8444,18 +8220,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 +8337,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
@@ -9075,11 +8831,11 @@
"origin": [
[
"src/views/board/index.tsx",
529
413
],
[
"src/views/board/index.tsx",
565
449
]
],
"translation": "Template"
@@ -9236,18 +8992,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 +9280,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 +9449,6 @@
"placeholders": {},
"comments": [],
"origin": [
[
"src/views/board/index.tsx",
312
],
[
"src/views/card/components/Dropdown.tsx",
49
@@ -9860,30 +9588,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 +9679,7 @@
"origin": [
[
"src/views/board/index.tsx",
272
249
],
[
"src/views/card/index.tsx",
@@ -10051,7 +9755,7 @@
"origin": [
[
"src/views/board/index.tsx",
216
193
],
[
"src/views/card/components/ListSelector.tsx",
@@ -10935,7 +10639,7 @@
],
[
"src/views/board/index.tsx",
529
413
],
[
"src/views/boards/index.tsx",
@@ -11283,11 +10987,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": {},
@@ -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]
],
@@ -1677,40 +1661,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": {},
@@ -1807,7 +1757,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 +1766,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"
},
@@ -2035,10 +1985,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": {
@@ -2224,9 +2171,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 +2226,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": {},
@@ -2422,15 +2338,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": {},
@@ -2987,7 +2894,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": {
@@ -3407,7 +3314,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 +3386,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]
@@ -3591,7 +3497,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 +3507,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 +3596,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": {},
@@ -3803,20 +3695,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 +3806,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 +3932,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": {
@@ -4453,8 +4331,8 @@
["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],
@@ -4509,24 +4387,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": {},
@@ -4948,15 +4814,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 +4884,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": {
@@ -5331,8 +5184,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 +5279,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 +5461,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 +5563,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]
@@ -5808,20 +5644,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 +5702,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"
@@ -5925,7 +5747,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"
@@ -6434,7 +6256,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],
@@ -6645,8 +6467,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": {},
@@ -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]
],
@@ -1677,40 +1661,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": {},
@@ -1807,7 +1757,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 +1766,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"
},
@@ -2035,10 +1985,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": {
@@ -2224,9 +2171,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 +2226,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": {},
@@ -2422,15 +2338,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": {},
@@ -2987,7 +2894,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": {
@@ -3407,7 +3314,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 +3386,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]
@@ -3591,7 +3497,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 +3507,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 +3596,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": {},
@@ -3803,20 +3695,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 +3806,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 +3932,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": {
@@ -4453,8 +4331,8 @@
["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],
@@ -4509,24 +4387,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": {},
@@ -4948,15 +4814,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 +4884,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": {
@@ -5331,8 +5184,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 +5279,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 +5461,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 +5563,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]
@@ -5808,20 +5644,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 +5702,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"
@@ -5925,7 +5747,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"
@@ -6434,7 +6256,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],
@@ -6645,8 +6467,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": {},
@@ -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]
],
@@ -1677,40 +1661,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": {},
@@ -1807,7 +1757,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 +1766,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"
},
@@ -2035,10 +1985,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": {
@@ -2224,9 +2171,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 +2226,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": {},
@@ -2422,15 +2338,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": {},
@@ -2987,7 +2894,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": {
@@ -3407,7 +3314,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 +3386,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]
@@ -3591,7 +3497,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 +3507,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 +3596,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": {},
@@ -3803,20 +3695,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 +3806,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 +3932,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": {
@@ -4453,8 +4331,8 @@
["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],
@@ -4509,24 +4387,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": {},
@@ -4948,15 +4814,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 +4884,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": {
@@ -5331,8 +5184,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 +5279,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 +5461,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 +5563,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]
@@ -5808,20 +5644,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 +5702,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"
@@ -5925,7 +5747,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"
@@ -6434,7 +6256,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],
@@ -6645,8 +6467,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": {},
@@ -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]
],
@@ -1677,40 +1661,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": {},
@@ -1807,7 +1757,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 +1766,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"
},
@@ -2035,10 +1985,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": {
@@ -2224,9 +2171,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 +2226,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": {},
@@ -2422,15 +2338,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": {},
@@ -2987,7 +2894,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": {
@@ -3407,7 +3314,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 +3386,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]
@@ -3591,7 +3497,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 +3507,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 +3596,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": {},
@@ -3803,20 +3695,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 +3806,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 +3932,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": {
@@ -4453,8 +4331,8 @@
["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],
@@ -4509,24 +4387,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": {},
@@ -4948,15 +4814,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 +4884,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": {
@@ -5331,8 +5184,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 +5279,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 +5461,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 +5563,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]
@@ -5808,20 +5644,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 +5702,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"
@@ -5925,7 +5747,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"
@@ -6434,7 +6256,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],
@@ -6645,8 +6467,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": {},
@@ -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]
],
@@ -1677,40 +1661,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": {},
@@ -1807,7 +1757,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 +1766,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ę"
},
@@ -2035,10 +1985,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": {
@@ -2224,9 +2171,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 +2226,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": {},
@@ -2422,15 +2338,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": {},
@@ -2987,7 +2894,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": {
@@ -3407,7 +3314,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 +3386,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]
@@ -3591,7 +3497,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 +3507,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 +3596,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": {},
@@ -3803,20 +3695,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 +3806,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 +3932,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": {
@@ -4453,8 +4331,8 @@
["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],
@@ -4509,24 +4387,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": {},
@@ -4948,15 +4814,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 +4884,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": {
@@ -5331,8 +5184,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 +5279,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 +5461,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 +5563,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]
@@ -5808,20 +5644,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 +5702,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"
@@ -5925,7 +5747,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"
@@ -6434,7 +6256,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],
@@ -6645,8 +6467,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": {},
@@ -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]
],
@@ -1677,40 +1661,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": {},
@@ -1807,7 +1757,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 +1766,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"
},
@@ -2035,10 +1985,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": {
@@ -2224,9 +2171,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 +2226,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": {},
@@ -2422,15 +2338,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": {},
@@ -2987,7 +2894,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": {
@@ -3407,7 +3314,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 +3386,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]
@@ -3591,7 +3497,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 +3507,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 +3596,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": {},
@@ -3803,20 +3695,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 +3806,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 +3932,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": {
@@ -4453,8 +4331,8 @@
["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],
@@ -4509,24 +4387,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": {},
@@ -4948,15 +4814,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 +4884,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": {
@@ -5331,8 +5184,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 +5279,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 +5461,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 +5563,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]
@@ -5808,20 +5644,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 +5702,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"
@@ -5925,7 +5747,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"
@@ -6434,7 +6256,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],
@@ -6645,8 +6467,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": {},
@@ -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]
],
@@ -1677,40 +1661,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": {},
@@ -1807,7 +1757,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 +1766,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": "Создать новый список"
},
@@ -2035,10 +1985,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": {
@@ -2224,9 +2171,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 +2226,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": {},
@@ -2422,15 +2338,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": {},
@@ -2987,7 +2894,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": {
@@ -3407,7 +3314,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 +3386,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]
@@ -3591,7 +3497,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 +3507,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 +3596,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": {},
@@ -3803,20 +3695,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 +3806,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 +3932,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": {
@@ -4453,8 +4331,8 @@
["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],
@@ -4509,24 +4387,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": {},
@@ -4948,15 +4814,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 +4884,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": {
@@ -5331,8 +5184,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 +5279,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 +5461,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 +5563,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]
@@ -5808,20 +5644,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 +5702,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 272],
["src/views/board/index.tsx", 249],
["src/views/card/index.tsx", 231]
],
"translation": "Не удалось обновить карточку"
@@ -5925,7 +5747,7 @@
"placeholders": {},
"comments": [],
"origin": [
["src/views/board/index.tsx", 216],
["src/views/board/index.tsx", 193],
["src/views/card/components/ListSelector.tsx", 54]
],
"translation": "Не удалось обновить список"
@@ -6434,7 +6256,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],
@@ -6645,8 +6467,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

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

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

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

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

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

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

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

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

@@ -163,9 +163,9 @@ 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 {
@@ -182,7 +182,6 @@ const loggingMiddleware = t.middleware(async ({ path, type, next, ctx, getRawInp
duration,
userId: user?.id,
...(isCloud && { email: user?.email }),
input,
};
const label = transport === "rest" ? "REST" : "tRPC";

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

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