@eifi1/ui-kit 0.42.0 → 0.44.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 +13 -7
- package/dist/admin/admin-area-labels.d.ts +4 -0
- package/dist/admin/admin-area-labels.js +3 -1
- package/dist/admin/admin-area-labels.js.map +1 -1
- package/dist/admin/admin-overview.d.ts +25 -8
- package/dist/admin/admin-overview.js +26 -3
- package/dist/admin/admin-overview.js.map +1 -1
- package/dist/admin/admin-sections.d.ts +36 -5
- package/dist/admin/admin-sections.js +35 -18
- package/dist/admin/admin-sections.js.map +1 -1
- package/dist/billing/billing-banner.d.ts +8 -0
- package/dist/billing/billing-banner.js +25 -2
- package/dist/billing/billing-banner.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +4 -0
- package/dist/billing/billing-labels.js +1 -0
- package/dist/billing/billing-labels.js.map +1 -1
- package/dist/billing/cancel-request.d.ts +53 -0
- package/dist/billing/cancel-request.js +21 -0
- package/dist/billing/cancel-request.js.map +1 -0
- package/dist/billing/provider-page.d.ts +49 -0
- package/dist/billing/provider-page.js +33 -0
- package/dist/billing/provider-page.js.map +1 -0
- package/dist/billing/subscription-actions.d.ts +32 -1
- package/dist/billing/subscription-actions.js +57 -4
- package/dist/billing/subscription-actions.js.map +1 -1
- package/dist/components/account-chips.d.ts +8 -1
- package/dist/components/account-chips.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/feedback/feedback-menu.d.ts +7 -4
- package/dist/feedback/feedback-menu.js +4 -2
- package/dist/feedback/feedback-menu.js.map +1 -1
- package/dist/hooks/use-search-param-state.js +4 -1
- package/dist/hooks/use-search-param-state.js.map +1 -1
- package/dist/i18n/defaults.js +2 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +36 -2
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +3 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +51 -2
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +36 -2
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +36 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +36 -2
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +36 -2
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +36 -2
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +15 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/search/global-search.d.ts +3 -0
- package/dist/search/global-search.js +3 -1
- package/dist/search/global-search.js.map +1 -1
- package/dist/settings/settings-catalogue.d.ts +9 -1
- package/dist/settings/settings-catalogue.js +1 -1
- package/dist/settings/settings-catalogue.js.map +1 -1
- package/dist/shell/admin-menu.d.ts +26 -3
- package/dist/shell/admin-menu.js +8 -4
- package/dist/shell/admin-menu.js.map +1 -1
- package/dist/shell/dev-switchers.js +5 -0
- package/dist/shell/dev-switchers.js.map +1 -1
- package/dist/shell/top-bar-parts-labels.d.ts +6 -0
- package/dist/shell/top-bar-parts-labels.js +4 -1
- package/dist/shell/top-bar-parts-labels.js.map +1 -1
- package/dist/shell/tour-menu.d.ts +19 -10
- package/dist/shell/tour-menu.js +20 -6
- package/dist/shell/tour-menu.js.map +1 -1
- package/dist/shell.d.ts +2 -0
- package/dist/shell.js +2 -0
- package/dist/shell.js.map +1 -1
- package/dist/tour/tour-catalogue-labels.d.ts +44 -0
- package/dist/tour/tour-catalogue-labels.js +32 -0
- package/dist/tour/tour-catalogue-labels.js.map +1 -0
- package/dist/tour/tour-catalogue.d.ts +68 -0
- package/dist/tour/tour-catalogue.js +189 -0
- package/dist/tour/tour-catalogue.js.map +1 -0
- package/dist/tour/tour-info.d.ts +112 -0
- package/dist/tour/tour-info.js +36 -0
- package/dist/tour/tour-info.js.map +1 -0
- package/dist/tour/tour.d.ts +38 -2
- package/dist/tour/tour.js +40 -3
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour/use-tour-progress.d.ts +116 -0
- package/dist/tour/use-tour-progress.js +146 -0
- package/dist/tour/use-tour-progress.js.map +1 -0
- package/dist/tour/use-tour-url-sync.d.ts +58 -0
- package/dist/tour/use-tour-url-sync.js +110 -0
- package/dist/tour/use-tour-url-sync.js.map +1 -0
- package/dist/tour.d.ts +8 -0
- package/dist/tour.js +19 -0
- package/dist/tour.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/admin-area-labels.ts +7 -1
- package/src/admin/admin-overview.tsx +61 -10
- package/src/admin/admin-sections.tsx +109 -48
- package/src/billing/billing-banner.tsx +25 -2
- package/src/billing/billing-labels.ts +6 -0
- package/src/billing/cancel-request.ts +75 -0
- package/src/billing/provider-page.ts +84 -0
- package/src/billing/subscription-actions.tsx +97 -11
- package/src/components/account-chips.tsx +8 -1
- package/src/feedback/feedback-menu.tsx +8 -3
- package/src/hooks/use-search-param-state.ts +6 -1
- package/src/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +36 -1
- package/src/i18n/kit-labels.tsx +3 -0
- package/src/i18n/locales/en.ts +13 -0
- package/src/i18n/locales/es.ts +36 -1
- package/src/i18n/locales/fr.ts +36 -1
- package/src/i18n/locales/hu.ts +36 -1
- package/src/i18n/locales/it.ts +36 -1
- package/src/i18n/locales/zh.ts +36 -1
- package/src/i18n/review.ts +14 -0
- package/src/index.ts +8 -0
- package/src/search/global-search.tsx +5 -0
- package/src/settings/settings-catalogue.ts +10 -2
- package/src/shell/admin-menu.tsx +34 -5
- package/src/shell/dev-switchers.tsx +8 -0
- package/src/shell/top-bar-parts-labels.ts +9 -0
- package/src/shell/tour-menu.tsx +50 -15
- package/src/shell.ts +3 -0
- package/src/tour/tour-catalogue-labels.ts +75 -0
- package/src/tour/tour-catalogue.tsx +316 -0
- package/src/tour/tour-info.ts +144 -0
- package/src/tour/tour.tsx +84 -3
- package/src/tour/use-tour-progress.ts +299 -0
- package/src/tour/use-tour-url-sync.ts +221 -0
- package/src/tour.ts +12 -0
package/src/i18n/locales/it.ts
CHANGED
|
@@ -613,6 +613,34 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
613
613
|
awaitClickHint: "Faccia clic sull’elemento evidenziato per continuare",
|
|
614
614
|
step: (current, total) => `${n(current)} / ${n(total)}`,
|
|
615
615
|
},
|
|
616
|
+
// 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page. keksdose's reviewed words.
|
|
617
|
+
tourCatalogue: {
|
|
618
|
+
searchLabel: "Cerca tour",
|
|
619
|
+
searchPlaceholder: "Cerca tour…",
|
|
620
|
+
groupLabel: "Argomento",
|
|
621
|
+
groupAll: (count) => `Tutti gli argomenti (${n(count)})`,
|
|
622
|
+
groupOption: (group, count) => `${group} (${n(count)})`,
|
|
623
|
+
stateLabel: "Filtra i tour",
|
|
624
|
+
stateAll: (count) => `Tutti (${n(count)})`,
|
|
625
|
+
stateTodo: (count) => `Da fare (${n(count)})`,
|
|
626
|
+
stateDone: (count) => `Completati (${n(count)})`,
|
|
627
|
+
showing: (shown, total) => `${n(shown)} di ${n(total)} tour`,
|
|
628
|
+
noMatches: "Nessun tour corrisponde",
|
|
629
|
+
noMatchesHint: "Provi un’altra parola o cancelli il filtro.",
|
|
630
|
+
clearFilters: "Cancella filtri",
|
|
631
|
+
badgeNew: "Nuovo",
|
|
632
|
+
badgeUpdated: "Aggiornato",
|
|
633
|
+
resetOn: (date, reason) => (reason ? `Reimpostato il ${date}: ${reason}` : `Reimpostato il ${date}`),
|
|
634
|
+
points: (count) => `+${n(count)} pt`,
|
|
635
|
+
steps: (count) => (count === 1 ? `${n(count)} passaggio` : `${n(count)} passaggi`),
|
|
636
|
+
duration: (minutes) => `Circa ${n(minutes)} min`,
|
|
637
|
+
completedOn: (date) => `Completato il ${date}`,
|
|
638
|
+
completed: "Completato",
|
|
639
|
+
notCompleted: "Non completato",
|
|
640
|
+
markUndone: "Segna come non completato",
|
|
641
|
+
start: "Avvia",
|
|
642
|
+
replay: "Ripeti",
|
|
643
|
+
},
|
|
616
644
|
commandPalette: {
|
|
617
645
|
clear: "Cancella ricerca",
|
|
618
646
|
submit: "Cerca",
|
|
@@ -1744,6 +1772,8 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1744
1772
|
`Il Suo abbonamento termina il ${date}. Fino ad allora tutto funziona come prima.`,
|
|
1745
1773
|
cancelConfirmAction: "Disdici ora",
|
|
1746
1774
|
keepSubscription: "Mantieni l’abbonamento",
|
|
1775
|
+
portalOpensNewTab: (provider, app) =>
|
|
1776
|
+
`Pagamenti e fatture si aprono su ${provider} in una nuova scheda; ${app ? `${app} resta aperto qui` : "questa pagina resta aperta"}.`,
|
|
1747
1777
|
grantWarningTitle: "Il Suo accesso gratuito continua",
|
|
1748
1778
|
grantWarningBody:
|
|
1749
1779
|
"Il Suo accesso gratuito non ha una data di fine. Finché dura, un acquisto non cambia il Suo piano: pagherebbe per un piano che non si applica.",
|
|
@@ -1831,7 +1861,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1831
1861
|
sections: {
|
|
1832
1862
|
overview: { title: "Panoramica", help: "Ciò che è in attesa, a colpo d’occhio." },
|
|
1833
1863
|
users: { title: "Utenti", help: "Chi ha un account e cosa può fare ciascuno." },
|
|
1834
|
-
invitations: { title: "Inviti", help: "
|
|
1864
|
+
invitations: { title: "Inviti", help: "Inviti persone via e-mail nella loro lingua; reinvii o revochi gli inviti ancora aperti." },
|
|
1835
1865
|
billing: { title: "Fatturazione", help: "Il piano, il pagamento e le fatture." },
|
|
1836
1866
|
activity: { title: "Attività", help: "Ciò che hanno fatto gli amministratori, dal più recente." },
|
|
1837
1867
|
},
|
|
@@ -1842,6 +1872,8 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1842
1872
|
users: "Utenti",
|
|
1843
1873
|
usersNew: (count) => `${n(count)} ${plural(count, "nuovo", "nuovi")} in 7 giorni`,
|
|
1844
1874
|
},
|
|
1875
|
+
overviewError: "Non è stato possibile caricare la panoramica.",
|
|
1876
|
+
retry: "Riprova",
|
|
1845
1877
|
tools: "Strumenti",
|
|
1846
1878
|
},
|
|
1847
1879
|
// 0.42.0 — docs/top-bar-harmonization.md §3.1: the top bar's shared parts, keksdose's
|
|
@@ -1855,6 +1887,9 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1855
1887
|
allTours: "Tutti i tour…",
|
|
1856
1888
|
badgeNew: "Nuovo",
|
|
1857
1889
|
completed: "Completato",
|
|
1890
|
+
badgeUpdated: "Aggiornato",
|
|
1891
|
+
updatedDot: "Tour aggiornati",
|
|
1892
|
+
newAndUpdated: "Nuovi e aggiornati",
|
|
1858
1893
|
},
|
|
1859
1894
|
sync: {
|
|
1860
1895
|
blocked: (count) =>
|
package/src/i18n/locales/zh.ts
CHANGED
|
@@ -592,6 +592,34 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
592
592
|
awaitClickHint: "点击高亮的元素以继续",
|
|
593
593
|
step: (current, total) => `${n(current)} / ${n(total)}`,
|
|
594
594
|
},
|
|
595
|
+
// 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page.
|
|
596
|
+
tourCatalogue: {
|
|
597
|
+
searchLabel: "搜索导览",
|
|
598
|
+
searchPlaceholder: "搜索导览…",
|
|
599
|
+
groupLabel: "主题",
|
|
600
|
+
groupAll: (count) => `全部主题(${n(count)})`,
|
|
601
|
+
groupOption: (group, count) => `${group}(${n(count)})`,
|
|
602
|
+
stateLabel: "筛选导览",
|
|
603
|
+
stateAll: (count) => `全部(${n(count)})`,
|
|
604
|
+
stateTodo: (count) => `待完成(${n(count)})`,
|
|
605
|
+
stateDone: (count) => `已完成(${n(count)})`,
|
|
606
|
+
showing: (shown, total) => `显示 ${n(shown)} / ${n(total)} 个导览`,
|
|
607
|
+
noMatches: "没有匹配的导览",
|
|
608
|
+
noMatchesHint: "请换个关键词,或清除筛选条件。",
|
|
609
|
+
clearFilters: "清除筛选",
|
|
610
|
+
badgeNew: "新",
|
|
611
|
+
badgeUpdated: "已更新",
|
|
612
|
+
resetOn: (date, reason) => (reason ? `已于 ${date} 重置:${reason}` : `已于 ${date} 重置`),
|
|
613
|
+
points: (count) => `+${n(count)} 分`,
|
|
614
|
+
steps: (count) => `${n(count)} 步`,
|
|
615
|
+
duration: (minutes) => `约 ${n(minutes)} 分钟`,
|
|
616
|
+
completedOn: (date) => `完成于 ${date}`,
|
|
617
|
+
completed: "已完成",
|
|
618
|
+
notCompleted: "未完成",
|
|
619
|
+
markUndone: "标记为未完成",
|
|
620
|
+
start: "开始",
|
|
621
|
+
replay: "再来一次",
|
|
622
|
+
},
|
|
595
623
|
commandPalette: {
|
|
596
624
|
clear: "清除搜索",
|
|
597
625
|
submit: "搜索",
|
|
@@ -1655,6 +1683,8 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1655
1683
|
cancelConfirmBody: (date) => `您的订阅将于 ${date} 结束。在此之前,一切照常使用。`,
|
|
1656
1684
|
cancelConfirmAction: "立即取消订阅",
|
|
1657
1685
|
keepSubscription: "保留订阅",
|
|
1686
|
+
portalOpensNewTab: (provider, app) =>
|
|
1687
|
+
`付款和发票将在新标签页中于 ${provider} 打开;${app ? `${app} 仍在此保持打开` : "本页面仍保持打开"}。`,
|
|
1658
1688
|
grantWarningTitle: "您的免费使用权仍然有效",
|
|
1659
1689
|
grantWarningBody: "您的免费使用权没有结束日期。在此期间,购买不会改变您的套餐:您将为一个不生效的套餐付费。",
|
|
1660
1690
|
grantWarningAction: "仍然购买",
|
|
@@ -1734,7 +1764,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1734
1764
|
sections: {
|
|
1735
1765
|
overview: { title: "概览", help: "一眼看清待处理的事项。" },
|
|
1736
1766
|
users: { title: "用户", help: "谁拥有账户,以及每个人可以做什么。" },
|
|
1737
|
-
invitations: { title: "邀请", help: "
|
|
1767
|
+
invitations: { title: "邀请", help: "通过电子邮件以对方的语言邀请他人;可重新发送或撤销尚未处理的邀请。" },
|
|
1738
1768
|
billing: { title: "账单", help: "套餐、付款和发票。" },
|
|
1739
1769
|
activity: { title: "活动", help: "管理员的操作记录,最新的在前。" },
|
|
1740
1770
|
},
|
|
@@ -1745,6 +1775,8 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1745
1775
|
users: "用户",
|
|
1746
1776
|
usersNew: (count) => `近 7 天新增 ${n(count)} 人`,
|
|
1747
1777
|
},
|
|
1778
|
+
overviewError: "无法加载概览。",
|
|
1779
|
+
retry: "重试",
|
|
1748
1780
|
tools: "工具",
|
|
1749
1781
|
},
|
|
1750
1782
|
// 0.42.0 — docs/top-bar-harmonization.md §3.1: the top bar's shared parts; "套餐" as
|
|
@@ -1758,6 +1790,9 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1758
1790
|
allTours: "全部导览…",
|
|
1759
1791
|
badgeNew: "新",
|
|
1760
1792
|
completed: "已完成",
|
|
1793
|
+
badgeUpdated: "已更新",
|
|
1794
|
+
updatedDot: "有更新的导览",
|
|
1795
|
+
newAndUpdated: "新增和更新",
|
|
1761
1796
|
},
|
|
1762
1797
|
sync: {
|
|
1763
1798
|
blocked: (count) => `${n(count)} 项修改无法从此设备发送`,
|
package/src/i18n/review.ts
CHANGED
|
@@ -397,6 +397,7 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
|
|
|
397
397
|
"billing.buyerHolds": [["{{buyer}}"], [undefined]],
|
|
398
398
|
"billing.paymentFailedAskBuyer": [["{{buyer}}"], [undefined]],
|
|
399
399
|
"billing.cancelConfirmBody": [["{{date}}"]],
|
|
400
|
+
"billing.portalOpensNewTab": [["{{provider}}", "{{app}}"], ["{{provider}}", undefined]],
|
|
400
401
|
"planChange.current": [["{{plan}}"]],
|
|
401
402
|
"planChange.changed": [["{{from}}", "{{to}}"]],
|
|
402
403
|
"planChange.set": [["{{to}}"]],
|
|
@@ -417,4 +418,17 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
|
|
|
417
418
|
"topBarParts.sync.waitingShort": one,
|
|
418
419
|
"topBarParts.dev.plan": [["{{plan}}"]],
|
|
419
420
|
"topBarParts.dev.backTo": [["{{role}}"]],
|
|
421
|
+
// 0.43.0 (docs/tours-harmonization.md §3, §8): the tours page's counts, its group
|
|
422
|
+
// option, the completion day and a reset's day and reason.
|
|
423
|
+
"tourCatalogue.groupAll": one,
|
|
424
|
+
"tourCatalogue.groupOption": [["{{group}}", 1], ["{{group}}", 3]],
|
|
425
|
+
"tourCatalogue.stateAll": one,
|
|
426
|
+
"tourCatalogue.stateTodo": one,
|
|
427
|
+
"tourCatalogue.stateDone": one,
|
|
428
|
+
"tourCatalogue.showing": [[3, 16]],
|
|
429
|
+
"tourCatalogue.resetOn": [["{{date}}", "{{reason}}"]],
|
|
430
|
+
"tourCatalogue.points": one,
|
|
431
|
+
"tourCatalogue.steps": one,
|
|
432
|
+
"tourCatalogue.duration": one,
|
|
433
|
+
"tourCatalogue.completedOn": [["{{date}}"]],
|
|
420
434
|
};
|
package/src/index.ts
CHANGED
|
@@ -796,6 +796,9 @@ export { DEFAULT_ADMIN_AREA_LABELS, useAdminAreaLabels } from "./admin/admin-are
|
|
|
796
796
|
export type { AdminAreaLabels, AdminTileLabels, AdminSharedSection } from "./admin/admin-area-labels";
|
|
797
797
|
export { ADMIN_SECTIONS, adminSections } from "./admin/admin-sections";
|
|
798
798
|
export type { AdminSectionDefinition, AdminOwnSection, AdminSectionsOptions } from "./admin/admin-sections";
|
|
799
|
+
// 0.43.0 (keksdose): the section order as ids, and the narrowed `leaveOut`.
|
|
800
|
+
export { adminSectionOrder } from "./admin/admin-sections";
|
|
801
|
+
export type { AdminSectionPlace, AdminSectionOrderOptions, AdminKeptSection } from "./admin/admin-sections";
|
|
799
802
|
export { AdminForbidden } from "./admin/admin-forbidden";
|
|
800
803
|
export type { AdminForbiddenProps } from "./admin/admin-forbidden";
|
|
801
804
|
export { AdminOverview, AttentionTile, adminWaitingCount, DEFAULT_ADMIN_ATTENTION_HREFS } from "./admin/admin-overview";
|
|
@@ -883,6 +886,11 @@ export { isPermanentGrant } from "./billing/billing-overview";
|
|
|
883
886
|
export type { BillingPortalAccess } from "./billing/billing-overview";
|
|
884
887
|
export { CancelSubscriptionConfirm } from "./billing/cancel-subscription";
|
|
885
888
|
export type { CancelSubscriptionConfirmProps } from "./billing/cancel-subscription";
|
|
889
|
+
// 0.43 (§17): the provider's portal in a new tab; the in-app cancel's follow-up.
|
|
890
|
+
export { openProviderPage } from "./billing/provider-page";
|
|
891
|
+
export type { OpenProviderPageOptions, ProviderPageWay } from "./billing/provider-page";
|
|
892
|
+
export { useCancelRequest } from "./billing/cancel-request";
|
|
893
|
+
export type { CancelRequest, CancelRequested, CancelRequestView } from "./billing/cancel-request";
|
|
886
894
|
// The operator's plan parts (§14.12) are admin/plan-change, in the admin section.
|
|
887
895
|
|
|
888
896
|
// ── settings ─────────────────────────────────────────────────────────────────
|
|
@@ -188,6 +188,9 @@ export interface GlobalSearchProps {
|
|
|
188
188
|
labels?: LabelOverride<GlobalSearchLabels>;
|
|
189
189
|
/** Passed through to the palette (`empty`, `loading`, `error`, `hint`, …). */
|
|
190
190
|
paletteLabels?: Partial<CommandPaletteLabels>;
|
|
191
|
+
/** 0.43: `data-*` attributes go on the kit's trigger button (`trigger="button"`) — a
|
|
192
|
+
* tour's anchor (`data-tour="search"`), a test hook. A function trigger is the app's. */
|
|
193
|
+
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
function isApplePlatform(): boolean {
|
|
@@ -290,6 +293,7 @@ function GlobalSearchImpl({
|
|
|
290
293
|
density,
|
|
291
294
|
labels,
|
|
292
295
|
paletteLabels,
|
|
296
|
+
...attributes
|
|
293
297
|
}: GlobalSearchProps & { navigate: (href: string) => void; hrefFor: (href: string) => string }) {
|
|
294
298
|
const l = useKitLabels("globalSearch", DEFAULT_GLOBAL_SEARCH_LABELS, labels);
|
|
295
299
|
const pl = useKitLabels("commandPalette", DEFAULT_COMMAND_PALETTE_LABELS, paletteLabels);
|
|
@@ -563,6 +567,7 @@ function GlobalSearchImpl({
|
|
|
563
567
|
className={hideTriggerOnPhone ? "hidden sm:block" : undefined}
|
|
564
568
|
>
|
|
565
569
|
<button
|
|
570
|
+
{...Object.fromEntries(Object.entries(attributes).filter(([k]) => k.startsWith("data-")))}
|
|
566
571
|
type="button"
|
|
567
572
|
onClick={openSearch}
|
|
568
573
|
aria-label={triggerLabel}
|
|
@@ -29,6 +29,13 @@ export interface SettingsGroup<G extends string = string> {
|
|
|
29
29
|
* kastlan's `/admin/billing` while billing is off. Default: visible.
|
|
30
30
|
*/
|
|
31
31
|
visible?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* 0.43: the group shows with no visible entry — a page of its own rather than of setting
|
|
34
|
+
* cards (the admin area's Overview, whose tiles are no settings; `adminSections` sets
|
|
35
|
+
* it). `visible: false` still hides it. Default: a group shows only with an entry once
|
|
36
|
+
* `entries` are given (§4.1).
|
|
37
|
+
*/
|
|
38
|
+
standalone?: boolean;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
/** One setting, and the card that holds it (§4.1). */
|
|
@@ -57,7 +64,8 @@ export interface SettingsEntry<G extends string = string> {
|
|
|
57
64
|
/**
|
|
58
65
|
* The groups that show, in their order: not `visible: false`, and — when `entries` is
|
|
59
66
|
* given — holding at least one visible entry (§4.1: Kurvenschmiede's notifications group
|
|
60
|
-
* stays hidden until it has a card). Without `entries` only the
|
|
67
|
+
* stays hidden until it has a card) or `standalone` (0.43). Without `entries` only the
|
|
68
|
+
* groups' own flag counts.
|
|
61
69
|
*/
|
|
62
70
|
export function visibleSettingsGroups<G extends string>(
|
|
63
71
|
groups: readonly SettingsGroup<G>[],
|
|
@@ -66,7 +74,7 @@ export function visibleSettingsGroups<G extends string>(
|
|
|
66
74
|
if (!entries) return groups.filter((g) => g.visible !== false);
|
|
67
75
|
const filled = new Set<G>();
|
|
68
76
|
for (const e of entries) if (e.visible !== false) filled.add(e.group);
|
|
69
|
-
return groups.filter((g) => g.visible !== false && filled.has(g.id));
|
|
77
|
+
return groups.filter((g) => g.visible !== false && (g.standalone === true || filled.has(g.id)));
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
/** The visible entries of the visible groups — what search may find. */
|
package/src/shell/admin-menu.tsx
CHANGED
|
@@ -2,8 +2,8 @@ import { Link } from "react-router";
|
|
|
2
2
|
import { ShieldCheck } from "lucide-react";
|
|
3
3
|
import { Chip } from "../components/chip";
|
|
4
4
|
import { MenuItem } from "../components/menu-item";
|
|
5
|
-
import {
|
|
6
|
-
import { DEFAULT_ADMIN_AREA_LABELS } from "../admin/admin-area-labels";
|
|
5
|
+
import type { LabelOverride } from "../i18n/kit-labels";
|
|
6
|
+
import { DEFAULT_ADMIN_AREA_LABELS, useAdminAreaLabels } from "../admin/admin-area-labels";
|
|
7
7
|
import type { AdminAreaLabels } from "../admin/admin-area-labels";
|
|
8
8
|
import type { TopBarMenuEntry } from "./topbar-action-menu";
|
|
9
9
|
|
|
@@ -45,8 +45,12 @@ export interface AdminMenuEntryOptions {
|
|
|
45
45
|
* `adminWaitingCount(attention, […])`. `0`, `null` or none shows no badge.
|
|
46
46
|
*/
|
|
47
47
|
badge?: number | null;
|
|
48
|
-
/**
|
|
49
|
-
*
|
|
48
|
+
/**
|
|
49
|
+
* The resolved `adminArea` words — pass `useAdminAreaLabels()`, or the row is ENGLISH
|
|
50
|
+
* in every language: a plain function reads no provider (kastlan, 0.42 adoption). Builds
|
|
51
|
+
* no hook, so the entry can be made in the list of entries. {@link useAdminMenuEntry}
|
|
52
|
+
* (0.43) is the same entry with the provider's words read for you.
|
|
53
|
+
*/
|
|
50
54
|
labels?: AdminAreaLabels;
|
|
51
55
|
/** The entry's `key`. Default `"administration"`. */
|
|
52
56
|
key?: string;
|
|
@@ -59,7 +63,12 @@ export interface AdminMenuEntryOptions {
|
|
|
59
63
|
* the kit's word and the waiting count as its badge. Put it in the entries for an admin
|
|
60
64
|
* only — the menu closes on the way, as any link row:
|
|
61
65
|
*
|
|
66
|
+
* const labels = useAdminAreaLabels();
|
|
62
67
|
* entries={[…, ...(isAdmin ? [adminMenuEntry({ badge: waiting, labels })] : []), …]}
|
|
68
|
+
*
|
|
69
|
+
* A function, not a hook: it reads NO provider, so pass `labels` (`useAdminAreaLabels()`)
|
|
70
|
+
* or the row says "Administration" in every language — or call {@link useAdminMenuEntry}
|
|
71
|
+
* at the component's top.
|
|
63
72
|
*/
|
|
64
73
|
export function adminMenuEntry({
|
|
65
74
|
href = "/admin",
|
|
@@ -79,6 +88,25 @@ export function adminMenuEntry({
|
|
|
79
88
|
};
|
|
80
89
|
}
|
|
81
90
|
|
|
91
|
+
export interface UseAdminMenuEntryOptions extends Omit<AdminMenuEntryOptions, "labels"> {
|
|
92
|
+
/** Over the provider's `adminArea` words: prop > `<UiKitProvider labels={{ adminArea }}>`
|
|
93
|
+
* > English. */
|
|
94
|
+
labels?: LabelOverride<AdminAreaLabels>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* {@link adminMenuEntry} with the provider's words (0.43, kastlan): the hook reads
|
|
99
|
+
* `useAdminAreaLabels()` itself, so the row is in the app's language without a `labels`
|
|
100
|
+
* to remember. Call it at the component's top, then put it in the entries:
|
|
101
|
+
*
|
|
102
|
+
* const admin = useAdminMenuEntry({ badge: waiting });
|
|
103
|
+
* entries={[…, ...(isAdmin ? [admin] : []), …]}
|
|
104
|
+
*/
|
|
105
|
+
export function useAdminMenuEntry({ labels: labelsProp, ...options }: UseAdminMenuEntryOptions = {}): TopBarMenuEntry {
|
|
106
|
+
const labels = useAdminAreaLabels(labelsProp);
|
|
107
|
+
return adminMenuEntry({ ...options, labels });
|
|
108
|
+
}
|
|
109
|
+
|
|
82
110
|
export interface AdminMenuRowProps {
|
|
83
111
|
/** The admin area. Default `/admin`. */
|
|
84
112
|
href?: string;
|
|
@@ -99,7 +127,8 @@ export interface AdminMenuRowProps {
|
|
|
99
127
|
* {(close) => <ul>… {isAdmin && <AdminMenuRow badge={waiting} onNavigate={close} />} …</ul>}
|
|
100
128
|
*/
|
|
101
129
|
export function AdminMenuRow({ href = "/admin", badge, onNavigate, current, labels: labelsProp }: AdminMenuRowProps) {
|
|
102
|
-
|
|
130
|
+
// The provider's words, as every kit part (0.43: through the namespace's own hook).
|
|
131
|
+
const labels = useAdminAreaLabels(labelsProp);
|
|
103
132
|
return (
|
|
104
133
|
<li>
|
|
105
134
|
<MenuItem
|
|
@@ -97,6 +97,14 @@ export function DevSwitchers<R extends string, P extends string>({
|
|
|
97
97
|
const bar = useKitLabels("topBar", DEFAULT_TOP_BAR_LABELS);
|
|
98
98
|
const words = useTopBarPartsLabels(labelsProp ? { dev: labelsProp } : undefined).dev;
|
|
99
99
|
const [kept, setKept] = useState<string | null>(() => (enabled ? readKey(storageKey) : null));
|
|
100
|
+
// 0.43 (keksdose): `enabled` may start false and turn true — an app's GET probe of the
|
|
101
|
+
// dev route — so the kept role is read again then, or "Back to {role}" is lost after a
|
|
102
|
+
// reload. Adjusted during render, React's pattern for state that follows a prop.
|
|
103
|
+
const [wasEnabled, setWasEnabled] = useState(enabled);
|
|
104
|
+
if (wasEnabled !== enabled) {
|
|
105
|
+
setWasEnabled(enabled);
|
|
106
|
+
if (enabled) setKept(readKey(storageKey));
|
|
107
|
+
}
|
|
100
108
|
if (!enabled) return null;
|
|
101
109
|
|
|
102
110
|
const original = (originalRole ?? kept ?? undefined) as R | undefined;
|
|
@@ -24,6 +24,12 @@ export interface TourMenuLabels {
|
|
|
24
24
|
badgeNew: string;
|
|
25
25
|
/** What the ✓ on a taken tour's row says to a reader. */
|
|
26
26
|
completed: string;
|
|
27
|
+
/** The chip on a tour reset by an update (0.43, docs/tours-harmonization.md §8). */
|
|
28
|
+
badgeUpdated: string;
|
|
29
|
+
/** The dot on the compass while only updated tours light it, for a reader. */
|
|
30
|
+
updatedDot: string;
|
|
31
|
+
/** The heading over the untaken new tours when updated ones are among them. */
|
|
32
|
+
newAndUpdated: string;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
/** `SyncStatusIndicator`'s words (§2.6). The "waiting for a plan" state is
|
|
@@ -94,6 +100,9 @@ export const DEFAULT_TOP_BAR_PARTS_LABELS: TopBarPartsLabels = {
|
|
|
94
100
|
allTours: "All tours…",
|
|
95
101
|
badgeNew: "New",
|
|
96
102
|
completed: "Completed",
|
|
103
|
+
badgeUpdated: "Updated",
|
|
104
|
+
updatedDot: "Updated tours",
|
|
105
|
+
newAndUpdated: "New and updated",
|
|
97
106
|
},
|
|
98
107
|
sync: {
|
|
99
108
|
blocked: (count) =>
|
package/src/shell/tour-menu.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import type { ReactNode } from "react";
|
|
|
2
2
|
import { useLocation } from "react-router";
|
|
3
3
|
import { Check, Compass } from "lucide-react";
|
|
4
4
|
import { Chip } from "../components/chip";
|
|
5
|
+
import { isTourDone, isTourNew, isTourReset, type TourCompletions, type TourInfo } from "../tour/tour-info";
|
|
5
6
|
import type { LabelOverride } from "../i18n/kit-labels";
|
|
6
7
|
import { TopBarActionMenu, type TopBarMenuEntry } from "./topbar-action-menu";
|
|
7
8
|
import { useTopBarPartsLabels, type TourMenuLabels } from "./top-bar-parts-labels";
|
|
@@ -11,28 +12,36 @@ import { useTopBarPartsLabels, type TourMenuLabels } from "./top-bar-parts-label
|
|
|
11
12
|
* The kit's tour engine (`TourProvider`) runs steps and keeps none; which tours exist,
|
|
12
13
|
* which are new and which are taken is the app's (keksdose: `useTours`, `useIsNewTour`,
|
|
13
14
|
* its progress store).
|
|
15
|
+
*
|
|
16
|
+
* Since 0.43 it is a subset of the catalogue's `TourInfo` (docs/tours-harmonization.md
|
|
17
|
+
* §3), so an app keeps ONE list: hand the menu the catalogue's tours with `completed`
|
|
18
|
+
* and `joined`, and it works out ✓, NEW and "Updated" itself. Given the record, the kit's
|
|
19
|
+
* ✓ and "Updated" win over a row's `completed` and `reset`; given `joined`, its NEW wins
|
|
20
|
+
* over `isNew` — so a row built from an app's own booleans cannot hide an update (§8,
|
|
21
|
+
* build review kastlan A3, keksdose K-S6). The booleans are for a menu with neither.
|
|
14
22
|
*/
|
|
15
|
-
export
|
|
23
|
+
export type TourMenuTour = Partial<Pick<TourInfo, "route" | "since" | "updated">> & {
|
|
16
24
|
id: string;
|
|
17
25
|
title: ReactNode;
|
|
18
|
-
/** The
|
|
19
|
-
|
|
20
|
-
route?: string;
|
|
26
|
+
/** The row's icon, drawn at 16px. */
|
|
27
|
+
icon?: ReactNode;
|
|
21
28
|
/** Shipped after this account was opened and not yet taken: a NEW chip on the row,
|
|
22
29
|
* and the dot on the compass. */
|
|
23
30
|
isNew?: boolean;
|
|
24
31
|
/** Taken: a ✓ on the row. */
|
|
25
32
|
completed?: boolean;
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
since?: string;
|
|
30
|
-
}
|
|
33
|
+
/** Taken before its last update (§8): an "Updated" chip, and the dot, as for NEW. */
|
|
34
|
+
reset?: boolean;
|
|
35
|
+
};
|
|
31
36
|
|
|
32
37
|
export interface TourMenuProps {
|
|
33
38
|
tours: readonly TourMenuTour[];
|
|
34
39
|
/** Start a tour — `useTour().start(steps, { id })` with the app's steps. */
|
|
35
40
|
onLaunch: (id: string) => void;
|
|
41
|
+
/** The device's completions (`useTourProgress().completed`): ✓ and "Updated" from them. */
|
|
42
|
+
completed?: TourCompletions;
|
|
43
|
+
/** The account's `created_at`: NEW from it, by `isTourNew`. */
|
|
44
|
+
joined?: string | null;
|
|
36
45
|
/**
|
|
37
46
|
* The app's tours page (keksdose `/tours`). With it the compass is a LINK there plus a
|
|
38
47
|
* hover menu, and the menu ends in "All tours…". Without it (§6.7, Kurvenschmiede) the
|
|
@@ -59,7 +68,9 @@ export interface TourMenuProps {
|
|
|
59
68
|
/** A path's first segment — "/reports/spending?g=month" → "/reports". */
|
|
60
69
|
const pageOf = (path: string) => `/${path.split(/[?#]/)[0].split("/")[1] ?? ""}`;
|
|
61
70
|
|
|
62
|
-
const isFresh = (t: TourMenuTour) => !!t.isNew && !t.completed;
|
|
71
|
+
const isFresh = (t: TourMenuTour) => (!!t.isNew && !t.completed) || !!t.reset;
|
|
72
|
+
/** When it last became news: its update, else its ship date. */
|
|
73
|
+
const newsOf = (t: TourMenuTour) => (t.reset ? t.updated?.date : undefined) ?? t.since ?? "";
|
|
63
74
|
|
|
64
75
|
/**
|
|
65
76
|
* The top bar's guided-tours control (docs/top-bar-harmonization.md §2.4, §6.7), lifted
|
|
@@ -68,8 +79,10 @@ const isFresh = (t: TourMenuTour) => !!t.isNew && !t.completed;
|
|
|
68
79
|
* and "All tours…". A taken tour's row carries a ✓, a new one a NEW chip.
|
|
69
80
|
*/
|
|
70
81
|
export function TourMenu({
|
|
71
|
-
tours,
|
|
82
|
+
tours: toursProp,
|
|
72
83
|
onLaunch,
|
|
84
|
+
completed: completions,
|
|
85
|
+
joined,
|
|
73
86
|
href,
|
|
74
87
|
pathname: pathnameProp,
|
|
75
88
|
maxNew = 3,
|
|
@@ -80,13 +93,27 @@ export function TourMenu({
|
|
|
80
93
|
const words = useTopBarPartsLabels(labelsProp ? { tours: labelsProp } : undefined).tours;
|
|
81
94
|
const location = useLocation();
|
|
82
95
|
const here = pageOf(pathnameProp ?? location.pathname);
|
|
96
|
+
const tours =
|
|
97
|
+
completions === undefined && joined === undefined
|
|
98
|
+
? toursProp
|
|
99
|
+
: toursProp.map((t) => {
|
|
100
|
+
const at = completions?.[t.id];
|
|
101
|
+
// Ever completed: the record when there is one, else the row's own word.
|
|
102
|
+
const ever = completions ? at !== undefined : !!t.completed;
|
|
103
|
+
return {
|
|
104
|
+
...t,
|
|
105
|
+
completed: completions ? isTourDone(t, at) : t.completed,
|
|
106
|
+
reset: completions ? isTourReset(t, at) : t.reset,
|
|
107
|
+
isNew: joined !== undefined ? isTourNew({ since: t.since, joined, completed: ever }) : t.isNew,
|
|
108
|
+
};
|
|
109
|
+
});
|
|
83
110
|
|
|
84
111
|
const forThisPage = tours.filter((t) => t.route !== undefined && pageOf(t.route) === here);
|
|
85
112
|
const fresh = tours.filter(isFresh);
|
|
86
113
|
// ISO dates sort as text; a stable sort keeps one day's tours in the app's order.
|
|
87
114
|
const newRows = fresh
|
|
88
115
|
.filter((t) => !forThisPage.includes(t))
|
|
89
|
-
.sort((a, b) => (b
|
|
116
|
+
.sort((a, b) => newsOf(b).localeCompare(newsOf(a)))
|
|
90
117
|
.slice(0, maxNew);
|
|
91
118
|
const rest = href && list !== "all" ? [] : tours.filter((t) => !forThisPage.includes(t) && !newRows.includes(t));
|
|
92
119
|
|
|
@@ -96,7 +123,11 @@ export function TourMenu({
|
|
|
96
123
|
key: tour.id,
|
|
97
124
|
icon: tour.icon,
|
|
98
125
|
label: tour.title,
|
|
99
|
-
trailing: tour.
|
|
126
|
+
trailing: tour.reset ? (
|
|
127
|
+
<Chip size="sm" tone="brand" caps className="px-1.5">
|
|
128
|
+
{words.badgeUpdated}
|
|
129
|
+
</Chip>
|
|
130
|
+
) : tour.completed ? (
|
|
100
131
|
<>
|
|
101
132
|
<Check aria-hidden className="size-4 text-success" />
|
|
102
133
|
<span className="sr-only">{words.completed}</span>
|
|
@@ -112,7 +143,7 @@ export function TourMenu({
|
|
|
112
143
|
|
|
113
144
|
const blocks = [
|
|
114
145
|
{ key: "this-page", label: words.thisPage, tours: forThisPage },
|
|
115
|
-
{ key: "new", label: words.newTours, tours: newRows },
|
|
146
|
+
{ key: "new", label: newRows.some((t) => t.reset) ? words.newAndUpdated : words.newTours, tours: newRows },
|
|
116
147
|
].filter((b) => b.tours.length > 0);
|
|
117
148
|
const tail: TopBarMenuEntry[] = [
|
|
118
149
|
...rest.map(entryFor),
|
|
@@ -133,7 +164,11 @@ export function TourMenu({
|
|
|
133
164
|
return (
|
|
134
165
|
<TopBarActionMenu
|
|
135
166
|
icon={<Compass className="size-5" />}
|
|
136
|
-
iconBadge={
|
|
167
|
+
iconBadge={
|
|
168
|
+
fresh.length > 0
|
|
169
|
+
? { label: fresh.some((t) => !t.reset) ? words.newDot : words.updatedDot, tone: "brand" }
|
|
170
|
+
: null
|
|
171
|
+
}
|
|
137
172
|
aria-label={words.menu}
|
|
138
173
|
href={href}
|
|
139
174
|
panelClassName="w-72 max-h-[70vh] overflow-y-auto"
|
package/src/shell.ts
CHANGED
|
@@ -17,6 +17,9 @@ export type { FillHeightProps } from "./shell/fill-height";
|
|
|
17
17
|
// (docs/admin-harmonization.md §2.1, §6.6).
|
|
18
18
|
export { adminMenuEntry, AdminMenuRow } from "./shell/admin-menu";
|
|
19
19
|
export type { AdminMenuEntryOptions, AdminMenuRowProps } from "./shell/admin-menu";
|
|
20
|
+
// 0.43.0 (kastlan): the entry with the provider's words.
|
|
21
|
+
export { useAdminMenuEntry } from "./shell/admin-menu";
|
|
22
|
+
export type { UseAdminMenuEntryOptions } from "./shell/admin-menu";
|
|
20
23
|
// 0.31.0: the settings and admin pages' shell (docs/settings-harmonization.md §3, §5).
|
|
21
24
|
export { SettingsLayout, SETTINGS_SEARCH_THRESHOLD } from "./settings/settings-layout";
|
|
22
25
|
export type { SettingsLayoutProps, SettingsLayoutWidth, SettingsDocumentTitleParts } from "./settings/settings-layout";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// The English words of `TourCatalogue` and `TourCatalogueCard` (docs/tours-harmonization.md
|
|
2
|
+
// §3), in a module with no component code, like every `*-labels.ts` (0.34, KS U8).
|
|
3
|
+
// keksdose's reviewed words (tour.* in its en.json) where it had them.
|
|
4
|
+
|
|
5
|
+
/** The `tourCatalogue` namespace. Counts arrive as numbers, dates already formatted. */
|
|
6
|
+
export interface TourCatalogueLabels {
|
|
7
|
+
/** The search box's name. */
|
|
8
|
+
searchLabel: string;
|
|
9
|
+
searchPlaceholder: string;
|
|
10
|
+
/** The group select's name (keksdose: "Topic"). */
|
|
11
|
+
groupLabel: string;
|
|
12
|
+
/** The select's first option, with the count the other filters leave. */
|
|
13
|
+
groupAll: (count: number) => string;
|
|
14
|
+
/** A group's option: its title and its count. */
|
|
15
|
+
groupOption: (group: string, count: number) => string;
|
|
16
|
+
/** The all / to do / done toggle's name. */
|
|
17
|
+
stateLabel: string;
|
|
18
|
+
stateAll: (count: number) => string;
|
|
19
|
+
stateTodo: (count: number) => string;
|
|
20
|
+
stateDone: (count: number) => string;
|
|
21
|
+
/** Under the filter bar while a filter is on. */
|
|
22
|
+
showing: (shown: number, total: number) => string;
|
|
23
|
+
noMatches: string;
|
|
24
|
+
noMatchesHint: string;
|
|
25
|
+
clearFilters: string;
|
|
26
|
+
/** The NEW pill. */
|
|
27
|
+
badgeNew: string;
|
|
28
|
+
/** The pill on a tour reset by an update (§8). */
|
|
29
|
+
badgeUpdated: string;
|
|
30
|
+
/** A reset tour's line: the update's day (formatted) and its reason (§8); `reason` is
|
|
31
|
+
* left out when the tour's update carries none. */
|
|
32
|
+
resetOn: (date: string, reason?: string) => string;
|
|
33
|
+
/** The points chip. */
|
|
34
|
+
points: (count: number) => string;
|
|
35
|
+
steps: (count: number) => string;
|
|
36
|
+
/** The duration, in minutes. */
|
|
37
|
+
duration: (minutes: number) => string;
|
|
38
|
+
/** Completed, with the day (formatted). */
|
|
39
|
+
completedOn: (date: string) => string;
|
|
40
|
+
/** Completed without a date (kastlan's old id list, §6.2). */
|
|
41
|
+
completed: string;
|
|
42
|
+
/** Not taken yet — the state icon, for a reader. */
|
|
43
|
+
notCompleted: string;
|
|
44
|
+
markUndone: string;
|
|
45
|
+
start: string;
|
|
46
|
+
replay: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const DEFAULT_TOUR_CATALOGUE_LABELS: TourCatalogueLabels = {
|
|
50
|
+
searchLabel: "Search tours",
|
|
51
|
+
searchPlaceholder: "Search tours…",
|
|
52
|
+
groupLabel: "Topic",
|
|
53
|
+
groupAll: (count) => `All topics (${count})`,
|
|
54
|
+
groupOption: (group, count) => `${group} (${count})`,
|
|
55
|
+
stateLabel: "Filter tours",
|
|
56
|
+
stateAll: (count) => `All (${count})`,
|
|
57
|
+
stateTodo: (count) => `Not taken (${count})`,
|
|
58
|
+
stateDone: (count) => `Completed (${count})`,
|
|
59
|
+
showing: (shown, total) => `Showing ${shown} of ${total} tours`,
|
|
60
|
+
noMatches: "No tour matches",
|
|
61
|
+
noMatchesHint: "Try another word, or clear the filter.",
|
|
62
|
+
clearFilters: "Clear filters",
|
|
63
|
+
badgeNew: "New",
|
|
64
|
+
badgeUpdated: "Updated",
|
|
65
|
+
resetOn: (date, reason) => (reason ? `Reset on ${date}: ${reason}` : `Reset on ${date}`),
|
|
66
|
+
points: (count) => `+${count} pts`,
|
|
67
|
+
steps: (count) => (count === 1 ? "1 step" : `${count} steps`),
|
|
68
|
+
duration: (minutes) => `About ${minutes} min`,
|
|
69
|
+
completedOn: (date) => `Completed on ${date}`,
|
|
70
|
+
completed: "Completed",
|
|
71
|
+
notCompleted: "Not completed",
|
|
72
|
+
markUndone: "Mark as not done",
|
|
73
|
+
start: "Start",
|
|
74
|
+
replay: "Replay",
|
|
75
|
+
};
|