@eifi1/ui-kit 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -5
- package/dist/account/data-export-setting.js +3 -2
- package/dist/account/data-export-setting.js.map +1 -1
- package/dist/account/delete-account-setting.d.ts +5 -1
- package/dist/account/delete-account-setting.js +3 -2
- package/dist/account/delete-account-setting.js.map +1 -1
- package/dist/account/email-change-setting.js +4 -3
- package/dist/account/email-change-setting.js.map +1 -1
- package/dist/account/sessions-setting.js +3 -2
- package/dist/account/sessions-setting.js.map +1 -1
- package/dist/admin/admin-action-confirm.d.ts +5 -1
- package/dist/admin/invitations-panel.d.ts +5 -1
- package/dist/admin/invitations-panel.js +3 -2
- package/dist/admin/invitations-panel.js.map +1 -1
- package/dist/admin/reviewer-scope-editor.d.ts +5 -1
- package/dist/admin/role-select.d.ts +5 -1
- package/dist/admin/transfer-ownership-dialog.d.ts +5 -1
- package/dist/admin/user-roster.d.ts +5 -1
- package/dist/auth/auth-errors.d.ts +24 -6
- package/dist/auth/auth-errors.js +7 -1
- package/dist/auth/auth-errors.js.map +1 -1
- package/dist/auth/register-form.d.ts +5 -1
- package/dist/auth/sign-in-form.d.ts +15 -1
- package/dist/auth/sign-in-form.js +12 -3
- package/dist/auth/sign-in-form.js.map +1 -1
- package/dist/chart.d.ts +5 -1
- package/dist/components/account-settings-labels.d.ts +12 -0
- package/dist/components/account-settings-labels.js +2 -1
- package/dist/components/account-settings-labels.js.map +1 -1
- package/dist/components/account-settings.d.ts +8 -2
- package/dist/components/account-settings.js +19 -11
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/amount-input.d.ts +5 -1
- package/dist/components/button-group.d.ts +5 -1
- package/dist/components/calculator.d.ts +5 -1
- package/dist/components/confirm-dialog.d.ts +5 -1
- package/dist/components/copy-button.d.ts +5 -1
- package/dist/components/danger-confirm.d.ts +5 -1
- package/dist/components/data-table.js +8 -1
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.js +9 -4
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/facing-pair.d.ts +5 -1
- package/dist/components/file-button.d.ts +5 -1
- package/dist/components/file-dropzone.d.ts +5 -1
- package/dist/components/form-actions.d.ts +5 -1
- package/dist/components/iban-input.d.ts +5 -1
- package/dist/components/language-select.d.ts +5 -1
- package/dist/components/legal-page.d.ts +5 -1
- package/dist/components/legal.d.ts +5 -1
- package/dist/components/money-field.d.ts +5 -1
- package/dist/components/number-field.d.ts +5 -1
- package/dist/components/number-input.d.ts +5 -1
- package/dist/components/numpad-sheet.d.ts +5 -1
- package/dist/components/passkeys-setting.js +2 -1
- package/dist/components/passkeys-setting.js.map +1 -1
- package/dist/components/phone-input.d.ts +5 -1
- package/dist/components/series-chart.d.ts +5 -1
- package/dist/components/settings-fields.d.ts +40 -3
- package/dist/components/settings-fields.js +20 -1
- package/dist/components/settings-fields.js.map +1 -1
- package/dist/components/share-card.d.ts +5 -1
- package/dist/components/text-link.d.ts +5 -1
- package/dist/components/time-input.d.ts +5 -1
- package/dist/components/ui.d.ts +5 -1
- package/dist/{delete-account-setting-CGVMckMw.d.ts → delete-account-setting-DCFS5Vof.d.ts} +52 -23
- package/dist/demo/demo-banner.d.ts +51 -0
- package/dist/demo/demo-banner.js +97 -0
- package/dist/demo/demo-banner.js.map +1 -0
- package/dist/demo/demo-countdown.d.ts +58 -0
- package/dist/demo/demo-countdown.js +79 -0
- package/dist/demo/demo-countdown.js.map +1 -0
- package/dist/demo/demo-ended.d.ts +45 -0
- package/dist/demo/demo-ended.js +42 -0
- package/dist/demo/demo-ended.js.map +1 -0
- package/dist/demo/demo-labels.d.ts +53 -0
- package/dist/demo/demo-labels.js +29 -0
- package/dist/demo/demo-labels.js.map +1 -0
- package/dist/demo/demo-session.d.ts +30 -0
- package/dist/demo/demo-session.js +8 -0
- package/dist/demo/demo-session.js.map +1 -0
- package/dist/demo/demo-start.d.ts +54 -0
- package/dist/demo/demo-start.js +68 -0
- package/dist/demo/demo-start.js.map +1 -0
- package/dist/feedback/feedback-attachment.d.ts +5 -1
- package/dist/feedback/feedback-context.d.ts +5 -1
- package/dist/feedback/feedback-crash.d.ts +5 -1
- package/dist/feedback/feedback-dialog.d.ts +5 -1
- package/dist/feedback/feedback-inbox.d.ts +5 -1
- package/dist/feedback/feedback-inbox.js +12 -2
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-labels.d.ts +5 -1
- package/dist/feedback/feedback-labels.js +1 -0
- package/dist/feedback/feedback-labels.js.map +1 -1
- package/dist/feedback/feedback-menu.d.ts +5 -1
- package/dist/feedback/feedback-record.d.ts +5 -1
- package/dist/feedback/feedback-record.js +2 -0
- package/dist/feedback/feedback-record.js.map +1 -1
- package/dist/feedback/feedback-row-detail.d.ts +5 -1
- package/dist/feedback/feedback-row-detail.js +7 -0
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback/feedback-status-undo.d.ts +5 -1
- package/dist/feedback/feedback-submit.d.ts +5 -1
- package/dist/feedback/feedback-swipe.d.ts +6 -2
- package/dist/feedback/feedback-swipe.js.map +1 -1
- package/dist/feedback/feedback-table.d.ts +5 -1
- package/dist/feedback/feedback-thread.d.ts +5 -1
- package/dist/feedback.d.ts +5 -1
- package/dist/hooks/use-file-drop.d.ts +5 -1
- package/dist/i18n/defaults.d.ts +5 -1
- package/dist/i18n/defaults.js +6 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +5 -1
- package/dist/i18n/german.js +84 -2
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +5 -1
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +5 -1
- package/dist/i18n/locales/de-CH.d.ts +5 -1
- package/dist/i18n/locales/en.d.ts +5 -1
- package/dist/i18n/locales/es.d.ts +5 -1
- package/dist/i18n/locales/es.js +81 -2
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +5 -1
- package/dist/i18n/locales/fr.js +82 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +5 -1
- package/dist/i18n/locales/hu.js +82 -2
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +5 -1
- package/dist/i18n/locales/it.js +81 -2
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +5 -1
- package/dist/i18n/locales/zh.js +80 -2
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +5 -1
- package/dist/i18n/review.js +14 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +25 -3
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -1
- package/dist/landing/access.d.ts +48 -0
- package/dist/landing/access.js +38 -0
- package/dist/landing/access.js.map +1 -0
- package/dist/landing/landing-actions.d.ts +138 -0
- package/dist/landing/landing-actions.js +39 -0
- package/dist/landing/landing-actions.js.map +1 -0
- package/dist/landing/landing-labels.d.ts +43 -0
- package/dist/landing/landing-labels.js +23 -0
- package/dist/landing/landing-labels.js.map +1 -0
- package/dist/landing/landing-sections.d.ts +218 -0
- package/dist/landing/landing-sections.js +167 -0
- package/dist/landing/landing-sections.js.map +1 -0
- package/dist/landing/page-seo.d.ts +99 -0
- package/dist/landing/page-seo.js +121 -0
- package/dist/landing/page-seo.js.map +1 -0
- package/dist/landing/public-header.d.ts +148 -0
- package/dist/landing/public-header.js +49 -0
- package/dist/landing/public-header.js.map +1 -0
- package/dist/landing/routing.d.ts +218 -0
- package/dist/landing/routing.js +81 -0
- package/dist/landing/routing.js.map +1 -0
- package/dist/rhf/fields.d.ts +5 -1
- package/dist/rhf/form.d.ts +5 -1
- package/dist/rhf.d.ts +5 -1
- package/dist/settings/settings-catalogue.d.ts +84 -0
- package/dist/settings/settings-catalogue.js +47 -0
- package/dist/settings/settings-catalogue.js.map +1 -0
- package/dist/settings/settings-context.d.ts +31 -0
- package/dist/settings/settings-context.js +11 -0
- package/dist/settings/settings-context.js.map +1 -0
- package/dist/settings/settings-heading.d.ts +50 -0
- package/dist/settings/settings-heading.js +27 -0
- package/dist/settings/settings-heading.js.map +1 -0
- package/dist/settings/settings-labels.d.ts +41 -0
- package/dist/settings/settings-labels.js +38 -0
- package/dist/settings/settings-labels.js.map +1 -0
- package/dist/settings/settings-layout.d.ts +77 -0
- package/dist/settings/settings-layout.js +242 -0
- package/dist/settings/settings-layout.js.map +1 -0
- package/dist/settings/settings-section.d.ts +133 -0
- package/dist/settings/settings-section.js +63 -0
- package/dist/settings/settings-section.js.map +1 -0
- package/dist/settings/use-account-language.d.ts +83 -0
- package/dist/settings/use-account-language.js +62 -0
- package/dist/settings/use-account-language.js.map +1 -0
- package/dist/settings/use-settings-focus.d.ts +34 -0
- package/dist/settings/use-settings-focus.js +77 -0
- package/dist/settings/use-settings-focus.js.map +1 -0
- package/dist/settings/use-settings-route.d.ts +118 -0
- package/dist/settings/use-settings-route.js +120 -0
- package/dist/settings/use-settings-route.js.map +1 -0
- package/dist/shell/app-shell.d.ts +11 -1
- package/dist/shell/app-shell.js +3 -2
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/top-bar-brand.d.ts +5 -1
- package/dist/shell.d.ts +15 -1
- package/dist/shell.js +56 -0
- package/dist/shell.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +5 -1
- package/dist/wizard.d.ts +5 -1
- package/package.json +2 -1
- package/src/account/data-export-setting.tsx +3 -2
- package/src/account/delete-account-setting.tsx +3 -2
- package/src/account/email-change-setting.tsx +4 -7
- package/src/account/sessions-setting.tsx +3 -2
- package/src/admin/invitations-panel.tsx +11 -4
- package/src/auth/auth-errors.ts +36 -5
- package/src/auth/sign-in-form.tsx +34 -3
- package/src/components/account-settings-labels.ts +13 -0
- package/src/components/account-settings.tsx +33 -12
- package/src/components/data-table.tsx +8 -0
- package/src/components/date-picker.tsx +9 -4
- package/src/components/passkeys-setting.tsx +2 -1
- package/src/components/settings-fields.tsx +50 -1
- package/src/demo/demo-banner.tsx +159 -0
- package/src/demo/demo-countdown.ts +141 -0
- package/src/demo/demo-ended.tsx +89 -0
- package/src/demo/demo-labels.ts +78 -0
- package/src/demo/demo-session.ts +32 -0
- package/src/demo/demo-start.tsx +150 -0
- package/src/feedback/feedback-inbox.tsx +21 -8
- package/src/feedback/feedback-labels.ts +2 -1
- package/src/feedback/feedback-record.ts +10 -7
- package/src/feedback/feedback-row-detail.tsx +28 -4
- package/src/feedback/feedback-swipe.tsx +1 -1
- package/src/i18n/defaults.ts +6 -0
- package/src/i18n/german.ts +86 -0
- package/src/i18n/kit-labels.tsx +9 -0
- package/src/i18n/locales/es.ts +83 -0
- package/src/i18n/locales/fr.ts +84 -0
- package/src/i18n/locales/hu.ts +84 -0
- package/src/i18n/locales/it.ts +83 -0
- package/src/i18n/locales/zh.ts +81 -0
- package/src/i18n/review.ts +13 -0
- package/src/index.ts +92 -1
- package/src/landing/access.ts +97 -0
- package/src/landing/landing-actions.tsx +92 -0
- package/src/landing/landing-labels.ts +59 -0
- package/src/landing/landing-sections.tsx +337 -0
- package/src/landing/page-seo.ts +251 -0
- package/src/landing/public-header.tsx +120 -0
- package/src/landing/routing.tsx +209 -0
- package/src/settings/settings-catalogue.ts +132 -0
- package/src/settings/settings-context.ts +33 -0
- package/src/settings/settings-heading.tsx +65 -0
- package/src/settings/settings-labels.ts +75 -0
- package/src/settings/settings-layout.tsx +379 -0
- package/src/settings/settings-section.tsx +108 -0
- package/src/settings/use-account-language.ts +146 -0
- package/src/settings/use-settings-focus.ts +120 -0
- package/src/settings/use-settings-route.ts +258 -0
- package/src/shell/app-shell.tsx +10 -2
- package/src/shell.ts +45 -0
package/src/i18n/locales/hu.ts
CHANGED
|
@@ -71,6 +71,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
71
71
|
// A noun after a number stays singular in Hungarian, so the counts need no plural.
|
|
72
72
|
feedbackStatus: {
|
|
73
73
|
OPEN: "Nyitott",
|
|
74
|
+
READY: "Megvalósításra kész",
|
|
74
75
|
IN_PROGRESS: "Folyamatban",
|
|
75
76
|
IN_EVALUATION: "Értékelés alatt",
|
|
76
77
|
NEEDS_LIVE_TEST: "Élesben tesztelendő",
|
|
@@ -129,6 +130,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
129
130
|
phoneActions: "Visszajelzés-műveletek",
|
|
130
131
|
},
|
|
131
132
|
feedbackDetail: {
|
|
133
|
+
subject: "Tárgy",
|
|
132
134
|
body: "Leírás",
|
|
133
135
|
edit: "Szerkesztés",
|
|
134
136
|
editDescription: "Leírás szerkesztése",
|
|
@@ -187,6 +189,8 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
187
189
|
qrAlt: "QR-kód a hitelesítő alkalmazáshoz",
|
|
188
190
|
secretHint: "Nem sikerül beolvasni? Adja meg inkább ezt a kulcsot az alkalmazásban:",
|
|
189
191
|
copySecret: "Kulcs másolása",
|
|
192
|
+
// 0.31.0: the card's title; the state ("Bekapcsolva") stands beside it.
|
|
193
|
+
title: "Kétlépcsős azonosítás",
|
|
190
194
|
},
|
|
191
195
|
passkeys: {
|
|
192
196
|
title: "Hozzáférési kulcsok",
|
|
@@ -897,6 +901,8 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
897
901
|
`${n(length)} betű és számjegy. A kötőjelek és a szóközök nem számítanak.`,
|
|
898
902
|
recoveryCodeInvalid: "Ez a tartalékkód érvénytelen, vagy már felhasználták.",
|
|
899
903
|
rateLimited,
|
|
904
|
+
// 0.31.0: the same words as `landing.requestAccess`.
|
|
905
|
+
requestAccess: "Hozzáférés kérése",
|
|
900
906
|
},
|
|
901
907
|
register: {
|
|
902
908
|
firstName: "Keresztnév",
|
|
@@ -1332,6 +1338,84 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
1332
1338
|
? `„${group}”: jóváhagyja az ellenőrizetlen szöveget?`
|
|
1333
1339
|
: `„${group}”: jóváhagyja mind a(z) ${n(count)} ellenőrizetlen szöveget, a képernyőn nem láthatókat is?`,
|
|
1334
1340
|
},
|
|
1341
|
+
// 0.31.0 — docs/settings-harmonization.md §4.3 and docs/landing-demo-harmonization.md
|
|
1342
|
+
// §4–§5: the settings shell, the landing page's generic words and the demo. The help
|
|
1343
|
+
// lines name things as `accountSettings`, `sessions` and `dataExport` do. "Ön"; the
|
|
1344
|
+
// buttons are nouns, as "Fiók létrehozása"; the numeral stays bare before its noun
|
|
1345
|
+
// ("3 találat", "50 perc"); every value the app passes in stands after a colon.
|
|
1346
|
+
settings: {
|
|
1347
|
+
title: "Beállítások",
|
|
1348
|
+
search: "Keresés a beállításokban",
|
|
1349
|
+
searchPlaceholder: "Keresés a beállításokban…",
|
|
1350
|
+
results: "Megfelelő beállítások",
|
|
1351
|
+
noMatches: (query) => `Nincs a keresésnek megfelelő beállítás: „${query}”.`,
|
|
1352
|
+
// As `commandPalette.clear`.
|
|
1353
|
+
clearSearch: "Keresés törlése",
|
|
1354
|
+
back: "Vissza a beállításokhoz",
|
|
1355
|
+
sections: "Beállításcsoportok",
|
|
1356
|
+
matchCount: (count) => `${n(count)} találat`,
|
|
1357
|
+
groups: {
|
|
1358
|
+
appearance: {
|
|
1359
|
+
title: "Megjelenés",
|
|
1360
|
+
help: "Nyelv, téma és az alkalmazás megjelenése ezen az eszközön.",
|
|
1361
|
+
},
|
|
1362
|
+
account: {
|
|
1363
|
+
title: "Fiók",
|
|
1364
|
+
help: "Az Ön profilja és e-mail-címe.",
|
|
1365
|
+
},
|
|
1366
|
+
security: {
|
|
1367
|
+
title: "Biztonság",
|
|
1368
|
+
help: "Hogyan jelentkezik be: jelszó, ellenőrző kódok, hozzáférési kulcsok és munkamenetek.",
|
|
1369
|
+
},
|
|
1370
|
+
notifications: {
|
|
1371
|
+
title: "Értesítések",
|
|
1372
|
+
help: "Miről, mikor és mely eszközökön kap értesítést.",
|
|
1373
|
+
},
|
|
1374
|
+
data: {
|
|
1375
|
+
title: "Adatok",
|
|
1376
|
+
help: "Exportálja a saját adatait, vagy törölje őket a fiókjával együtt.",
|
|
1377
|
+
},
|
|
1378
|
+
},
|
|
1379
|
+
},
|
|
1380
|
+
landing: {
|
|
1381
|
+
signIn: "Bejelentkezés",
|
|
1382
|
+
requestAccess: "Hozzáférés kérése",
|
|
1383
|
+
getStarted: "Kezdés",
|
|
1384
|
+
tryDemo: "Demó kipróbálása",
|
|
1385
|
+
openApp: "Alkalmazás megnyitása",
|
|
1386
|
+
continueDemo: "Demó folytatása",
|
|
1387
|
+
beta: "Béta",
|
|
1388
|
+
// Plain text: a mail's subject and body lines, never HTML. The app's name after a
|
|
1389
|
+
// colon, so it needs no case ending ("Hozzáférés a Kastlanhoz").
|
|
1390
|
+
accessSubject: (app) => (app ? `Hozzáférési kérelem: ${app}` : "Hozzáférési kérelem"),
|
|
1391
|
+
accessName: "Név:",
|
|
1392
|
+
accessCompany: "Cég:",
|
|
1393
|
+
accessUse: "Mire használná:",
|
|
1394
|
+
},
|
|
1395
|
+
demo: {
|
|
1396
|
+
starting: "A demó indítása…",
|
|
1397
|
+
rateLimited: (minutes) =>
|
|
1398
|
+
minutes
|
|
1399
|
+
? `Túl sok demó indult erről a hálózatról. Próbálja újra ${n(minutes)} perc múlva.`
|
|
1400
|
+
: "Túl sok demó indult erről a hálózatról. Próbálja újra később.",
|
|
1401
|
+
capacity: "A demó jelenleg megtelt. Kérjük, próbálja újra később.",
|
|
1402
|
+
unavailable: "A demó jelenleg nem érhető el.",
|
|
1403
|
+
failed: "A demót nem sikerült elindítani. Kérjük, próbálja újra.",
|
|
1404
|
+
// As `legal.backHome`.
|
|
1405
|
+
backToStart: "Vissza a kezdőlapra",
|
|
1406
|
+
hoursLeft: (hours, minutes) => `Demó · még ${n(hours)} óra ${n(minutes)} perc`,
|
|
1407
|
+
minutesLeft: (minutes) => `Demó · még ${n(minutes)} perc`,
|
|
1408
|
+
badge: "Demó",
|
|
1409
|
+
readOnly: "Mintaadatokat lát. Módosításra nincs lehetőség.",
|
|
1410
|
+
sandbox: "Amit Ön hoz létre, az a demó végén törlődik.",
|
|
1411
|
+
details: "A demó részletei",
|
|
1412
|
+
writeLocked: "A demóban ez nem lehetséges.",
|
|
1413
|
+
endedTitle: "A demó véget ért",
|
|
1414
|
+
endedReadOnly: "A mintaadatokat rendszeresen visszaállítjuk.",
|
|
1415
|
+
endedSandbox:
|
|
1416
|
+
"A mintaadatokat rendszeresen visszaállítjuk, a demóban létrehozott saját munkáját pedig töröljük.",
|
|
1417
|
+
restart: "Új demó indítása",
|
|
1418
|
+
},
|
|
1335
1419
|
};
|
|
1336
1420
|
}
|
|
1337
1421
|
|
package/src/i18n/locales/it.ts
CHANGED
|
@@ -70,6 +70,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
70
70
|
// 0.27.0. "Feedback" stays, as `feedbackDialog`; "rielaborazione" for rework.
|
|
71
71
|
feedbackStatus: {
|
|
72
72
|
OPEN: "Aperto",
|
|
73
|
+
READY: "Pronto da implementare",
|
|
73
74
|
IN_PROGRESS: "In corso",
|
|
74
75
|
IN_EVALUATION: "In valutazione",
|
|
75
76
|
NEEDS_LIVE_TEST: "Da testare in produzione",
|
|
@@ -131,6 +132,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
131
132
|
phoneActions: "Azioni sul feedback",
|
|
132
133
|
},
|
|
133
134
|
feedbackDetail: {
|
|
135
|
+
subject: "Oggetto",
|
|
134
136
|
body: "Descrizione",
|
|
135
137
|
edit: "Modifica",
|
|
136
138
|
editDescription: "Modifica descrizione",
|
|
@@ -191,6 +193,8 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
191
193
|
qrAlt: "Codice QR per la Sua app di autenticazione",
|
|
192
194
|
secretHint: "Non riesce a scansionarlo? Inserisca invece questa chiave nell’app:",
|
|
193
195
|
copySecret: "Copia chiave",
|
|
196
|
+
// 0.31.0: the card's title; the state ("Attiva") stands beside it.
|
|
197
|
+
title: "Autenticazione a due fattori",
|
|
194
198
|
},
|
|
195
199
|
passkeys: {
|
|
196
200
|
title: "Passkey",
|
|
@@ -895,6 +899,8 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
895
899
|
recoveryCodeHint: (length) => `${n(length)} lettere e cifre. Trattini e spazi non contano.`,
|
|
896
900
|
recoveryCodeInvalid: "Questo codice di riserva non è valido o è già stato usato.",
|
|
897
901
|
rateLimited,
|
|
902
|
+
// 0.31.0: the same words as `landing.requestAccess`.
|
|
903
|
+
requestAccess: "Richiedi l’accesso",
|
|
898
904
|
},
|
|
899
905
|
register: {
|
|
900
906
|
firstName: "Nome",
|
|
@@ -1342,6 +1348,83 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1342
1348
|
? `Approvare il testo non rivisto in «${group}»?`
|
|
1343
1349
|
: `Approvare tutti i ${n(count)} testi non rivisti in «${group}», compresi quelli non visibili sullo schermo?`,
|
|
1344
1350
|
},
|
|
1351
|
+
// 0.31.0 — docs/settings-harmonization.md §4.3 and docs/landing-demo-harmonization.md
|
|
1352
|
+
// §4–§5: the settings shell, the landing page's generic words and the demo. The help
|
|
1353
|
+
// lines name things as `accountSettings`, `sessions` and `dataExport` do. "Lei", and
|
|
1354
|
+
// no sentence about the reader needs a gender ("avvisato/a").
|
|
1355
|
+
settings: {
|
|
1356
|
+
title: "Impostazioni",
|
|
1357
|
+
search: "Cerca nelle impostazioni",
|
|
1358
|
+
searchPlaceholder: "Cerca nelle impostazioni…",
|
|
1359
|
+
results: "Impostazioni corrispondenti",
|
|
1360
|
+
noMatches: (query) => `Nessuna impostazione corrisponde a «${query}».`,
|
|
1361
|
+
// As `commandPalette.clear`.
|
|
1362
|
+
clearSearch: "Cancella ricerca",
|
|
1363
|
+
back: "Torna alle impostazioni",
|
|
1364
|
+
sections: "Sezioni delle impostazioni",
|
|
1365
|
+
matchCount: (count) => `${n(count)} ${plural(count, "risultato", "risultati")}`,
|
|
1366
|
+
groups: {
|
|
1367
|
+
appearance: {
|
|
1368
|
+
title: "Aspetto",
|
|
1369
|
+
help: "Lingua, tema e aspetto dell’app su questo dispositivo.",
|
|
1370
|
+
},
|
|
1371
|
+
account: {
|
|
1372
|
+
title: "Account",
|
|
1373
|
+
help: "Il Suo profilo e il Suo indirizzo email.",
|
|
1374
|
+
},
|
|
1375
|
+
security: {
|
|
1376
|
+
title: "Sicurezza",
|
|
1377
|
+
help: "Come accede: password, codici 2FA, passkey e sessioni.",
|
|
1378
|
+
},
|
|
1379
|
+
notifications: {
|
|
1380
|
+
title: "Notifiche",
|
|
1381
|
+
help: "Quali notifiche riceve, quando e su quali dispositivi.",
|
|
1382
|
+
},
|
|
1383
|
+
data: {
|
|
1384
|
+
title: "Dati",
|
|
1385
|
+
help: "Esporti i Suoi dati o li elimini insieme al Suo account.",
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
},
|
|
1389
|
+
landing: {
|
|
1390
|
+
signIn: "Accedi",
|
|
1391
|
+
requestAccess: "Richiedi l’accesso",
|
|
1392
|
+
getStarted: "Inizia",
|
|
1393
|
+
tryDemo: "Prova la demo",
|
|
1394
|
+
openApp: "Apri l’app",
|
|
1395
|
+
continueDemo: "Continua la demo",
|
|
1396
|
+
beta: "Beta",
|
|
1397
|
+
// Plain text: a mail's subject and body lines, never HTML.
|
|
1398
|
+
accessSubject: (app) => (app ? `Accesso a ${app}` : "Richiesta di accesso"),
|
|
1399
|
+
accessName: "Nome:",
|
|
1400
|
+
accessCompany: "Azienda:",
|
|
1401
|
+
accessUse: "Uso previsto:",
|
|
1402
|
+
},
|
|
1403
|
+
demo: {
|
|
1404
|
+
starting: "Avvio della demo…",
|
|
1405
|
+
rateLimited: (minutes) =>
|
|
1406
|
+
minutes
|
|
1407
|
+
? `Troppe demo da questa rete. Riprovi tra ${n(minutes)} min.`
|
|
1408
|
+
: "Troppe demo da questa rete. Riprovi più tardi.",
|
|
1409
|
+
capacity: "La demo è al completo in questo momento. Riprovi più tardi.",
|
|
1410
|
+
unavailable: "La demo non è disponibile in questo momento.",
|
|
1411
|
+
failed: "Impossibile avviare la demo. Riprovi.",
|
|
1412
|
+
// As `legal.backHome`.
|
|
1413
|
+
backToStart: "Torna alla pagina iniziale",
|
|
1414
|
+
// "ancora" rather than "mancano", which would have to agree with the count.
|
|
1415
|
+
hoursLeft: (hours, minutes) => `Demo · ancora ${n(hours)} h ${n(minutes)} min`,
|
|
1416
|
+
minutesLeft: (minutes) => `Demo · ancora ${n(minutes)} min`,
|
|
1417
|
+
badge: "Demo",
|
|
1418
|
+
readOnly: "Sta guardando dati di esempio. Non è possibile apportare modifiche.",
|
|
1419
|
+
sandbox: "Quello che crea viene eliminato al termine della demo.",
|
|
1420
|
+
details: "Dettagli della demo",
|
|
1421
|
+
writeLocked: "Non è possibile nella demo.",
|
|
1422
|
+
endedTitle: "La demo è terminata",
|
|
1423
|
+
endedReadOnly: "I dati di esempio vengono reimpostati regolarmente.",
|
|
1424
|
+
endedSandbox:
|
|
1425
|
+
"I dati di esempio vengono reimpostati regolarmente; quello che ha creato nella demo viene eliminato.",
|
|
1426
|
+
restart: "Avvia una nuova demo",
|
|
1427
|
+
},
|
|
1345
1428
|
};
|
|
1346
1429
|
}
|
|
1347
1430
|
|
package/src/i18n/locales/zh.ts
CHANGED
|
@@ -64,6 +64,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
64
64
|
// 0.27.0. 反馈 for feedback, as `feedbackDialog`; 返工 for rework.
|
|
65
65
|
feedbackStatus: {
|
|
66
66
|
OPEN: "待处理",
|
|
67
|
+
READY: "待实现",
|
|
67
68
|
IN_PROGRESS: "处理中",
|
|
68
69
|
IN_EVALUATION: "评估中",
|
|
69
70
|
NEEDS_LIVE_TEST: "待线上测试",
|
|
@@ -122,6 +123,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
122
123
|
phoneActions: "反馈操作",
|
|
123
124
|
},
|
|
124
125
|
feedbackDetail: {
|
|
126
|
+
subject: "主题",
|
|
125
127
|
body: "描述",
|
|
126
128
|
edit: "编辑",
|
|
127
129
|
editDescription: "编辑描述",
|
|
@@ -180,6 +182,8 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
180
182
|
qrAlt: "身份验证器应用的二维码",
|
|
181
183
|
secretHint: "无法扫描?请改为在应用中输入此密钥:",
|
|
182
184
|
copySecret: "复制密钥",
|
|
185
|
+
// 0.31.0: the card's title; the state ("已开启") stands beside it.
|
|
186
|
+
title: "双重验证",
|
|
183
187
|
},
|
|
184
188
|
passkeys: {
|
|
185
189
|
title: "通行密钥",
|
|
@@ -866,6 +870,8 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
866
870
|
recoveryCodeHint: (length) => `共 ${n(length)} 位字母和数字,连字符和空格不影响。`,
|
|
867
871
|
recoveryCodeInvalid: "此备用码无效或已被使用。",
|
|
868
872
|
rateLimited,
|
|
873
|
+
// 0.31.0: the same words as `landing.requestAccess`.
|
|
874
|
+
requestAccess: "申请访问权限",
|
|
869
875
|
},
|
|
870
876
|
register: {
|
|
871
877
|
firstName: "名字",
|
|
@@ -1276,6 +1282,81 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1276
1282
|
? `通过“${group}”中的未审校文本?`
|
|
1277
1283
|
: `通过“${group}”中全部 ${n(count)} 条未审校文本(包括未显示在屏幕上的)?`,
|
|
1278
1284
|
},
|
|
1285
|
+
// 0.31.0 — docs/settings-harmonization.md §4.3 and docs/landing-demo-harmonization.md
|
|
1286
|
+
// §4–§5: the settings shell, the landing page's generic words and the demo. The help
|
|
1287
|
+
// lines name things as `accountSettings`, `sessions` and `dataExport` do; "访问权限"
|
|
1288
|
+
// as `shareCard`. A space either side of a numeral or a Latin name.
|
|
1289
|
+
settings: {
|
|
1290
|
+
title: "设置",
|
|
1291
|
+
search: "搜索设置",
|
|
1292
|
+
searchPlaceholder: "搜索设置…",
|
|
1293
|
+
results: "匹配的设置",
|
|
1294
|
+
noMatches: (query) => `没有与“${query}”匹配的设置。`,
|
|
1295
|
+
// As `commandPalette.clear`.
|
|
1296
|
+
clearSearch: "清除搜索",
|
|
1297
|
+
back: "返回设置",
|
|
1298
|
+
sections: "设置分类",
|
|
1299
|
+
matchCount: (count) => `${n(count)} 个匹配项`,
|
|
1300
|
+
groups: {
|
|
1301
|
+
appearance: {
|
|
1302
|
+
title: "外观",
|
|
1303
|
+
help: "语言、主题,以及应用在此设备上的外观。",
|
|
1304
|
+
},
|
|
1305
|
+
account: {
|
|
1306
|
+
title: "账户",
|
|
1307
|
+
help: "您的个人资料和电子邮箱地址。",
|
|
1308
|
+
},
|
|
1309
|
+
security: {
|
|
1310
|
+
title: "安全",
|
|
1311
|
+
help: "您的登录方式:密码、双重验证码、通行密钥和会话。",
|
|
1312
|
+
},
|
|
1313
|
+
notifications: {
|
|
1314
|
+
title: "通知",
|
|
1315
|
+
help: "您会收到哪些通知、何时收到,以及在哪些设备上收到。",
|
|
1316
|
+
},
|
|
1317
|
+
data: {
|
|
1318
|
+
title: "数据",
|
|
1319
|
+
help: "导出属于您的数据,或将其连同账户一并删除。",
|
|
1320
|
+
},
|
|
1321
|
+
},
|
|
1322
|
+
},
|
|
1323
|
+
landing: {
|
|
1324
|
+
signIn: "登录",
|
|
1325
|
+
requestAccess: "申请访问权限",
|
|
1326
|
+
getStarted: "开始使用",
|
|
1327
|
+
tryDemo: "体验演示",
|
|
1328
|
+
openApp: "打开应用",
|
|
1329
|
+
continueDemo: "继续演示",
|
|
1330
|
+
beta: "测试版",
|
|
1331
|
+
// Plain text: a mail's subject and body lines, never HTML. Full-width colons.
|
|
1332
|
+
accessSubject: (app) => (app ? `申请访问 ${app}` : "访问权限申请"),
|
|
1333
|
+
accessName: "姓名:",
|
|
1334
|
+
accessCompany: "公司:",
|
|
1335
|
+
accessUse: "用途:",
|
|
1336
|
+
},
|
|
1337
|
+
demo: {
|
|
1338
|
+
starting: "正在启动演示…",
|
|
1339
|
+
rateLimited: (minutes) =>
|
|
1340
|
+
minutes
|
|
1341
|
+
? `来自此网络的演示过多,请 ${n(minutes)} 分钟后重试。`
|
|
1342
|
+
: "来自此网络的演示过多,请稍后重试。",
|
|
1343
|
+
capacity: "演示名额已满,请稍后重试。",
|
|
1344
|
+
unavailable: "演示暂不可用。",
|
|
1345
|
+
failed: "无法启动演示,请重试。",
|
|
1346
|
+
// As `legal.backHome`.
|
|
1347
|
+
backToStart: "返回首页",
|
|
1348
|
+
hoursLeft: (hours, minutes) => `演示 · 剩余 ${n(hours)} 小时 ${n(minutes)} 分钟`,
|
|
1349
|
+
minutesLeft: (minutes) => `演示 · 剩余 ${n(minutes)} 分钟`,
|
|
1350
|
+
badge: "演示",
|
|
1351
|
+
readOnly: "您正在查看示例数据,无法进行修改。",
|
|
1352
|
+
sandbox: "您自己创建的内容将在演示结束时删除。",
|
|
1353
|
+
details: "演示详情",
|
|
1354
|
+
writeLocked: "演示中无法执行此操作。",
|
|
1355
|
+
endedTitle: "演示已结束",
|
|
1356
|
+
endedReadOnly: "示例数据会定期重置。",
|
|
1357
|
+
endedSandbox: "示例数据会定期重置;您在演示中创建的内容会被删除。",
|
|
1358
|
+
restart: "开始新的演示",
|
|
1359
|
+
},
|
|
1279
1360
|
};
|
|
1280
1361
|
}
|
|
1281
1362
|
|
package/src/i18n/review.ts
CHANGED
|
@@ -338,4 +338,17 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
|
|
|
338
338
|
"invitations.linkReady": [["{{email}}"]],
|
|
339
339
|
"invitations.notSent": [["{{email}}"]],
|
|
340
340
|
"accountState.deletionOn": [["{{date}}"]],
|
|
341
|
+
// 0.31.0: the settings shell, the landing page and the demo
|
|
342
|
+
// (docs/settings-harmonization.md, docs/landing-demo-harmonization.md). An optional
|
|
343
|
+
// argument both given and left out: the subject without an app name, the refusal
|
|
344
|
+
// without a `Retry-After`.
|
|
345
|
+
"settings.noMatches": [["{{query}}"]],
|
|
346
|
+
"settings.matchCount": one,
|
|
347
|
+
"landing.accessSubject": [["{{app}}"], [undefined]],
|
|
348
|
+
"demo.rateLimited": [[], [1], [3]],
|
|
349
|
+
"demo.hoursLeft": [
|
|
350
|
+
[1, 1],
|
|
351
|
+
[3, 12],
|
|
352
|
+
],
|
|
353
|
+
"demo.minutesLeft": one,
|
|
341
354
|
};
|
package/src/index.ts
CHANGED
|
@@ -545,7 +545,7 @@ export type { CompanySwitcherProps, CompanySwitcherLabels, CompanySwitcherCompan
|
|
|
545
545
|
// invitations panel, and the account's own settings — email change, sessions, deletion,
|
|
546
546
|
// export. Callbacks only; the confirmation level is the server's.
|
|
547
547
|
export { isRateLimited, retryAfterSeconds } from "./auth/auth-errors";
|
|
548
|
-
export type { AccountErrorCode, KitErrorCode } from "./auth/auth-errors";
|
|
548
|
+
export type { AccountErrorCode, DemoErrorCode, KitErrorCode } from "./auth/auth-errors";
|
|
549
549
|
export { EmailChangeSetting, DEFAULT_EMAIL_CHANGE_LABELS } from "./account/email-change-setting";
|
|
550
550
|
export type {
|
|
551
551
|
EmailChangeSettingProps,
|
|
@@ -633,6 +633,97 @@ export type {
|
|
|
633
633
|
InvitationsPanelProps,
|
|
634
634
|
} from "./admin/invitations-panel";
|
|
635
635
|
export type { AdminPerson } from "./admin/admin-parts";
|
|
636
|
+
|
|
637
|
+
// ── 0.31.0: the settings page and the admin page on one shell (docs/settings-harmonization.md) ──
|
|
638
|
+
// Router-aware: the layout, its route and focus hooks (also in `@eifi1/ui-kit/shell`).
|
|
639
|
+
export { SettingsLayout, SETTINGS_SEARCH_THRESHOLD } from "./settings/settings-layout";
|
|
640
|
+
export type { SettingsLayoutProps, SettingsLayoutWidth } from "./settings/settings-layout";
|
|
641
|
+
export {
|
|
642
|
+
useSettingsRoute,
|
|
643
|
+
resolveSettingsLocation,
|
|
644
|
+
settingsFromListState,
|
|
645
|
+
SETTINGS_FROM_LIST_STATE,
|
|
646
|
+
} from "./settings/use-settings-route";
|
|
647
|
+
export type {
|
|
648
|
+
UseSettingsRouteOptions,
|
|
649
|
+
SettingsRoute,
|
|
650
|
+
SettingsLayoutMode,
|
|
651
|
+
SettingsLocation,
|
|
652
|
+
SettingsLocationRules,
|
|
653
|
+
SettingsLocationResult,
|
|
654
|
+
} from "./settings/use-settings-route";
|
|
655
|
+
export { useSettingsFocus, SETTINGS_FOCUS_RING, SETTINGS_FOCUS_MS } from "./settings/use-settings-focus";
|
|
656
|
+
// The router-free companions: a card, the context, the heading level, the catalogue.
|
|
657
|
+
export { SettingsSection } from "./settings/settings-section";
|
|
658
|
+
export type { SettingsSectionProps } from "./settings/settings-section";
|
|
659
|
+
export { useSettingsLayout } from "./settings/settings-context";
|
|
660
|
+
export type { SettingsLayoutContextValue } from "./settings/settings-context";
|
|
661
|
+
export { SettingsHeadingLevel, useSettingsHeadingLevel } from "./settings/settings-heading";
|
|
662
|
+
export type { SettingsHeadingLevelProps, SettingsHeadingTag } from "./settings/settings-heading";
|
|
663
|
+
export {
|
|
664
|
+
settingsSearchEntries,
|
|
665
|
+
visibleSettingsGroups,
|
|
666
|
+
visibleSettingsEntries,
|
|
667
|
+
settingsHref,
|
|
668
|
+
} from "./settings/settings-catalogue";
|
|
669
|
+
export type { SettingsGroup, SettingsEntry, SettingsSearchEntriesOptions } from "./settings/settings-catalogue";
|
|
670
|
+
export { DEFAULT_SETTINGS_LABELS } from "./settings/settings-labels";
|
|
671
|
+
export type { SettingsLabels, SettingsGroupLabels, SettingsCoreGroup } from "./settings/settings-labels";
|
|
672
|
+
// Only the language follows the account (§6.2).
|
|
673
|
+
export { useAccountLanguage, resolveAccountLanguage, matchOfferedLanguage } from "./settings/use-account-language";
|
|
674
|
+
export type {
|
|
675
|
+
UseAccountLanguageOptions,
|
|
676
|
+
AccountLanguage,
|
|
677
|
+
AccountLanguageSource,
|
|
678
|
+
ResolveAccountLanguageInput,
|
|
679
|
+
ResolvedAccountLanguage,
|
|
680
|
+
} from "./settings/use-account-language";
|
|
681
|
+
|
|
682
|
+
// ── 0.31.0: the public landing page and the demo (docs/landing-demo-harmonization.md) ──
|
|
683
|
+
export { DEFAULT_LANDING_LABELS, useLandingLabels } from "./landing/landing-labels";
|
|
684
|
+
export type { LandingLabels } from "./landing/landing-labels";
|
|
685
|
+
export { accessAction, useAccessAction } from "./landing/access";
|
|
686
|
+
export type { AccessChoice, AccessLink, AccessLabels } from "./landing/access";
|
|
687
|
+
export { LandingActions } from "./landing/landing-actions";
|
|
688
|
+
export type { LandingActionsProps, LandingSession } from "./landing/landing-actions";
|
|
689
|
+
export { Hero, FeatureRows, FeatureRow, TrustStrip, CtaBand, PublicFooter } from "./landing/landing-sections";
|
|
690
|
+
export type {
|
|
691
|
+
HeroProps,
|
|
692
|
+
FeatureRowsProps,
|
|
693
|
+
FeatureRowProps,
|
|
694
|
+
TrustItem,
|
|
695
|
+
TrustStripProps,
|
|
696
|
+
CtaBandProps,
|
|
697
|
+
PublicFooterProps,
|
|
698
|
+
} from "./landing/landing-sections";
|
|
699
|
+
export { usePageSeo, seoCopyProblems, metaContent } from "./landing/page-seo";
|
|
700
|
+
export type { SeoCopy, SeoCopyLimits, SeoCopyProblem, SeoCopyProblemCode } from "./landing/page-seo";
|
|
701
|
+
// Router-aware (also in `@eifi1/ui-kit/shell`): the header and the resume.
|
|
702
|
+
export {
|
|
703
|
+
RootEntry,
|
|
704
|
+
RedirectIfAuthed,
|
|
705
|
+
useLastVisitedPage,
|
|
706
|
+
readLastVisitedPage,
|
|
707
|
+
clearLastVisitedPage,
|
|
708
|
+
safeNextPath,
|
|
709
|
+
NEXT_PARAM,
|
|
710
|
+
DEFAULT_LAST_VISITED_EXCLUDES,
|
|
711
|
+
} from "./landing/routing";
|
|
712
|
+
export type { RootEntryProps, RedirectIfAuthedProps, LastVisitedPageOptions, PathPattern } from "./landing/routing";
|
|
713
|
+
export { PublicHeader } from "./landing/public-header";
|
|
714
|
+
export type { PublicHeaderProps, PublicHeaderBrand } from "./landing/public-header";
|
|
715
|
+
export { DEFAULT_DEMO_LABELS, useDemoLabels } from "./demo/demo-labels";
|
|
716
|
+
export type { DemoLabels } from "./demo/demo-labels";
|
|
717
|
+
export { isDemoSession } from "./demo/demo-session";
|
|
718
|
+
export type { DemoModel, DemoSessionUser } from "./demo/demo-session";
|
|
719
|
+
export { useDemoCountdown, demoCountdown, DEMO_WARNING_MINUTES, DEMO_MINUTES_ONLY } from "./demo/demo-countdown";
|
|
720
|
+
export type { DemoCountdown, DemoCountdownOptions, DemoExpiry } from "./demo/demo-countdown";
|
|
721
|
+
export { DemoStart } from "./demo/demo-start";
|
|
722
|
+
export type { DemoStartProps } from "./demo/demo-start";
|
|
723
|
+
export { DemoBanner } from "./demo/demo-banner";
|
|
724
|
+
export type { DemoBannerProps } from "./demo/demo-banner";
|
|
725
|
+
export { DemoEnded } from "./demo/demo-ended";
|
|
726
|
+
export type { DemoEndedProps } from "./demo/demo-ended";
|
|
636
727
|
export type {
|
|
637
728
|
LegalPageProps,
|
|
638
729
|
LegalKitSectionProps,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
import { DEFAULT_LANDING_LABELS, useLandingLabels } from "./landing-labels";
|
|
4
|
+
import type { LandingLabels } from "./landing-labels";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* How a visitor gets an account (docs/landing-demo-harmonization.md §2.2, §4.2).
|
|
8
|
+
*
|
|
9
|
+
* - `request` — every app today: registration is invitation-only, so the primary action
|
|
10
|
+
* is a mail to `support@<domain>`, answered with an invitation. There is no request
|
|
11
|
+
* form and no table. `app` names the product in the mail's subject ("Access to
|
|
12
|
+
* Kastlan"); `askCompany` adds kastlan's "Company:" line to the body.
|
|
13
|
+
* - `register` — the day an app opens registration, the SAME slot becomes "Get started"
|
|
14
|
+
* → its `/register`. Nothing else on the page changes.
|
|
15
|
+
*
|
|
16
|
+
* One value per app, passed to every part that offers access — `PublicHeader`, `Hero`,
|
|
17
|
+
* `CtaBand`, `LandingActions`, `SignInForm`, `DemoStart`, `DemoBanner`, `DemoEnded` — so
|
|
18
|
+
* the switch is one line when it comes.
|
|
19
|
+
*/
|
|
20
|
+
export type AccessChoice =
|
|
21
|
+
| { kind: "request"; email: string; app?: string; askCompany?: boolean }
|
|
22
|
+
| { kind: "register"; href: string };
|
|
23
|
+
|
|
24
|
+
/** What {@link accessAction} answers: where the access action goes and what it says. */
|
|
25
|
+
export interface AccessLink {
|
|
26
|
+
kind: AccessChoice["kind"];
|
|
27
|
+
/** A `mailto:` for `request`, the app's register page for `register`. */
|
|
28
|
+
href: string;
|
|
29
|
+
/** "Request access" or "Get started". */
|
|
30
|
+
label: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The words {@link accessAction} reads. */
|
|
34
|
+
export type AccessLabels = Pick<
|
|
35
|
+
LandingLabels,
|
|
36
|
+
"requestAccess" | "getStarted" | "accessSubject" | "accessName" | "accessCompany" | "accessUse"
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* RFC 3986 percent-encoding, stricter than `encodeURIComponent`: `!'()*` too. RFC 6068
|
|
41
|
+
* would allow them in a header value, but a mail client that splits on `'` or `(` is
|
|
42
|
+
* the one that breaks a subject like "Access to Ada's Garden Planner", and an encoded
|
|
43
|
+
* one reads the same everywhere.
|
|
44
|
+
*/
|
|
45
|
+
function encodeStrict(value: string): string {
|
|
46
|
+
return encodeURIComponent(value).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The `mailto:` of a request (§4.2): the subject in the visitor's language ("Access to
|
|
51
|
+
* <App>") and a short body to fill in — "Name:", kastlan's "Company:", "What would you
|
|
52
|
+
* use it for:" — one prompt per line.
|
|
53
|
+
*
|
|
54
|
+
* RFC 6068: the lines are separated by CRLF (`%0D%0A`), spaces are `%20` (a `+` would be
|
|
55
|
+
* read as a plus), and the address keeps its `@` but nothing else unencoded.
|
|
56
|
+
*/
|
|
57
|
+
function mailtoHref(access: Extract<AccessChoice, { kind: "request" }>, labels: AccessLabels): string {
|
|
58
|
+
const body = [labels.accessName, ...(access.askCompany ? [labels.accessCompany] : []), labels.accessUse].join("\r\n");
|
|
59
|
+
const to = encodeStrict(access.email).replace(/%40/g, "@");
|
|
60
|
+
return `mailto:${to}?subject=${encodeStrict(labels.accessSubject(access.app))}&body=${encodeStrict(body)}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The access action for `access`, in the language of `labels` (§4.2): "Request access"
|
|
65
|
+
* as a mail to support, or "Get started" → the register page.
|
|
66
|
+
*
|
|
67
|
+
* Pure, so a test or an app's own button can use it; the kit's parts call
|
|
68
|
+
* {@link useAccessAction}, which takes the words from the provider. The labels already
|
|
69
|
+
* carry the language — there is no separate locale argument.
|
|
70
|
+
*/
|
|
71
|
+
export function accessAction(access: AccessChoice, labels: Partial<AccessLabels> = {}): AccessLink {
|
|
72
|
+
// A key given as `undefined` keeps the English, as in every kit namespace.
|
|
73
|
+
const words: AccessLabels = { ...DEFAULT_LANDING_LABELS };
|
|
74
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
75
|
+
if (value !== undefined) (words as unknown as Record<string, unknown>)[key] = value;
|
|
76
|
+
}
|
|
77
|
+
if (access.kind === "register") return { kind: "register", href: access.href, label: words.getStarted };
|
|
78
|
+
return { kind: "request", href: mailtoHref(access, words), label: words.requestAccess };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** {@link accessAction} with the `landing` words of the nearest provider (then `labels`). */
|
|
82
|
+
export function useAccessAction(access: AccessChoice, labels?: Partial<LandingLabels>): AccessLink {
|
|
83
|
+
const words = useLandingLabels(labels);
|
|
84
|
+
// `access` is usually an inline object; its fields are what the link depends on.
|
|
85
|
+
const kind = access.kind;
|
|
86
|
+
const target = access.kind === "register" ? access.href : access.email;
|
|
87
|
+
const app = access.kind === "request" ? access.app : undefined;
|
|
88
|
+
const askCompany = access.kind === "request" ? access.askCompany === true : false;
|
|
89
|
+
return useMemo(
|
|
90
|
+
() =>
|
|
91
|
+
accessAction(
|
|
92
|
+
kind === "register" ? { kind, href: target } : { kind, email: target, app, askCompany },
|
|
93
|
+
words,
|
|
94
|
+
),
|
|
95
|
+
[kind, target, app, askCompany, words],
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
import { Button } from "../components/ui";
|
|
5
|
+
import type { ButtonSize } from "../components/ui";
|
|
6
|
+
import { useAccessAction } from "./access";
|
|
7
|
+
import type { AccessChoice } from "./access";
|
|
8
|
+
import { useLandingLabels } from "./landing-labels";
|
|
9
|
+
import type { LandingLabels } from "./landing-labels";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Who is looking at a public page (docs/landing-demo-harmonization.md §3.1, §4.1):
|
|
13
|
+
* nobody signed in, a demo session, or a real account. The app derives it from its auth
|
|
14
|
+
* state — a demo is `isDemoSession(me)`.
|
|
15
|
+
*/
|
|
16
|
+
export type LandingSession = "none" | "demo" | "user";
|
|
17
|
+
|
|
18
|
+
/** keksdose's hero buttons: wider than the kit's `md` (px-5 py-2.5), the page's
|
|
19
|
+
* largest actions. */
|
|
20
|
+
const LARGE = "px-5 py-2.5";
|
|
21
|
+
|
|
22
|
+
export interface LandingActionsProps extends Omit<ComponentPropsWithoutRef<"div">, "children"> {
|
|
23
|
+
session: LandingSession;
|
|
24
|
+
access: AccessChoice;
|
|
25
|
+
/** "Try the demo" without a session, "Continue the demo" with a demo one. Default
|
|
26
|
+
* `/demo`, which starts a demo — or, for a live one, continues it (§3.1). */
|
|
27
|
+
demoHref?: string;
|
|
28
|
+
/** "Open app" for a real session: the resume target. Default `/`, which resumes
|
|
29
|
+
* through `RootEntry`. */
|
|
30
|
+
openAppHref?: string;
|
|
31
|
+
/** `large` (default): the hero's and the CTA band's size. Or a kit `ButtonSize`. */
|
|
32
|
+
size?: ButtonSize | "large";
|
|
33
|
+
/** `start` (default) under the hero's copy; `center` in the CTA band. */
|
|
34
|
+
align?: "start" | "center";
|
|
35
|
+
labels?: Partial<LandingLabels>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The landing's action pair for the visitor's state (§4.1), shared by {@link Hero} and
|
|
40
|
+
* {@link CtaBand}:
|
|
41
|
+
*
|
|
42
|
+
* | session | actions |
|
|
43
|
+
* |---|---|
|
|
44
|
+
* | none | **Request access** (primary), Try the demo |
|
|
45
|
+
* | demo | **Request access** (primary), Continue the demo |
|
|
46
|
+
* | user | **Open app** (primary), alone |
|
|
47
|
+
*
|
|
48
|
+
* keksdose showed a signed-in visitor (who reached the landing by its brand link,
|
|
49
|
+
* `/welcome`) the register/demo pair, which `RedirectIfAuthed` then silently bounced
|
|
50
|
+
* back into the app; one "Open app" says where it goes. "Request access" is the mail of
|
|
51
|
+
* {@link accessAction}, or "Get started" once the app opens registration.
|
|
52
|
+
*
|
|
53
|
+
* Stacked full-width on a phone, a row from `sm` up — keksdose's layout.
|
|
54
|
+
*/
|
|
55
|
+
export function LandingActions({
|
|
56
|
+
session,
|
|
57
|
+
access,
|
|
58
|
+
demoHref = "/demo",
|
|
59
|
+
openAppHref = "/",
|
|
60
|
+
size = "large",
|
|
61
|
+
align = "start",
|
|
62
|
+
labels: labelsProp,
|
|
63
|
+
className,
|
|
64
|
+
...rest
|
|
65
|
+
}: LandingActionsProps) {
|
|
66
|
+
const labels = useLandingLabels(labelsProp);
|
|
67
|
+
const accessLink = useAccessAction(access, labelsProp);
|
|
68
|
+
const buttonSize: ButtonSize = size === "large" ? "md" : size;
|
|
69
|
+
const extra = size === "large" ? LARGE : undefined;
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
{...rest}
|
|
73
|
+
data-slot="landing-actions"
|
|
74
|
+
className={cn("flex flex-col gap-3 sm:flex-row", align === "center" && "sm:justify-center", className)}
|
|
75
|
+
>
|
|
76
|
+
{session === "user" ? (
|
|
77
|
+
<Button href={openAppHref} variant="brand" size={buttonSize} className={extra}>
|
|
78
|
+
{labels.openApp}
|
|
79
|
+
</Button>
|
|
80
|
+
) : (
|
|
81
|
+
<>
|
|
82
|
+
<Button href={accessLink.href} variant="brand" size={buttonSize} className={extra}>
|
|
83
|
+
{accessLink.label}
|
|
84
|
+
</Button>
|
|
85
|
+
<Button href={demoHref} variant="secondary" size={buttonSize} className={extra}>
|
|
86
|
+
{session === "demo" ? labels.continueDemo : labels.tryDemo}
|
|
87
|
+
</Button>
|
|
88
|
+
</>
|
|
89
|
+
)}
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|