@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.
Files changed (254) hide show
  1. package/README.md +42 -5
  2. package/dist/account/data-export-setting.js +3 -2
  3. package/dist/account/data-export-setting.js.map +1 -1
  4. package/dist/account/delete-account-setting.d.ts +5 -1
  5. package/dist/account/delete-account-setting.js +3 -2
  6. package/dist/account/delete-account-setting.js.map +1 -1
  7. package/dist/account/email-change-setting.js +4 -3
  8. package/dist/account/email-change-setting.js.map +1 -1
  9. package/dist/account/sessions-setting.js +3 -2
  10. package/dist/account/sessions-setting.js.map +1 -1
  11. package/dist/admin/admin-action-confirm.d.ts +5 -1
  12. package/dist/admin/invitations-panel.d.ts +5 -1
  13. package/dist/admin/invitations-panel.js +3 -2
  14. package/dist/admin/invitations-panel.js.map +1 -1
  15. package/dist/admin/reviewer-scope-editor.d.ts +5 -1
  16. package/dist/admin/role-select.d.ts +5 -1
  17. package/dist/admin/transfer-ownership-dialog.d.ts +5 -1
  18. package/dist/admin/user-roster.d.ts +5 -1
  19. package/dist/auth/auth-errors.d.ts +24 -6
  20. package/dist/auth/auth-errors.js +7 -1
  21. package/dist/auth/auth-errors.js.map +1 -1
  22. package/dist/auth/register-form.d.ts +5 -1
  23. package/dist/auth/sign-in-form.d.ts +15 -1
  24. package/dist/auth/sign-in-form.js +12 -3
  25. package/dist/auth/sign-in-form.js.map +1 -1
  26. package/dist/chart.d.ts +5 -1
  27. package/dist/components/account-settings-labels.d.ts +12 -0
  28. package/dist/components/account-settings-labels.js +2 -1
  29. package/dist/components/account-settings-labels.js.map +1 -1
  30. package/dist/components/account-settings.d.ts +8 -2
  31. package/dist/components/account-settings.js +19 -11
  32. package/dist/components/account-settings.js.map +1 -1
  33. package/dist/components/amount-input.d.ts +5 -1
  34. package/dist/components/button-group.d.ts +5 -1
  35. package/dist/components/calculator.d.ts +5 -1
  36. package/dist/components/confirm-dialog.d.ts +5 -1
  37. package/dist/components/copy-button.d.ts +5 -1
  38. package/dist/components/danger-confirm.d.ts +5 -1
  39. package/dist/components/data-table.js +8 -1
  40. package/dist/components/data-table.js.map +1 -1
  41. package/dist/components/date-picker.js +9 -4
  42. package/dist/components/date-picker.js.map +1 -1
  43. package/dist/components/facing-pair.d.ts +5 -1
  44. package/dist/components/file-button.d.ts +5 -1
  45. package/dist/components/file-dropzone.d.ts +5 -1
  46. package/dist/components/form-actions.d.ts +5 -1
  47. package/dist/components/iban-input.d.ts +5 -1
  48. package/dist/components/language-select.d.ts +5 -1
  49. package/dist/components/legal-page.d.ts +5 -1
  50. package/dist/components/legal.d.ts +5 -1
  51. package/dist/components/money-field.d.ts +5 -1
  52. package/dist/components/number-field.d.ts +5 -1
  53. package/dist/components/number-input.d.ts +5 -1
  54. package/dist/components/numpad-sheet.d.ts +5 -1
  55. package/dist/components/passkeys-setting.js +2 -1
  56. package/dist/components/passkeys-setting.js.map +1 -1
  57. package/dist/components/phone-input.d.ts +5 -1
  58. package/dist/components/series-chart.d.ts +5 -1
  59. package/dist/components/settings-fields.d.ts +40 -3
  60. package/dist/components/settings-fields.js +20 -1
  61. package/dist/components/settings-fields.js.map +1 -1
  62. package/dist/components/share-card.d.ts +5 -1
  63. package/dist/components/text-link.d.ts +5 -1
  64. package/dist/components/time-input.d.ts +5 -1
  65. package/dist/components/ui.d.ts +5 -1
  66. package/dist/{delete-account-setting-CGVMckMw.d.ts → delete-account-setting-DCFS5Vof.d.ts} +52 -23
  67. package/dist/demo/demo-banner.d.ts +51 -0
  68. package/dist/demo/demo-banner.js +97 -0
  69. package/dist/demo/demo-banner.js.map +1 -0
  70. package/dist/demo/demo-countdown.d.ts +58 -0
  71. package/dist/demo/demo-countdown.js +79 -0
  72. package/dist/demo/demo-countdown.js.map +1 -0
  73. package/dist/demo/demo-ended.d.ts +45 -0
  74. package/dist/demo/demo-ended.js +42 -0
  75. package/dist/demo/demo-ended.js.map +1 -0
  76. package/dist/demo/demo-labels.d.ts +53 -0
  77. package/dist/demo/demo-labels.js +29 -0
  78. package/dist/demo/demo-labels.js.map +1 -0
  79. package/dist/demo/demo-session.d.ts +30 -0
  80. package/dist/demo/demo-session.js +8 -0
  81. package/dist/demo/demo-session.js.map +1 -0
  82. package/dist/demo/demo-start.d.ts +54 -0
  83. package/dist/demo/demo-start.js +68 -0
  84. package/dist/demo/demo-start.js.map +1 -0
  85. package/dist/feedback/feedback-attachment.d.ts +5 -1
  86. package/dist/feedback/feedback-context.d.ts +5 -1
  87. package/dist/feedback/feedback-crash.d.ts +5 -1
  88. package/dist/feedback/feedback-dialog.d.ts +5 -1
  89. package/dist/feedback/feedback-inbox.d.ts +5 -1
  90. package/dist/feedback/feedback-inbox.js +12 -2
  91. package/dist/feedback/feedback-inbox.js.map +1 -1
  92. package/dist/feedback/feedback-labels.d.ts +5 -1
  93. package/dist/feedback/feedback-labels.js +1 -0
  94. package/dist/feedback/feedback-labels.js.map +1 -1
  95. package/dist/feedback/feedback-menu.d.ts +5 -1
  96. package/dist/feedback/feedback-record.d.ts +5 -1
  97. package/dist/feedback/feedback-record.js +2 -0
  98. package/dist/feedback/feedback-record.js.map +1 -1
  99. package/dist/feedback/feedback-row-detail.d.ts +5 -1
  100. package/dist/feedback/feedback-row-detail.js +7 -0
  101. package/dist/feedback/feedback-row-detail.js.map +1 -1
  102. package/dist/feedback/feedback-status-undo.d.ts +5 -1
  103. package/dist/feedback/feedback-submit.d.ts +5 -1
  104. package/dist/feedback/feedback-swipe.d.ts +6 -2
  105. package/dist/feedback/feedback-swipe.js.map +1 -1
  106. package/dist/feedback/feedback-table.d.ts +5 -1
  107. package/dist/feedback/feedback-thread.d.ts +5 -1
  108. package/dist/feedback.d.ts +5 -1
  109. package/dist/hooks/use-file-drop.d.ts +5 -1
  110. package/dist/i18n/defaults.d.ts +5 -1
  111. package/dist/i18n/defaults.js +6 -0
  112. package/dist/i18n/defaults.js.map +1 -1
  113. package/dist/i18n/german.d.ts +5 -1
  114. package/dist/i18n/german.js +84 -2
  115. package/dist/i18n/german.js.map +1 -1
  116. package/dist/i18n/kit-labels.d.ts +5 -1
  117. package/dist/i18n/kit-labels.js.map +1 -1
  118. package/dist/i18n/languages.d.ts +5 -1
  119. package/dist/i18n/locales/de-CH.d.ts +5 -1
  120. package/dist/i18n/locales/en.d.ts +5 -1
  121. package/dist/i18n/locales/es.d.ts +5 -1
  122. package/dist/i18n/locales/es.js +81 -2
  123. package/dist/i18n/locales/es.js.map +1 -1
  124. package/dist/i18n/locales/fr.d.ts +5 -1
  125. package/dist/i18n/locales/fr.js +82 -2
  126. package/dist/i18n/locales/fr.js.map +1 -1
  127. package/dist/i18n/locales/hu.d.ts +5 -1
  128. package/dist/i18n/locales/hu.js +82 -2
  129. package/dist/i18n/locales/hu.js.map +1 -1
  130. package/dist/i18n/locales/it.d.ts +5 -1
  131. package/dist/i18n/locales/it.js +81 -2
  132. package/dist/i18n/locales/it.js.map +1 -1
  133. package/dist/i18n/locales/zh.d.ts +5 -1
  134. package/dist/i18n/locales/zh.js +80 -2
  135. package/dist/i18n/locales/zh.js.map +1 -1
  136. package/dist/i18n/review.d.ts +5 -1
  137. package/dist/i18n/review.js +14 -1
  138. package/dist/i18n/review.js.map +1 -1
  139. package/dist/index.d.ts +25 -3
  140. package/dist/index.js +95 -0
  141. package/dist/index.js.map +1 -1
  142. package/dist/landing/access.d.ts +48 -0
  143. package/dist/landing/access.js +38 -0
  144. package/dist/landing/access.js.map +1 -0
  145. package/dist/landing/landing-actions.d.ts +138 -0
  146. package/dist/landing/landing-actions.js +39 -0
  147. package/dist/landing/landing-actions.js.map +1 -0
  148. package/dist/landing/landing-labels.d.ts +43 -0
  149. package/dist/landing/landing-labels.js +23 -0
  150. package/dist/landing/landing-labels.js.map +1 -0
  151. package/dist/landing/landing-sections.d.ts +218 -0
  152. package/dist/landing/landing-sections.js +167 -0
  153. package/dist/landing/landing-sections.js.map +1 -0
  154. package/dist/landing/page-seo.d.ts +99 -0
  155. package/dist/landing/page-seo.js +121 -0
  156. package/dist/landing/page-seo.js.map +1 -0
  157. package/dist/landing/public-header.d.ts +148 -0
  158. package/dist/landing/public-header.js +49 -0
  159. package/dist/landing/public-header.js.map +1 -0
  160. package/dist/landing/routing.d.ts +218 -0
  161. package/dist/landing/routing.js +81 -0
  162. package/dist/landing/routing.js.map +1 -0
  163. package/dist/rhf/fields.d.ts +5 -1
  164. package/dist/rhf/form.d.ts +5 -1
  165. package/dist/rhf.d.ts +5 -1
  166. package/dist/settings/settings-catalogue.d.ts +84 -0
  167. package/dist/settings/settings-catalogue.js +47 -0
  168. package/dist/settings/settings-catalogue.js.map +1 -0
  169. package/dist/settings/settings-context.d.ts +31 -0
  170. package/dist/settings/settings-context.js +11 -0
  171. package/dist/settings/settings-context.js.map +1 -0
  172. package/dist/settings/settings-heading.d.ts +50 -0
  173. package/dist/settings/settings-heading.js +27 -0
  174. package/dist/settings/settings-heading.js.map +1 -0
  175. package/dist/settings/settings-labels.d.ts +41 -0
  176. package/dist/settings/settings-labels.js +38 -0
  177. package/dist/settings/settings-labels.js.map +1 -0
  178. package/dist/settings/settings-layout.d.ts +77 -0
  179. package/dist/settings/settings-layout.js +242 -0
  180. package/dist/settings/settings-layout.js.map +1 -0
  181. package/dist/settings/settings-section.d.ts +133 -0
  182. package/dist/settings/settings-section.js +63 -0
  183. package/dist/settings/settings-section.js.map +1 -0
  184. package/dist/settings/use-account-language.d.ts +83 -0
  185. package/dist/settings/use-account-language.js +62 -0
  186. package/dist/settings/use-account-language.js.map +1 -0
  187. package/dist/settings/use-settings-focus.d.ts +34 -0
  188. package/dist/settings/use-settings-focus.js +77 -0
  189. package/dist/settings/use-settings-focus.js.map +1 -0
  190. package/dist/settings/use-settings-route.d.ts +118 -0
  191. package/dist/settings/use-settings-route.js +120 -0
  192. package/dist/settings/use-settings-route.js.map +1 -0
  193. package/dist/shell/app-shell.d.ts +11 -1
  194. package/dist/shell/app-shell.js +3 -2
  195. package/dist/shell/app-shell.js.map +1 -1
  196. package/dist/shell/top-bar-brand.d.ts +5 -1
  197. package/dist/shell.d.ts +15 -1
  198. package/dist/shell.js +56 -0
  199. package/dist/shell.js.map +1 -1
  200. package/dist/wizard/stepper-nav.d.ts +5 -1
  201. package/dist/wizard.d.ts +5 -1
  202. package/package.json +2 -1
  203. package/src/account/data-export-setting.tsx +3 -2
  204. package/src/account/delete-account-setting.tsx +3 -2
  205. package/src/account/email-change-setting.tsx +4 -7
  206. package/src/account/sessions-setting.tsx +3 -2
  207. package/src/admin/invitations-panel.tsx +11 -4
  208. package/src/auth/auth-errors.ts +36 -5
  209. package/src/auth/sign-in-form.tsx +34 -3
  210. package/src/components/account-settings-labels.ts +13 -0
  211. package/src/components/account-settings.tsx +33 -12
  212. package/src/components/data-table.tsx +8 -0
  213. package/src/components/date-picker.tsx +9 -4
  214. package/src/components/passkeys-setting.tsx +2 -1
  215. package/src/components/settings-fields.tsx +50 -1
  216. package/src/demo/demo-banner.tsx +159 -0
  217. package/src/demo/demo-countdown.ts +141 -0
  218. package/src/demo/demo-ended.tsx +89 -0
  219. package/src/demo/demo-labels.ts +78 -0
  220. package/src/demo/demo-session.ts +32 -0
  221. package/src/demo/demo-start.tsx +150 -0
  222. package/src/feedback/feedback-inbox.tsx +21 -8
  223. package/src/feedback/feedback-labels.ts +2 -1
  224. package/src/feedback/feedback-record.ts +10 -7
  225. package/src/feedback/feedback-row-detail.tsx +28 -4
  226. package/src/feedback/feedback-swipe.tsx +1 -1
  227. package/src/i18n/defaults.ts +6 -0
  228. package/src/i18n/german.ts +86 -0
  229. package/src/i18n/kit-labels.tsx +9 -0
  230. package/src/i18n/locales/es.ts +83 -0
  231. package/src/i18n/locales/fr.ts +84 -0
  232. package/src/i18n/locales/hu.ts +84 -0
  233. package/src/i18n/locales/it.ts +83 -0
  234. package/src/i18n/locales/zh.ts +81 -0
  235. package/src/i18n/review.ts +13 -0
  236. package/src/index.ts +92 -1
  237. package/src/landing/access.ts +97 -0
  238. package/src/landing/landing-actions.tsx +92 -0
  239. package/src/landing/landing-labels.ts +59 -0
  240. package/src/landing/landing-sections.tsx +337 -0
  241. package/src/landing/page-seo.ts +251 -0
  242. package/src/landing/public-header.tsx +120 -0
  243. package/src/landing/routing.tsx +209 -0
  244. package/src/settings/settings-catalogue.ts +132 -0
  245. package/src/settings/settings-context.ts +33 -0
  246. package/src/settings/settings-heading.tsx +65 -0
  247. package/src/settings/settings-labels.ts +75 -0
  248. package/src/settings/settings-layout.tsx +379 -0
  249. package/src/settings/settings-section.tsx +108 -0
  250. package/src/settings/use-account-language.ts +146 -0
  251. package/src/settings/use-settings-focus.ts +120 -0
  252. package/src/settings/use-settings-route.ts +258 -0
  253. package/src/shell/app-shell.tsx +10 -2
  254. package/src/shell.ts +45 -0
@@ -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
 
@@ -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
 
@@ -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
 
@@ -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
+ }