@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.
Files changed (134) hide show
  1. package/README.md +13 -7
  2. package/dist/admin/admin-area-labels.d.ts +4 -0
  3. package/dist/admin/admin-area-labels.js +3 -1
  4. package/dist/admin/admin-area-labels.js.map +1 -1
  5. package/dist/admin/admin-overview.d.ts +25 -8
  6. package/dist/admin/admin-overview.js +26 -3
  7. package/dist/admin/admin-overview.js.map +1 -1
  8. package/dist/admin/admin-sections.d.ts +36 -5
  9. package/dist/admin/admin-sections.js +35 -18
  10. package/dist/admin/admin-sections.js.map +1 -1
  11. package/dist/billing/billing-banner.d.ts +8 -0
  12. package/dist/billing/billing-banner.js +25 -2
  13. package/dist/billing/billing-banner.js.map +1 -1
  14. package/dist/billing/billing-labels.d.ts +4 -0
  15. package/dist/billing/billing-labels.js +1 -0
  16. package/dist/billing/billing-labels.js.map +1 -1
  17. package/dist/billing/cancel-request.d.ts +53 -0
  18. package/dist/billing/cancel-request.js +21 -0
  19. package/dist/billing/cancel-request.js.map +1 -0
  20. package/dist/billing/provider-page.d.ts +49 -0
  21. package/dist/billing/provider-page.js +33 -0
  22. package/dist/billing/provider-page.js.map +1 -0
  23. package/dist/billing/subscription-actions.d.ts +32 -1
  24. package/dist/billing/subscription-actions.js +57 -4
  25. package/dist/billing/subscription-actions.js.map +1 -1
  26. package/dist/components/account-chips.d.ts +8 -1
  27. package/dist/components/account-chips.js.map +1 -1
  28. package/dist/eslint/index.js +1 -1
  29. package/dist/feedback/feedback-menu.d.ts +7 -4
  30. package/dist/feedback/feedback-menu.js +4 -2
  31. package/dist/feedback/feedback-menu.js.map +1 -1
  32. package/dist/hooks/use-search-param-state.js +4 -1
  33. package/dist/hooks/use-search-param-state.js.map +1 -1
  34. package/dist/i18n/defaults.js +2 -0
  35. package/dist/i18n/defaults.js.map +1 -1
  36. package/dist/i18n/german.js +36 -2
  37. package/dist/i18n/german.js.map +1 -1
  38. package/dist/i18n/kit-labels.d.ts +3 -0
  39. package/dist/i18n/kit-labels.js.map +1 -1
  40. package/dist/i18n/locales/en.js +51 -2
  41. package/dist/i18n/locales/en.js.map +3 -3
  42. package/dist/i18n/locales/es.js +36 -2
  43. package/dist/i18n/locales/es.js.map +1 -1
  44. package/dist/i18n/locales/fr.js +36 -2
  45. package/dist/i18n/locales/fr.js.map +1 -1
  46. package/dist/i18n/locales/hu.js +36 -2
  47. package/dist/i18n/locales/hu.js.map +1 -1
  48. package/dist/i18n/locales/it.js +36 -2
  49. package/dist/i18n/locales/it.js.map +1 -1
  50. package/dist/i18n/locales/zh.js +36 -2
  51. package/dist/i18n/locales/zh.js.map +1 -1
  52. package/dist/i18n/review.js +15 -1
  53. package/dist/i18n/review.js.map +1 -1
  54. package/dist/index.d.ts +6 -0
  55. package/dist/index.js +6 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/search/global-search.d.ts +3 -0
  58. package/dist/search/global-search.js +3 -1
  59. package/dist/search/global-search.js.map +1 -1
  60. package/dist/settings/settings-catalogue.d.ts +9 -1
  61. package/dist/settings/settings-catalogue.js +1 -1
  62. package/dist/settings/settings-catalogue.js.map +1 -1
  63. package/dist/shell/admin-menu.d.ts +26 -3
  64. package/dist/shell/admin-menu.js +8 -4
  65. package/dist/shell/admin-menu.js.map +1 -1
  66. package/dist/shell/dev-switchers.js +5 -0
  67. package/dist/shell/dev-switchers.js.map +1 -1
  68. package/dist/shell/top-bar-parts-labels.d.ts +6 -0
  69. package/dist/shell/top-bar-parts-labels.js +4 -1
  70. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  71. package/dist/shell/tour-menu.d.ts +19 -10
  72. package/dist/shell/tour-menu.js +20 -6
  73. package/dist/shell/tour-menu.js.map +1 -1
  74. package/dist/shell.d.ts +2 -0
  75. package/dist/shell.js +2 -0
  76. package/dist/shell.js.map +1 -1
  77. package/dist/tour/tour-catalogue-labels.d.ts +44 -0
  78. package/dist/tour/tour-catalogue-labels.js +32 -0
  79. package/dist/tour/tour-catalogue-labels.js.map +1 -0
  80. package/dist/tour/tour-catalogue.d.ts +68 -0
  81. package/dist/tour/tour-catalogue.js +189 -0
  82. package/dist/tour/tour-catalogue.js.map +1 -0
  83. package/dist/tour/tour-info.d.ts +112 -0
  84. package/dist/tour/tour-info.js +36 -0
  85. package/dist/tour/tour-info.js.map +1 -0
  86. package/dist/tour/tour.d.ts +38 -2
  87. package/dist/tour/tour.js +40 -3
  88. package/dist/tour/tour.js.map +1 -1
  89. package/dist/tour/use-tour-progress.d.ts +116 -0
  90. package/dist/tour/use-tour-progress.js +146 -0
  91. package/dist/tour/use-tour-progress.js.map +1 -0
  92. package/dist/tour/use-tour-url-sync.d.ts +58 -0
  93. package/dist/tour/use-tour-url-sync.js +110 -0
  94. package/dist/tour/use-tour-url-sync.js.map +1 -0
  95. package/dist/tour.d.ts +8 -0
  96. package/dist/tour.js +19 -0
  97. package/dist/tour.js.map +1 -1
  98. package/package.json +1 -1
  99. package/src/admin/admin-area-labels.ts +7 -1
  100. package/src/admin/admin-overview.tsx +61 -10
  101. package/src/admin/admin-sections.tsx +109 -48
  102. package/src/billing/billing-banner.tsx +25 -2
  103. package/src/billing/billing-labels.ts +6 -0
  104. package/src/billing/cancel-request.ts +75 -0
  105. package/src/billing/provider-page.ts +84 -0
  106. package/src/billing/subscription-actions.tsx +97 -11
  107. package/src/components/account-chips.tsx +8 -1
  108. package/src/feedback/feedback-menu.tsx +8 -3
  109. package/src/hooks/use-search-param-state.ts +6 -1
  110. package/src/i18n/defaults.ts +2 -0
  111. package/src/i18n/german.ts +36 -1
  112. package/src/i18n/kit-labels.tsx +3 -0
  113. package/src/i18n/locales/en.ts +13 -0
  114. package/src/i18n/locales/es.ts +36 -1
  115. package/src/i18n/locales/fr.ts +36 -1
  116. package/src/i18n/locales/hu.ts +36 -1
  117. package/src/i18n/locales/it.ts +36 -1
  118. package/src/i18n/locales/zh.ts +36 -1
  119. package/src/i18n/review.ts +14 -0
  120. package/src/index.ts +8 -0
  121. package/src/search/global-search.tsx +5 -0
  122. package/src/settings/settings-catalogue.ts +10 -2
  123. package/src/shell/admin-menu.tsx +34 -5
  124. package/src/shell/dev-switchers.tsx +8 -0
  125. package/src/shell/top-bar-parts-labels.ts +9 -0
  126. package/src/shell/tour-menu.tsx +50 -15
  127. package/src/shell.ts +3 -0
  128. package/src/tour/tour-catalogue-labels.ts +75 -0
  129. package/src/tour/tour-catalogue.tsx +316 -0
  130. package/src/tour/tour-info.ts +144 -0
  131. package/src/tour/tour.tsx +84 -3
  132. package/src/tour/use-tour-progress.ts +299 -0
  133. package/src/tour/use-tour-url-sync.ts +221 -0
  134. package/src/tour.ts +12 -0
@@ -1,11 +1,30 @@
1
- import type { ComponentPropsWithRef } from "react";
1
+ import type { ComponentPropsWithRef, ReactNode } from "react";
2
+ import { ExternalLink } from "lucide-react";
2
3
 
4
+ import { DEFAULT_COMMON_LABELS, useKitLabels } from "../i18n/kit-labels";
3
5
  import type { LabelOverride } from "../i18n/kit-labels";
4
6
  import { cn } from "../lib/cn";
5
7
  import { Button } from "../components/button";
6
8
  import { useBillingLabels } from "./billing-labels";
7
9
  import type { BillingLabels } from "./billing-labels";
8
10
  import type { BillingPortalAccess, PortalTarget } from "./billing-overview";
11
+ import type { CancelRequested } from "./cancel-request";
12
+
13
+ /** The portal action's mark (§17.3): the external-link glyph after the words, and "(opens
14
+ * in a new tab)" for a reader — on a link, `Button`'s `external` says the latter. */
15
+ export function ExternalMark({ reader }: { reader?: string }) {
16
+ return (
17
+ <>
18
+ <ExternalLink aria-hidden data-external="" className="size-4 shrink-0 rtl:-scale-x-100" />
19
+ {reader !== undefined && (
20
+ <>
21
+ {" "}
22
+ <span className="sr-only">({reader})</span>
23
+ </>
24
+ )}
25
+ </>
26
+ );
27
+ }
9
28
 
10
29
  export interface SubscriptionActionsProps extends Omit<ComponentPropsWithRef<"div">, "children"> {
11
30
  /** "Payment and invoices", as a link — or {@link onManage}. Without either, none. */
@@ -62,6 +81,25 @@ export interface SubscriptionActionsProps extends Omit<ComponentPropsWithRef<"di
62
81
  * the cancel's place while {@link cancelAtPeriodEnd} is true. Without it, the cancel
63
82
  * stays as before. */
64
83
  onResume?: () => void;
84
+ /**
85
+ * 0.43 (kastlan `18bf4c0`): what was asked in the app and the overview does not show
86
+ * yet — `useCancelRequest().view(row).requested`. `"cancel"`: shown as scheduled ("Keep
87
+ * subscription" with {@link onResume}; without it, no second "Cancel subscription").
88
+ * `"resume"`: shown as not scheduled. `null` (default): {@link cancelAtPeriodEnd} says.
89
+ */
90
+ cancelRequested?: CancelRequested;
91
+ /**
92
+ * 0.43 (§17): the portal opens in a NEW TAB — the app's handler calls
93
+ * `openProviderPage` — so its actions carry the external-link mark, a link opens with
94
+ * `target="_blank"`, and the `portalOpensNewTab` note shows under them. Default true;
95
+ * `false` for an app whose portal still opens in the same tab.
96
+ */
97
+ portalNewTab?: boolean;
98
+ /** 0.43: the provider's name in the note. Default `"Paddle"` (§2 decision 16). */
99
+ providerName?: string;
100
+ /** 0.43: the app's name in the note ("Kastlan stays open here"). Without it, "this page
101
+ * stays open". */
102
+ appName?: string;
65
103
  /** Which one is under way: its spinner, no second press. `"resume"` since 0.42. */
66
104
  pending?: "manage" | "cancel" | "resume" | null;
67
105
  /** Prop > `<UiKitProvider labels={{ billing }}>` > English. */
@@ -89,6 +127,12 @@ export interface SubscriptionActionsProps extends Omit<ComponentPropsWithRef<"di
89
127
  * "Cancel subscription" is the in-app cancel (`onCancelInApp`). While a cancel is
90
128
  * scheduled (`cancelAtPeriodEnd`) and `onResume` is given, "Keep subscription" stands in
91
129
  * the cancel's place, whoever the viewer is.
130
+ *
131
+ * 0.43 (§17): the portal opens in a new tab (`openProviderPage` in the app's handler):
132
+ * its actions carry the external-link mark, and a muted note says so — "Payment and
133
+ * invoices open at {provider} in a new tab; {app} stays open here." The in-app cancel and
134
+ * "Keep subscription" stay unmarked. After an in-app cancel or resume, `cancelRequested`
135
+ * (`useCancelRequest`) shows what was asked until the provider's webhook lands.
92
136
  */
93
137
  export function SubscriptionActions({
94
138
  manageHref,
@@ -102,12 +146,17 @@ export function SubscriptionActions({
102
146
  onCancelInApp,
103
147
  cancelAtPeriodEnd = false,
104
148
  onResume,
149
+ cancelRequested = null,
150
+ portalNewTab = true,
151
+ providerName = "Paddle",
152
+ appName,
105
153
  pending,
106
154
  labels: labelsProp,
107
155
  className,
108
156
  ...rest
109
157
  }: SubscriptionActionsProps) {
110
158
  const labels = useBillingLabels(labelsProp);
159
+ const common = useKitLabels("common", DEFAULT_COMMON_LABELS);
111
160
  if (!atProvider) {
112
161
  return (
113
162
  <div {...rest} data-at-provider="false" className={cn("text-sm text-muted", className)}>
@@ -121,38 +170,75 @@ export function SubscriptionActions({
121
170
  // Under "buyer_only" the in-app cancel only; under "open" the portal's first, the
122
171
  // in-app cancel only where no way to the portal's is given.
123
172
  const cancelCall = buyerOnly ? onCancelInApp : (portalCancel ?? (cancelHref === undefined ? onCancelInApp : undefined));
173
+ // 0.43: what was asked in the app wins over the row until the row moves.
174
+ const scheduled = cancelRequested === "cancel" ? true : cancelRequested === "resume" ? false : cancelAtPeriodEnd;
175
+ // 0.43 (§17): the portal's actions are marked, a link opens a tab of its own.
176
+ const newTab = portalNewTab && !buyerOnly;
177
+ const words = (text: string, external: boolean, link: boolean): ReactNode =>
178
+ external ? (
179
+ <>
180
+ {text}
181
+ <ExternalMark reader={link ? undefined : common.opensInNewTab} />
182
+ </>
183
+ ) : (
184
+ text
185
+ );
186
+ const portalCancelShown = !buyerOnly && (cancelHref !== undefined || portalCancel !== undefined);
124
187
  const manage = buyerOnly ? (
125
188
  <p className="basis-full text-sm text-muted" data-buyer-holds="">
126
189
  {labels.buyerHolds(buyerName || undefined)}
127
190
  </p>
128
191
  ) : manageHref !== undefined ? (
129
- <Button href={manageHref} variant="secondary">
130
- {labels.manage}
192
+ <Button href={manageHref} variant="secondary" external={newTab || undefined} data-portal-action="manage">
193
+ {words(labels.manage, newTab, true)}
131
194
  </Button>
132
195
  ) : manageCall !== undefined ? (
133
- <Button type="button" variant="secondary" onClick={manageCall} pending={pending === "manage"}>
134
- {labels.manage}
196
+ <Button
197
+ type="button"
198
+ variant="secondary"
199
+ onClick={manageCall}
200
+ pending={pending === "manage"}
201
+ className={newTab ? "relative" : undefined}
202
+ data-portal-action="manage"
203
+ >
204
+ {words(labels.manage, newTab, false)}
135
205
  </Button>
136
206
  ) : null;
137
207
  const cancel =
138
- cancelAtPeriodEnd && onResume !== undefined ? (
208
+ scheduled && onResume !== undefined ? (
139
209
  <Button type="button" variant="link" onClick={onResume} pending={pending === "resume"} data-keep-subscription="">
140
210
  {labels.keepSubscription}
141
211
  </Button>
142
- ) : cancelHref !== undefined && !buyerOnly ? (
143
- <Button href={cancelHref} variant="link" tone="danger">
144
- {labels.cancel}
212
+ ) : cancelRequested === "cancel" ? null : cancelHref !== undefined && !buyerOnly ? (
213
+ <Button href={cancelHref} variant="link" tone="danger" external={newTab || undefined} data-portal-action="cancel">
214
+ {words(labels.cancel, newTab, true)}
145
215
  </Button>
146
216
  ) : cancelCall !== undefined ? (
147
- <Button type="button" variant="link" tone="danger" onClick={cancelCall} pending={pending === "cancel"}>
148
- {labels.cancel}
217
+ <Button
218
+ type="button"
219
+ variant="link"
220
+ tone="danger"
221
+ onClick={cancelCall}
222
+ pending={pending === "cancel"}
223
+ className={newTab && portalCancelShown ? "relative" : undefined}
224
+ data-portal-action={portalCancelShown ? "cancel" : undefined}
225
+ >
226
+ {words(labels.cancel, newTab && portalCancelShown, false)}
149
227
  </Button>
150
228
  ) : null;
151
229
  if (!manage && !cancel) return null;
230
+ // The note while a way to the portal shows: "Payment and invoices open at Paddle in a
231
+ // new tab; Kastlan stays open here."
232
+ const portalShown = !buyerOnly && (manage !== null || (cancel !== null && portalCancelShown && !(scheduled && onResume)));
152
233
  return (
153
234
  <div {...rest} className={cn("flex flex-wrap items-center gap-x-4 gap-y-2", className)}>
154
235
  {manage}
155
236
  {cancel}
237
+ {newTab && portalShown && (
238
+ <p className="basis-full text-sm text-muted" data-portal-note="">
239
+ {labels.portalOpensNewTab(providerName, appName || undefined)}
240
+ </p>
241
+ )}
156
242
  </div>
157
243
  );
158
244
  }
@@ -47,7 +47,14 @@ interface AccountChipLook {
47
47
  export interface RoleDefinition {
48
48
  label: string;
49
49
  tone?: ChipTone;
50
- /** A lucide component or an element — Kurvenschmiede marks its admins with a shield. */
50
+ /**
51
+ * A lucide component or an element — Kurvenschmiede marks its admins with a shield.
52
+ * Unlike {@link href} and {@link variant}, it shows EVERYWHERE the role is drawn: in
53
+ * {@link AccountRoleChip} and in {@link RoleChip} (the roster's column) alike. That is
54
+ * intended (0.43, keksdose's question): the glyph is part of how the role reads, so the
55
+ * shield on the header's chip is the shield in the users list. An app that wants the
56
+ * glyph in one place only hands the other a second vocabulary without `icon`.
57
+ */
51
58
  icon?: LucideIcon | ReactElement;
52
59
  /**
53
60
  * 0.42.0 (docs/admin-harmonization.md §2.1, settings §12.3): the role's tool — an
@@ -71,6 +71,9 @@ export interface FeedbackMenuProps {
71
71
  /** Which edge of the trigger the panel lines up with, `start` or `end` in the reading
72
72
  * direction (see `HoverMenu`, whose type this is). Default `end`. */
73
73
  align?: HoverMenuProps["align"];
74
+ /** 0.43: `data-*` attributes go on the trigger — a tour's anchor
75
+ * (`data-tour="feedback-menu"`), a test hook. */
76
+ [key: `data-${string}`]: string | number | boolean | undefined;
74
77
  }
75
78
 
76
79
  /**
@@ -101,9 +104,9 @@ export interface FeedbackMenuProps {
101
104
  * to the top like one with none of what makes it actionable. The server refuses it with a
102
105
  * 422 anyway (§3.4).
103
106
  *
104
- * The menu forwards nothing to its trigger, as `TopBarActionMenu` does not: keksdose's
105
- * tour anchor (`data-tour="feedback-menu"`) stays on a wrapping `<span>`. Needs a router,
106
- * as every `TopBarActionMenu` with link rows does.
107
+ * Since 0.43 its `data-*` attributes go on the trigger (keksdose's tour anchor
108
+ * `data-tour="feedback-menu"` needs no wrapping `<span>` any more). Needs a router, as
109
+ * every `TopBarActionMenu` with link rows does.
107
110
  */
108
111
  export function FeedbackMenu({
109
112
  onFile,
@@ -115,6 +118,7 @@ export function FeedbackMenu({
115
118
  iconBadge,
116
119
  labels: labelsProp,
117
120
  align,
121
+ ...attributes
118
122
  }: FeedbackMenuProps) {
119
123
  const labels = useKitLabels("feedbackMenu", DEFAULT_FEEDBACK_MENU_LABELS, labelsProp);
120
124
  const categoryLabels = useFeedbackCategoryLabels();
@@ -167,6 +171,7 @@ export function FeedbackMenu({
167
171
  aria-label={labels.trigger}
168
172
  entries={entries}
169
173
  align={align}
174
+ triggerAttributes={Object.fromEntries(Object.entries(attributes).filter(([k]) => k.startsWith("data-")))}
170
175
  />
171
176
  );
172
177
  }
@@ -208,7 +208,12 @@ function writeQuery(
208
208
  }
209
209
  }
210
210
  queued.lastPushed = push;
211
- void navigate(`?${search}`, push ? undefined : { replace: true, state: carriedState(queued.state) });
211
+ // The hash is spelled out: `navigate("?…")` drops it (keksdose dev#495; build review
212
+ // 0.43, keksdose N2 — the tours page's filters).
213
+ void navigate(
214
+ { search: `?${search}`, hash: location.hash },
215
+ push ? undefined : { replace: true, state: carriedState(queued.state) },
216
+ );
212
217
  }
213
218
 
214
219
  /** The rendered query, and a write that composes with every other one on the router. */
@@ -13,6 +13,7 @@ import {
13
13
  } from "../components/ui-labels";
14
14
  import { DEFAULT_WIZARD_LABELS } from "../wizard/wizard-labels";
15
15
  import { DEFAULT_TOUR_LABELS } from "../tour/tour-labels";
16
+ import { DEFAULT_TOUR_CATALOGUE_LABELS } from "../tour/tour-catalogue-labels";
16
17
  import { DEFAULT_COMMAND_PALETTE_LABELS } from "../search/command-palette-labels";
17
18
  import { DEFAULT_GLOBAL_SEARCH_LABELS } from "../search/global-search-labels";
18
19
  import { DEFAULT_SERIES_CHART_LABELS } from "../components/series-chart-labels";
@@ -167,6 +168,7 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
167
168
  file: DEFAULT_FILE_LABELS,
168
169
  wizard: DEFAULT_WIZARD_LABELS,
169
170
  tour: DEFAULT_TOUR_LABELS,
171
+ tourCatalogue: DEFAULT_TOUR_CATALOGUE_LABELS,
170
172
  commandPalette: DEFAULT_COMMAND_PALETTE_LABELS,
171
173
  globalSearch: DEFAULT_GLOBAL_SEARCH_LABELS,
172
174
  seriesChart: DEFAULT_SERIES_CHART_LABELS,
@@ -626,6 +626,34 @@ export function germanLabels(numberLocale: string): UiKitLabels {
626
626
  awaitClickHint: "Klicken Sie auf das hervorgehobene Element, um fortzufahren",
627
627
  step: (current, total) => `${n(current)} / ${n(total)}`,
628
628
  },
629
+ // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page. keksdose's reviewed words.
630
+ tourCatalogue: {
631
+ searchLabel: "Touren durchsuchen",
632
+ searchPlaceholder: "Touren durchsuchen…",
633
+ groupLabel: "Thema",
634
+ groupAll: (count) => `Alle Themen (${n(count)})`,
635
+ groupOption: (group, count) => `${group} (${n(count)})`,
636
+ stateLabel: "Touren filtern",
637
+ stateAll: (count) => `Alle (${n(count)})`,
638
+ stateTodo: (count) => `Offen (${n(count)})`,
639
+ stateDone: (count) => `Abgeschlossen (${n(count)})`,
640
+ showing: (shown, total) => `${n(shown)} von ${n(total)} Touren`,
641
+ noMatches: "Keine Tour passt",
642
+ noMatchesHint: "Anderes Wort versuchen oder Filter zurücksetzen.",
643
+ clearFilters: "Filter zurücksetzen",
644
+ badgeNew: "Neu",
645
+ badgeUpdated: "Aktualisiert",
646
+ resetOn: (date, reason) => (reason ? `Zurückgesetzt am ${date}: ${reason}` : `Zurückgesetzt am ${date}`),
647
+ points: (count) => `+${n(count)} Pkt`,
648
+ steps: (count) => (count === 1 ? `${n(count)} Schritt` : `${n(count)} Schritte`),
649
+ duration: (minutes) => `Etwa ${n(minutes)} Min.`,
650
+ completedOn: (date) => `Abgeschlossen am ${date}`,
651
+ completed: "Abgeschlossen",
652
+ notCompleted: "Nicht abgeschlossen",
653
+ markUndone: "Als nicht erledigt markieren",
654
+ start: "Starten",
655
+ replay: "Erneut",
656
+ },
629
657
  commandPalette: {
630
658
  clear: "Suche löschen",
631
659
  submit: "Suchen",
@@ -1779,6 +1807,8 @@ export function germanLabels(numberLocale: string): UiKitLabels {
1779
1807
  // §312k BGB: the confirmation button says exactly this.
1780
1808
  cancelConfirmAction: "Jetzt kündigen",
1781
1809
  keepSubscription: "Abonnement behalten",
1810
+ portalOpensNewTab: (provider, app) =>
1811
+ `Zahlung und Rechnungen öffnen sich bei ${provider} in einem neuen Tab; ${app ? `${app} bleibt hier offen` : "diese Seite bleibt offen"}.`,
1782
1812
  grantWarningTitle: "Ihr kostenloser Zugang läuft weiter",
1783
1813
  grantWarningBody:
1784
1814
  "Ihr kostenloser Zugang hat kein Enddatum. Solange er läuft, ändert ein Kauf Ihren Tarif nicht: Sie würden für einen Tarif bezahlen, der nicht gilt.",
@@ -1872,7 +1902,7 @@ export function germanLabels(numberLocale: string): UiKitLabels {
1872
1902
  sections: {
1873
1903
  overview: { title: "Übersicht", help: "Was gerade wartet, auf einen Blick." },
1874
1904
  users: { title: "Benutzer", help: "Wer ein Konto hat und was jede Person darf." },
1875
- invitations: { title: "Einladungen", help: "Die Links, mit denen neue Personen dazukommen." },
1905
+ invitations: { title: "Einladungen", help: "Laden Sie Personen per E-Mail in ihrer Sprache ein; senden Sie offene Einladungen erneut oder widerrufen Sie sie." },
1876
1906
  billing: { title: "Abrechnung", help: "Tarif, Zahlung und Rechnungen." },
1877
1907
  activity: { title: "Aktivität", help: "Was in der Verwaltung getan wurde, das Neueste zuerst." },
1878
1908
  },
@@ -1883,6 +1913,8 @@ export function germanLabels(numberLocale: string): UiKitLabels {
1883
1913
  users: "Benutzer",
1884
1914
  usersNew: (count) => `${n(count)} neu in 7 Tagen`,
1885
1915
  },
1916
+ overviewError: "Die Übersicht konnte nicht geladen werden.",
1917
+ retry: "Erneut versuchen",
1886
1918
  tools: "Werkzeuge",
1887
1919
  },
1888
1920
  // 0.42.0 — docs/top-bar-harmonization.md §3.1: the top bar's shared parts. keksdose's
@@ -1896,6 +1928,9 @@ export function germanLabels(numberLocale: string): UiKitLabels {
1896
1928
  allTours: "Alle Touren…",
1897
1929
  badgeNew: "Neu",
1898
1930
  completed: "Abgeschlossen",
1931
+ badgeUpdated: "Aktualisiert",
1932
+ updatedDot: "Aktualisierte Touren",
1933
+ newAndUpdated: "Neu und aktualisiert",
1899
1934
  },
1900
1935
  sync: {
1901
1936
  blocked: (count) =>
@@ -25,6 +25,7 @@ import type { CharacterCountLabels, PasswordRevealLabels } from "../components/f
25
25
  import type { TabsLabels } from "../components/tabs";
26
26
  import type { WizardLabels } from "../wizard/types";
27
27
  import type { TourLabels } from "../tour/tour";
28
+ import type { TourCatalogueLabels } from "../tour/tour-catalogue-labels";
28
29
  import type { CommandPaletteLabels } from "../search/command-palette";
29
30
  import type { GlobalSearchLabels } from "../search/global-search";
30
31
  import type { MonthPickerLabels } from "../components/month-picker";
@@ -321,6 +322,8 @@ export interface UiKitLabels {
321
322
  file: FileLabels;
322
323
  wizard: WizardLabels;
323
324
  tour: TourLabels;
325
+ /** 0.43.0: the tours page — `TourCatalogue` and its card (docs/tours-harmonization.md §3). */
326
+ tourCatalogue: TourCatalogueLabels;
324
327
  commandPalette: CommandPaletteLabels;
325
328
  globalSearch: GlobalSearchLabels;
326
329
  seriesChart: SeriesChartLabels;
@@ -222,6 +222,19 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
222
222
  waiting: (count) => `${n(count)} waiting`,
223
223
  tiles: { ...d.adminArea.tiles, usersNew: (count) => `${n(count)} new in 7 days` },
224
224
  },
225
+ // 0.43.0: the tours page's counts (docs/tours-harmonization.md §3).
226
+ tourCatalogue: {
227
+ ...d.tourCatalogue,
228
+ groupAll: (count) => `All topics (${n(count)})`,
229
+ groupOption: (group, count) => `${group} (${n(count)})`,
230
+ stateAll: (count) => `All (${n(count)})`,
231
+ stateTodo: (count) => `Not taken (${n(count)})`,
232
+ stateDone: (count) => `Completed (${n(count)})`,
233
+ showing: (shown, total) => `Showing ${n(shown)} of ${n(total)} tours`,
234
+ points: (count) => `+${n(count)} pts`,
235
+ steps: (count) => (count === 1 ? "1 step" : `${n(count)} steps`),
236
+ duration: (minutes) => `About ${n(minutes)} min`,
237
+ },
225
238
  // 0.42.0: the top bar's three counts (docs/top-bar-harmonization.md §3.1).
226
239
  topBarParts: {
227
240
  ...d.topBarParts,
@@ -609,6 +609,34 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
609
609
  awaitClickHint: "Haga clic en el elemento resaltado para continuar",
610
610
  step: (current, total) => `${n(current)} / ${n(total)}`,
611
611
  },
612
+ // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page.
613
+ tourCatalogue: {
614
+ searchLabel: "Buscar visitas",
615
+ searchPlaceholder: "Buscar visitas…",
616
+ groupLabel: "Tema",
617
+ groupAll: (count) => `Todos los temas (${n(count)})`,
618
+ groupOption: (group, count) => `${group} (${n(count)})`,
619
+ stateLabel: "Filtrar visitas",
620
+ stateAll: (count) => `Todas (${n(count)})`,
621
+ stateTodo: (count) => `Pendientes (${n(count)})`,
622
+ stateDone: (count) => `Completadas (${n(count)})`,
623
+ showing: (shown, total) => `Mostrando ${n(shown)} de ${n(total)} visitas`,
624
+ noMatches: "Ninguna visita coincide",
625
+ noMatchesHint: "Pruebe con otra palabra o borre los filtros.",
626
+ clearFilters: "Borrar filtros",
627
+ badgeNew: "Nuevo",
628
+ badgeUpdated: "Actualizado",
629
+ resetOn: (date, reason) => (reason ? `Restablecida el ${date}: ${reason}` : `Restablecida el ${date}`),
630
+ points: (count) => `+${n(count)} pts`,
631
+ steps: (count) => (count === 1 ? `${n(count)} paso` : `${n(count)} pasos`),
632
+ duration: (minutes) => `Unos ${n(minutes)} min`,
633
+ completedOn: (date) => `Completada el ${date}`,
634
+ completed: "Completada",
635
+ notCompleted: "No completada",
636
+ markUndone: "Marcar como no completada",
637
+ start: "Empezar",
638
+ replay: "Repetir",
639
+ },
612
640
  commandPalette: {
613
641
  clear: "Borrar búsqueda",
614
642
  submit: "Buscar",
@@ -1727,6 +1755,8 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
1727
1755
  cancelConfirmBody: (date) => `Su suscripción termina el ${date}. Hasta entonces, todo funciona como antes.`,
1728
1756
  cancelConfirmAction: "Cancelar ahora la suscripción",
1729
1757
  keepSubscription: "Mantener la suscripción",
1758
+ portalOpensNewTab: (provider, app) =>
1759
+ `Los pagos y las facturas se abren en ${provider} en una pestaña nueva; ${app ? `${app} sigue abierto aquí` : "esta página sigue abierta"}.`,
1730
1760
  grantWarningTitle: "Su acceso gratuito continúa",
1731
1761
  grantWarningBody:
1732
1762
  "Su acceso gratuito no tiene fecha de fin. Mientras dure, una compra no cambia su plan: pagaría por un plan que no se aplica.",
@@ -1812,7 +1842,7 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
1812
1842
  sections: {
1813
1843
  overview: { title: "Resumen", help: "Lo que está pendiente, de un vistazo." },
1814
1844
  users: { title: "Usuarios", help: "Quién tiene una cuenta y qué puede hacer cada uno." },
1815
- invitations: { title: "Invitaciones", help: "Los enlaces con los que entra una persona nueva." },
1845
+ invitations: { title: "Invitaciones", help: "Invite a personas por correo electrónico en su idioma; reenvíe o revoque las invitaciones aún pendientes." },
1816
1846
  billing: { title: "Facturación", help: "El plan, el pago y las facturas." },
1817
1847
  activity: { title: "Actividad", help: "Lo que hicieron los administradores, lo más reciente primero." },
1818
1848
  },
@@ -1823,6 +1853,8 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
1823
1853
  users: "Usuarios",
1824
1854
  usersNew: (count) => `${n(count)} ${plural(count, "nuevo", "nuevos")} en 7 días`,
1825
1855
  },
1856
+ overviewError: "No se pudo cargar el resumen.",
1857
+ retry: "Reintentar",
1826
1858
  tools: "Herramientas",
1827
1859
  },
1828
1860
  // 0.42.0 — docs/top-bar-harmonization.md §3.1: the top bar's shared parts; "plan" as
@@ -1836,6 +1868,9 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
1836
1868
  allTours: "Todas las visitas…",
1837
1869
  badgeNew: "Nuevo",
1838
1870
  completed: "Completada",
1871
+ badgeUpdated: "Actualizado",
1872
+ updatedDot: "Visitas actualizadas",
1873
+ newAndUpdated: "Nuevas y actualizadas",
1839
1874
  },
1840
1875
  sync: {
1841
1876
  blocked: (count) =>
@@ -624,6 +624,34 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
624
624
  awaitClickHint: "Cliquez sur l’élément mis en évidence pour continuer",
625
625
  step: (current, total) => `${n(current)} / ${n(total)}`,
626
626
  },
627
+ // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page. keksdose's reviewed words.
628
+ tourCatalogue: {
629
+ searchLabel: "Rechercher une visite",
630
+ searchPlaceholder: "Rechercher une visite…",
631
+ groupLabel: "Thème",
632
+ groupAll: (count) => `Tous les thèmes (${n(count)})`,
633
+ groupOption: (group, count) => `${group} (${n(count)})`,
634
+ stateLabel: "Filtrer les visites",
635
+ stateAll: (count) => `Toutes (${n(count)})`,
636
+ stateTodo: (count) => `Non suivies (${n(count)})`,
637
+ stateDone: (count) => `Terminées (${n(count)})`,
638
+ showing: (shown, total) => `${n(shown)} visites sur ${n(total)}`,
639
+ noMatches: "Aucune visite ne correspond",
640
+ noMatchesHint: "Essayez un autre mot, ou effacez le filtre.",
641
+ clearFilters: "Effacer les filtres",
642
+ badgeNew: "Nouveau",
643
+ badgeUpdated: "Mise à jour",
644
+ resetOn: (date, reason) => (reason ? `Réinitialisée le ${date}\u202f: ${reason}` : `Réinitialisée le ${date}`),
645
+ points: (count) => `+${n(count)} pts`,
646
+ steps: (count) => (count === 1 ? `${n(count)} étape` : `${n(count)} étapes`),
647
+ duration: (minutes) => `Environ ${n(minutes)} min`,
648
+ completedOn: (date) => `Terminée le ${date}`,
649
+ completed: "Terminée",
650
+ notCompleted: "Non terminée",
651
+ markUndone: "Marquer comme non terminée",
652
+ start: "Démarrer",
653
+ replay: "Rejouer",
654
+ },
627
655
  commandPalette: {
628
656
  clear: "Effacer la recherche",
629
657
  submit: "Rechercher",
@@ -1762,6 +1790,8 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
1762
1790
  `Votre abonnement prendra fin le ${date}. D’ici là, tout fonctionne comme avant.`,
1763
1791
  cancelConfirmAction: "Résilier maintenant",
1764
1792
  keepSubscription: "Conserver l’abonnement",
1793
+ portalOpensNewTab: (provider, app) =>
1794
+ `Le paiement et les factures s’ouvrent chez ${provider} dans un nouvel onglet\u202f; ${app ? `${app} reste ouvert ici` : "cette page reste ouverte"}.`,
1765
1795
  grantWarningTitle: "Votre accès gratuit continue",
1766
1796
  grantWarningBody:
1767
1797
  "Votre accès gratuit n’a pas de date de fin. Tant qu’il dure, un achat ne change pas votre formule : vous paieriez pour une formule qui ne s’applique pas.",
@@ -1851,7 +1881,7 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
1851
1881
  sections: {
1852
1882
  overview: { title: "Vue d’ensemble", help: "Ce qui est en attente, en un coup d’œil." },
1853
1883
  users: { title: "Utilisateurs", help: "Qui a un compte, et ce que chacun peut faire." },
1854
- invitations: { title: "Invitations", help: "Les liens qui permettent à une nouvelle personne d’entrer." },
1884
+ invitations: { title: "Invitations", help: "Invitez des personnes par e-mail dans leur langue\u202f; renvoyez ou révoquez les invitations encore ouvertes." },
1855
1885
  billing: { title: "Facturation", help: "L’offre, le paiement et les factures." },
1856
1886
  activity: { title: "Activité", help: "Ce que les administrateurs ont fait, du plus récent au plus ancien." },
1857
1887
  },
@@ -1862,6 +1892,8 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
1862
1892
  users: "Utilisateurs",
1863
1893
  usersNew: (count) => `${n(count)} ${plural(count, "nouveau", "nouveaux")} en 7 jours`,
1864
1894
  },
1895
+ overviewError: "La vue d’ensemble n’a pas pu être chargée.",
1896
+ retry: "Réessayer",
1865
1897
  tools: "Outils",
1866
1898
  },
1867
1899
  // 0.42.0 — docs/top-bar-harmonization.md §3.1: the top bar's shared parts, keksdose's
@@ -1875,6 +1907,9 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
1875
1907
  allTours: "Toutes les visites…",
1876
1908
  badgeNew: "Nouveau",
1877
1909
  completed: "Terminée",
1910
+ badgeUpdated: "Mise à jour",
1911
+ updatedDot: "Visites mises à jour",
1912
+ newAndUpdated: "Nouvelles et mises à jour",
1878
1913
  },
1879
1914
  sync: {
1880
1915
  blocked: (count) =>
@@ -611,6 +611,34 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
611
611
  awaitClickHint: "A folytatáshoz kattintson a kiemelt elemre",
612
612
  step: (current, total) => `${n(current)} / ${n(total)}`,
613
613
  },
614
+ // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page.
615
+ tourCatalogue: {
616
+ searchLabel: "Bemutatók keresése",
617
+ searchPlaceholder: "Bemutatók keresése…",
618
+ groupLabel: "Téma",
619
+ groupAll: (count) => `Összes téma (${n(count)})`,
620
+ groupOption: (group, count) => `${group} (${n(count)})`,
621
+ stateLabel: "Bemutatók szűrése",
622
+ stateAll: (count) => `Összes (${n(count)})`,
623
+ stateTodo: (count) => `Teendő (${n(count)})`,
624
+ stateDone: (count) => `Elvégezve (${n(count)})`,
625
+ showing: (shown, total) => `${n(shown)} / ${n(total)} bemutató látható`,
626
+ noMatches: "Nincs megfelelő bemutató",
627
+ noMatchesHint: "Próbáljon egy másik szót, vagy törölje a szűrőket.",
628
+ clearFilters: "Szűrők törlése",
629
+ badgeNew: "Új",
630
+ badgeUpdated: "Frissítve",
631
+ resetOn: (date, reason) => (reason ? `Visszaállítva: ${date} – ${reason}` : `Visszaállítva: ${date}`),
632
+ points: (count) => `+${n(count)} pont`,
633
+ steps: (count) => `${n(count)} lépés`,
634
+ duration: (minutes) => `Kb. ${n(minutes)} perc`,
635
+ completedOn: (date) => `Elvégezve: ${date}`,
636
+ completed: "Elvégezve",
637
+ notCompleted: "Még nincs elvégezve",
638
+ markUndone: "Megjelölés el nem végzettként",
639
+ start: "Indítás",
640
+ replay: "Újra",
641
+ },
614
642
  commandPalette: {
615
643
  clear: "Keresés törlése",
616
644
  submit: "Keresés",
@@ -1730,6 +1758,8 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
1730
1758
  cancelConfirmBody: (date) => `Az előfizetése ekkor ér véget: ${date}. Addig minden a megszokott módon működik.`,
1731
1759
  cancelConfirmAction: "Lemondás most",
1732
1760
  keepSubscription: "Előfizetés megtartása",
1761
+ portalOpensNewTab: (provider, app) =>
1762
+ `A fizetés és a számlák új lapon nyílnak meg a(z) ${provider} oldalán; ${app ? `a(z) ${app} itt nyitva marad` : "ez az oldal nyitva marad"}.`,
1733
1763
  grantWarningTitle: "Az ingyenes hozzáférése továbbra is él",
1734
1764
  grantWarningBody:
1735
1765
  "Az ingyenes hozzáférésének nincs lejárati dátuma. Amíg tart, a vásárlás nem változtatja meg a csomagját: olyan csomagért fizetne, amely nem érvényes.",
@@ -1818,7 +1848,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
1818
1848
  sections: {
1819
1849
  overview: { title: "Áttekintés", help: "Ami most várakozik, egy pillantásra." },
1820
1850
  users: { title: "Felhasználók", help: "Kinek van fiókja, és ki mit tehet." },
1821
- invitations: { title: "Meghívások", help: "A linkek, amelyekkel új személy csatlakozhat." },
1851
+ invitations: { title: "Meghívások", help: "Hívjon meg embereket e-mailben a saját nyelvükön; küldje újra vagy vonja vissza a még nyitott meghívókat." },
1822
1852
  billing: { title: "Számlázás", help: "A csomag, a fizetés és a számlák." },
1823
1853
  activity: { title: "Tevékenység", help: "Mit tettek az adminisztrátorok, a legújabb elöl." },
1824
1854
  },
@@ -1829,6 +1859,8 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
1829
1859
  users: "Felhasználók",
1830
1860
  usersNew: (count) => `${n(count)} új az elmúlt 7 napban`,
1831
1861
  },
1862
+ overviewError: "Az áttekintést nem sikerült betölteni.",
1863
+ retry: "Próbálja újra",
1832
1864
  tools: "Eszközök",
1833
1865
  },
1834
1866
  // 0.42.0 — docs/top-bar-harmonization.md §3.1: the top bar's shared parts; "csomag" as
@@ -1842,6 +1874,9 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
1842
1874
  allTours: "Összes bemutató…",
1843
1875
  badgeNew: "Új",
1844
1876
  completed: "Elvégezve",
1877
+ badgeUpdated: "Frissítve",
1878
+ updatedDot: "Frissített bemutatók",
1879
+ newAndUpdated: "Új és frissített",
1845
1880
  },
1846
1881
  sync: {
1847
1882
  blocked: (count) => `${n(count)} módosítás nem küldhető el erről az eszközről`,