@eifi1/ui-kit 0.41.0 → 0.43.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 +26 -7
- package/dist/admin/admin-area-labels.d.ts +59 -0
- package/dist/admin/admin-area-labels.js +35 -0
- package/dist/admin/admin-area-labels.js.map +1 -0
- package/dist/admin/admin-forbidden.d.ts +35 -0
- package/dist/admin/admin-forbidden.js +36 -0
- package/dist/admin/admin-forbidden.js.map +1 -0
- package/dist/admin/admin-overview.d.ts +110 -0
- package/dist/admin/admin-overview.js +92 -0
- package/dist/admin/admin-overview.js.map +1 -0
- package/dist/admin/admin-sections.d.ts +94 -0
- package/dist/admin/admin-sections.js +81 -0
- package/dist/admin/admin-sections.js.map +1 -0
- package/dist/admin/admin-tool-links.d.ts +48 -0
- package/dist/admin/admin-tool-links.js +31 -0
- package/dist/admin/admin-tool-links.js.map +1 -0
- package/dist/billing/billing-banner.d.ts +33 -2
- package/dist/billing/billing-banner.js +29 -3
- package/dist/billing/billing-banner.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +36 -0
- package/dist/billing/billing-labels.js +10 -0
- package/dist/billing/billing-labels.js.map +1 -1
- package/dist/billing/billing-overview.d.ts +33 -0
- package/dist/billing/billing-overview.js +6 -0
- package/dist/billing/billing-overview.js.map +1 -1
- package/dist/billing/billing-standing.d.ts +8 -0
- package/dist/billing/billing-standing.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/cancel-subscription.d.ts +46 -0
- package/dist/billing/cancel-subscription.js +57 -0
- package/dist/billing/cancel-subscription.js.map +1 -0
- package/dist/billing/plan-card.d.ts +45 -1
- package/dist/billing/plan-card.js +53 -2
- package/dist/billing/plan-card.js.map +1 -1
- package/dist/billing/plan-switch.d.ts +10 -1
- package/dist/billing/plan-switch.js +22 -13
- package/dist/billing/plan-switch.js.map +1 -1
- package/dist/billing/provider-page.d.ts +40 -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 +68 -4
- package/dist/billing/subscription-actions.js +65 -5
- package/dist/billing/subscription-actions.js.map +1 -1
- package/dist/components/account-chips.d.ts +50 -2
- package/dist/components/account-chips.js +33 -0
- package/dist/components/account-chips.js.map +1 -1
- package/dist/components/empty-state.d.ts +2 -1
- package/dist/components/empty-state.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 +7 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +113 -0
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +12 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +142 -1
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +110 -0
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +109 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +110 -0
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +110 -0
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +109 -0
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +34 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +24 -2
- package/dist/index.js +29 -1
- 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/settings/settings-layout.d.ts +7 -1
- package/dist/settings/settings-layout.js +35 -27
- package/dist/settings/settings-layout.js.map +1 -1
- package/dist/shell/account-menu-language.d.ts +46 -0
- package/dist/shell/account-menu-language.js +64 -0
- package/dist/shell/account-menu-language.js.map +1 -0
- package/dist/shell/admin-menu.d.ts +69 -0
- package/dist/shell/admin-menu.js +64 -0
- package/dist/shell/admin-menu.js.map +1 -0
- package/dist/shell/app-shell.d.ts +10 -0
- package/dist/shell/app-shell.js +22 -4
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/dev-switchers.d.ts +53 -0
- package/dist/shell/dev-switchers.js +107 -0
- package/dist/shell/dev-switchers.js.map +1 -0
- package/dist/shell/sync-status-indicator.d.ts +86 -0
- package/dist/shell/sync-status-indicator.js +170 -0
- package/dist/shell/sync-status-indicator.js.map +1 -0
- package/dist/shell/top-bar-actions.d.ts +49 -0
- package/dist/shell/top-bar-actions.js +31 -0
- package/dist/shell/top-bar-actions.js.map +1 -0
- package/dist/shell/top-bar-parts-labels.d.ts +90 -0
- package/dist/shell/top-bar-parts-labels.js +51 -0
- package/dist/shell/top-bar-parts-labels.js.map +1 -0
- package/dist/shell/topbar-action-menu.d.ts +13 -1
- package/dist/shell/topbar-action-menu.js +5 -2
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell/topbar-controls.d.ts +16 -3
- package/dist/shell/topbar-controls.js +5 -1
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/shell/tour-menu.d.ts +67 -0
- package/dist/shell/tour-menu.js +87 -0
- package/dist/shell/tour-menu.js.map +1 -0
- package/dist/shell.d.ts +16 -0
- package/dist/shell.js +21 -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/use-tour-progress.d.ts +61 -0
- package/dist/tour/use-tour-progress.js +116 -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 +18 -0
- package/dist/tour.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/admin-area-labels.ts +91 -0
- package/src/admin/admin-forbidden.tsx +75 -0
- package/src/admin/admin-overview.tsx +217 -0
- package/src/admin/admin-sections.tsx +198 -0
- package/src/admin/admin-tool-links.tsx +84 -0
- package/src/billing/billing-banner.tsx +57 -6
- package/src/billing/billing-labels.ts +59 -0
- package/src/billing/billing-overview.ts +37 -0
- package/src/billing/billing-standing.ts +8 -0
- package/src/billing/cancel-request.ts +75 -0
- package/src/billing/cancel-subscription.tsx +102 -0
- package/src/billing/plan-card.tsx +97 -1
- package/src/billing/plan-switch.tsx +16 -0
- package/src/billing/provider-page.ts +75 -0
- package/src/billing/subscription-actions.tsx +155 -20
- package/src/components/account-chips.tsx +89 -2
- package/src/components/empty-state.tsx +2 -1
- package/src/feedback/feedback-menu.tsx +8 -3
- package/src/hooks/use-search-param-state.ts +6 -1
- package/src/i18n/defaults.ts +6 -0
- package/src/i18n/german.ts +127 -0
- package/src/i18n/kit-labels.tsx +12 -0
- package/src/i18n/locales/en.ts +32 -0
- package/src/i18n/locales/es.ts +121 -0
- package/src/i18n/locales/fr.ts +121 -0
- package/src/i18n/locales/hu.ts +119 -0
- package/src/i18n/locales/it.ts +122 -0
- package/src/i18n/locales/zh.ts +114 -0
- package/src/i18n/review.ts +33 -0
- package/src/index.ts +38 -2
- package/src/search/global-search.tsx +5 -0
- package/src/settings/settings-catalogue.ts +10 -2
- package/src/settings/settings-layout.tsx +58 -34
- package/src/shell/account-menu-language.tsx +116 -0
- package/src/shell/admin-menu.tsx +146 -0
- package/src/shell/app-shell.tsx +44 -3
- package/src/shell/dev-switchers.tsx +179 -0
- package/src/shell/sync-status-indicator.tsx +301 -0
- package/src/shell/top-bar-actions.tsx +79 -0
- package/src/shell/top-bar-parts-labels.ts +142 -0
- package/src/shell/topbar-action-menu.tsx +15 -2
- package/src/shell/topbar-controls.tsx +20 -3
- package/src/shell/tour-menu.tsx +179 -0
- package/src/shell.ts +27 -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/use-tour-progress.ts +198 -0
- package/src/tour/use-tour-url-sync.ts +221 -0
- package/src/tour.ts +11 -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
|
-
import type { PortalTarget } from "./billing-overview";
|
|
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. */
|
|
@@ -35,8 +54,54 @@ export interface SubscriptionActionsProps extends Omit<ComponentPropsWithRef<"di
|
|
|
35
54
|
* (§12.26). Default true (0.32's behaviour).
|
|
36
55
|
*/
|
|
37
56
|
atProvider?: boolean;
|
|
38
|
-
/**
|
|
39
|
-
|
|
57
|
+
/**
|
|
58
|
+
* 0.42 (§16.2, server-kit 0.14): the overview's `portal`. `"buyer_only"` — the row has
|
|
59
|
+
* a buyer and the viewer isn't them, so the portal would answer `403
|
|
60
|
+
* billing_portal_buyer_only`: "Payment and invoices" becomes a caption naming the buyer
|
|
61
|
+
* (`buyerHolds`, no button), and "Cancel subscription" calls {@link onCancelInApp}
|
|
62
|
+
* (no portal at all — `cancelHref`, `onCancel` and `onPortal` are not used). `"open"`
|
|
63
|
+
* (default): as before.
|
|
64
|
+
*/
|
|
65
|
+
portal?: BillingPortalAccess;
|
|
66
|
+
/** 0.42: the overview's `buyer_name`, for the caption under `"buyer_only"`. Empty: "…with
|
|
67
|
+
* the admin who bought the subscription". */
|
|
68
|
+
buyerName?: string | null;
|
|
69
|
+
/**
|
|
70
|
+
* 0.42 (§16.2 decision 2): "Cancel subscription" in the app — open
|
|
71
|
+
* `CancelSubscriptionConfirm`, whose Confirm is `POST /billing/cancel`. The cancel under
|
|
72
|
+
* `"buyer_only"`. Under `"open"` it is the last resort, used only when no portal cancel
|
|
73
|
+
* (`cancelHref`, `onCancel`, `onPortal`) is given — a person-payer app that mounts the
|
|
74
|
+
* route and not the portal's cancel.
|
|
75
|
+
*/
|
|
76
|
+
onCancelInApp?: () => void;
|
|
77
|
+
/** 0.42 (§16.7 C): the overview's `cancel_at_period_end` — a cancel is scheduled. With
|
|
78
|
+
* {@link onResume}, "Keep subscription" takes the cancel's place. */
|
|
79
|
+
cancelAtPeriodEnd?: boolean;
|
|
80
|
+
/** 0.42 (§16.7 C): "Keep subscription", the app's `POST /billing/resume` — shown in
|
|
81
|
+
* the cancel's place while {@link cancelAtPeriodEnd} is true. Without it, the cancel
|
|
82
|
+
* stays as before. */
|
|
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;
|
|
103
|
+
/** Which one is under way: its spinner, no second press. `"resume"` since 0.42. */
|
|
104
|
+
pending?: "manage" | "cancel" | "resume" | null;
|
|
40
105
|
/** Prop > `<UiKitProvider labels={{ billing }}>` > English. */
|
|
41
106
|
labels?: LabelOverride<BillingLabels>;
|
|
42
107
|
}
|
|
@@ -56,6 +121,18 @@ export interface SubscriptionActionsProps extends Omit<ComponentPropsWithRef<"di
|
|
|
56
121
|
* Before the payer has reached the provider (`atProvider={false}`, a trial without a
|
|
57
122
|
* card, a beta) there is no portal to open: the part says so in a caption instead of
|
|
58
123
|
* offering two buttons that could only end in `409 billing_not_at_provider`.
|
|
124
|
+
*
|
|
125
|
+
* 0.42 (§16): where the company's buyer is another admin (`portal="buyer_only"`), the
|
|
126
|
+
* portal stays the buyer's — "Payment and invoices" is a caption that names them, and
|
|
127
|
+
* "Cancel subscription" is the in-app cancel (`onCancelInApp`). While a cancel is
|
|
128
|
+
* scheduled (`cancelAtPeriodEnd`) and `onResume` is given, "Keep subscription" stands in
|
|
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.
|
|
59
136
|
*/
|
|
60
137
|
export function SubscriptionActions({
|
|
61
138
|
manageHref,
|
|
@@ -64,12 +141,22 @@ export function SubscriptionActions({
|
|
|
64
141
|
onCancel,
|
|
65
142
|
onPortal,
|
|
66
143
|
atProvider = true,
|
|
144
|
+
portal = "open",
|
|
145
|
+
buyerName,
|
|
146
|
+
onCancelInApp,
|
|
147
|
+
cancelAtPeriodEnd = false,
|
|
148
|
+
onResume,
|
|
149
|
+
cancelRequested = null,
|
|
150
|
+
portalNewTab = true,
|
|
151
|
+
providerName = "Paddle",
|
|
152
|
+
appName,
|
|
67
153
|
pending,
|
|
68
154
|
labels: labelsProp,
|
|
69
155
|
className,
|
|
70
156
|
...rest
|
|
71
157
|
}: SubscriptionActionsProps) {
|
|
72
158
|
const labels = useBillingLabels(labelsProp);
|
|
159
|
+
const common = useKitLabels("common", DEFAULT_COMMON_LABELS);
|
|
73
160
|
if (!atProvider) {
|
|
74
161
|
return (
|
|
75
162
|
<div {...rest} data-at-provider="false" className={cn("text-sm text-muted", className)}>
|
|
@@ -77,33 +164,81 @@ export function SubscriptionActions({
|
|
|
77
164
|
</div>
|
|
78
165
|
);
|
|
79
166
|
}
|
|
167
|
+
const buyerOnly = portal === "buyer_only";
|
|
80
168
|
const manageCall = onManage ?? (onPortal ? () => onPortal("overview") : undefined);
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
169
|
+
const portalCancel = onCancel ?? (onPortal ? () => onPortal("cancel") : undefined);
|
|
170
|
+
// Under "buyer_only" the in-app cancel only; under "open" the portal's first, the
|
|
171
|
+
// in-app cancel only where no way to the portal's is given.
|
|
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);
|
|
187
|
+
const manage = buyerOnly ? (
|
|
188
|
+
<p className="basis-full text-sm text-muted" data-buyer-holds="">
|
|
189
|
+
{labels.buyerHolds(buyerName || undefined)}
|
|
190
|
+
</p>
|
|
191
|
+
) : manageHref !== undefined ? (
|
|
192
|
+
<Button href={manageHref} variant="secondary" external={newTab || undefined} data-portal-action="manage">
|
|
193
|
+
{words(labels.manage, newTab, true)}
|
|
194
|
+
</Button>
|
|
195
|
+
) : manageCall !== undefined ? (
|
|
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)}
|
|
205
|
+
</Button>
|
|
206
|
+
) : null;
|
|
92
207
|
const cancel =
|
|
93
|
-
|
|
94
|
-
<Button
|
|
95
|
-
{labels.
|
|
208
|
+
scheduled && onResume !== undefined ? (
|
|
209
|
+
<Button type="button" variant="link" onClick={onResume} pending={pending === "resume"} data-keep-subscription="">
|
|
210
|
+
{labels.keepSubscription}
|
|
211
|
+
</Button>
|
|
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)}
|
|
96
215
|
</Button>
|
|
97
216
|
) : cancelCall !== undefined ? (
|
|
98
|
-
<Button
|
|
99
|
-
|
|
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)}
|
|
100
227
|
</Button>
|
|
101
228
|
) : null;
|
|
102
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)));
|
|
103
233
|
return (
|
|
104
234
|
<div {...rest} className={cn("flex flex-wrap items-center gap-x-4 gap-y-2", className)}>
|
|
105
235
|
{manage}
|
|
106
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
|
+
)}
|
|
107
242
|
</div>
|
|
108
243
|
);
|
|
109
244
|
}
|
|
@@ -6,7 +6,7 @@ import type { DateInput, FormatDateStyle, FormatRelativeTimeOptions } from "../l
|
|
|
6
6
|
import { useKitDateFormatter, useKitLabels, useKitLocale, type LabelOverride } from "../i18n/kit-labels";
|
|
7
7
|
import { toLocalIso } from "../lib/dates";
|
|
8
8
|
import { Chip } from "./chip";
|
|
9
|
-
import type { ChipShape, ChipSize, ChipTone, ChipVariant } from "./chip";
|
|
9
|
+
import type { ChipLinkProps, ChipShape, ChipSize, ChipTone, ChipVariant } from "./chip";
|
|
10
10
|
import type { DataTableColumn } from "./data-table";
|
|
11
11
|
import { DEFAULT_ACCOUNT_STATE_LABELS } from "./account-chips-labels";
|
|
12
12
|
|
|
@@ -47,8 +47,29 @@ interface AccountChipLook {
|
|
|
47
47
|
export interface RoleDefinition {
|
|
48
48
|
label: string;
|
|
49
49
|
tone?: ChipTone;
|
|
50
|
-
/**
|
|
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;
|
|
59
|
+
/**
|
|
60
|
+
* 0.42.0 (docs/admin-harmonization.md §2.1, settings §12.3): the role's tool — an
|
|
61
|
+
* admin's `/admin`, a reviewer's `/translations`. {@link AccountRoleChip} is a link to
|
|
62
|
+
* it; a role without one gets a plain chip. {@link RoleChip} ignores it: a roster's
|
|
63
|
+
* chips name a role, they are not a way in.
|
|
64
|
+
*/
|
|
65
|
+
href?: string;
|
|
66
|
+
/**
|
|
67
|
+
* 0.42.0 (keksdose build review): this role's look in {@link AccountRoleChip} — keksdose
|
|
68
|
+
* draws only ADMIN `solid` and its REVIEWER and MEMBER `soft`. Default `solid`, the
|
|
69
|
+
* header's look; the chip's own `variant` prop wins over it. {@link RoleChip} ignores
|
|
70
|
+
* it: a roster's column keeps one look, set on the column.
|
|
71
|
+
*/
|
|
72
|
+
variant?: ChipVariant;
|
|
52
73
|
}
|
|
53
74
|
|
|
54
75
|
/**
|
|
@@ -107,6 +128,72 @@ export function RoleChip<R extends string = string>({
|
|
|
107
128
|
);
|
|
108
129
|
}
|
|
109
130
|
|
|
131
|
+
/* ── AccountRoleChip ───────────────────────────────────────────────────────── */
|
|
132
|
+
|
|
133
|
+
export interface AccountRoleChipProps<R extends string = string> extends RoleChipProps<R> {
|
|
134
|
+
/**
|
|
135
|
+
* Runs on the way to the role's tool — the account menu's `close`, so the panel does
|
|
136
|
+
* not stay open over the page it just navigated to (keksdose F7). Only called on a
|
|
137
|
+
* link chip.
|
|
138
|
+
*/
|
|
139
|
+
onNavigate?: () => void;
|
|
140
|
+
/** Your router's link for the role's `href`. Default: the `<UiKitProvider
|
|
141
|
+
* linkComponent>`, then `<a>` — {@link Chip}'s `renderLink`. */
|
|
142
|
+
renderLink?: (props: ChipLinkProps) => ReactElement;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The role in the account menu's header (0.42.0, docs/admin-harmonization.md §2.1, §7):
|
|
147
|
+
* {@link RoleChip} in keksdose's look — `solid`, `sm`, `square`, `caps`, the role's icon
|
|
148
|
+
* and tone — made for `TopBarActionMenu`'s `header.extra`:
|
|
149
|
+
*
|
|
150
|
+
* extra: (close) => <AccountRoleChip value={me.role} roles={ROLES} onNavigate={close} />
|
|
151
|
+
*
|
|
152
|
+
* A role whose {@link RoleDefinition.href} is set is a shortcut to its tool: the chip is a
|
|
153
|
+
* link (through `renderLink`, else the provider's link, as {@link Chip} draws links) and
|
|
154
|
+
* calls `onNavigate` on the click. Any other role is a plain chip — every account shows
|
|
155
|
+
* its role (§7, "Always a badge"). The words and tones are the app's vocabulary; the kit
|
|
156
|
+
* adds none. `solid` stays the default, so a role the app gives no look still reads as
|
|
157
|
+
* the header's tag; a role that should sit quieter (keksdose's MEMBER and REVIEWER) sets
|
|
158
|
+
* {@link RoleDefinition.variant} `soft` in the vocabulary. Carries `data-role` with the key.
|
|
159
|
+
*/
|
|
160
|
+
export function AccountRoleChip<R extends string = string>({
|
|
161
|
+
value: role,
|
|
162
|
+
roles,
|
|
163
|
+
onNavigate,
|
|
164
|
+
renderLink,
|
|
165
|
+
size = "sm",
|
|
166
|
+
variant,
|
|
167
|
+
shape = "square",
|
|
168
|
+
caps = true,
|
|
169
|
+
className,
|
|
170
|
+
...rest
|
|
171
|
+
}: AccountRoleChipProps<R>) {
|
|
172
|
+
if (role === null || role === undefined || role === "") return null;
|
|
173
|
+
const def = roles[role];
|
|
174
|
+
const look = {
|
|
175
|
+
...rest,
|
|
176
|
+
"data-role": role,
|
|
177
|
+
tone: def?.tone ?? "neutral",
|
|
178
|
+
icon: def?.icon,
|
|
179
|
+
size,
|
|
180
|
+
// The prop, then the role's own look, then the header's `solid`.
|
|
181
|
+
variant: variant ?? def?.variant ?? "solid",
|
|
182
|
+
shape,
|
|
183
|
+
caps,
|
|
184
|
+
className,
|
|
185
|
+
} as const;
|
|
186
|
+
const label = def?.label ?? role;
|
|
187
|
+
if (def?.href) {
|
|
188
|
+
return (
|
|
189
|
+
<Chip {...look} href={def.href} renderLink={renderLink} onClick={onNavigate ? () => onNavigate() : undefined}>
|
|
190
|
+
{label}
|
|
191
|
+
</Chip>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return <Chip {...look}>{label}</Chip>;
|
|
195
|
+
}
|
|
196
|
+
|
|
110
197
|
/* ── AccountStateChip ──────────────────────────────────────────────────────── */
|
|
111
198
|
|
|
112
199
|
/**
|
|
@@ -17,8 +17,9 @@ export interface EmptyStateProps extends Omit<ComponentPropsWithRef<"div">, "chi
|
|
|
17
17
|
* keksdose's error boundary IS the page when it trips, and its `<h2>` went missing
|
|
18
18
|
* when it moved onto EmptyState — so the page's heading outline lost the one line
|
|
19
19
|
* that says what happened. Only the element changes; the look is the box's.
|
|
20
|
+
* `h1` (0.42.0) for an empty state that is the whole page — `AdminForbidden`.
|
|
20
21
|
*/
|
|
21
|
-
headingAs?: "h2" | "h3" | "h4" | "h5" | "h6";
|
|
22
|
+
headingAs?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
22
23
|
/** A glyph ABOVE the title — kastlan's InboxEmptyState (an inbox), keksdose's
|
|
23
24
|
* offline card (a cloud with a slash). Sized by the box (`[&_svg]:size-8`) and
|
|
24
25
|
* muted, so five call sites cannot pick five sizes; hidden from assistive tech,
|
|
@@ -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
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
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
|
-
|
|
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. */
|
package/src/i18n/defaults.ts
CHANGED
|
@@ -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";
|
|
@@ -98,6 +99,8 @@ import { DEFAULT_DEVICE_RESET_LABELS } from "../settings/device-reset-setting-la
|
|
|
98
99
|
import { DEFAULT_ROW_ACTIONS_LABELS } from "../components/row-actions-labels";
|
|
99
100
|
import { DEFAULT_APP_SHELL_MORE_LABELS } from "../shell/app-shell-labels";
|
|
100
101
|
import { DEFAULT_ACCOUNT_STATE_LABELS } from "../components/account-chips-labels";
|
|
102
|
+
import { DEFAULT_ADMIN_AREA_LABELS } from "../admin/admin-area-labels";
|
|
103
|
+
import { DEFAULT_TOP_BAR_PARTS_LABELS } from "../shell/top-bar-parts-labels";
|
|
101
104
|
import { DEFAULT_SHARE_CARD_LABELS } from "../components/share-card-labels";
|
|
102
105
|
import { DEFAULT_REAUTH_DIALOG_LABELS } from "../components/reauth-dialog-labels";
|
|
103
106
|
import { DEFAULT_SERVER_WAKE_LABELS } from "../components/server-wake-labels";
|
|
@@ -165,6 +168,7 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
|
|
|
165
168
|
file: DEFAULT_FILE_LABELS,
|
|
166
169
|
wizard: DEFAULT_WIZARD_LABELS,
|
|
167
170
|
tour: DEFAULT_TOUR_LABELS,
|
|
171
|
+
tourCatalogue: DEFAULT_TOUR_CATALOGUE_LABELS,
|
|
168
172
|
commandPalette: DEFAULT_COMMAND_PALETTE_LABELS,
|
|
169
173
|
globalSearch: DEFAULT_GLOBAL_SEARCH_LABELS,
|
|
170
174
|
seriesChart: DEFAULT_SERIES_CHART_LABELS,
|
|
@@ -256,4 +260,6 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
|
|
|
256
260
|
signChip: DEFAULT_SIGN_CHIP_LABELS,
|
|
257
261
|
columnMapper: DEFAULT_COLUMN_MAPPER_LABELS,
|
|
258
262
|
notifications: DEFAULT_NOTIFICATIONS_LABELS,
|
|
263
|
+
adminArea: DEFAULT_ADMIN_AREA_LABELS,
|
|
264
|
+
topBarParts: DEFAULT_TOP_BAR_PARTS_LABELS,
|
|
259
265
|
};
|
package/src/i18n/german.ts
CHANGED
|
@@ -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: "Versuchen Sie ein anderes Wort, oder setzen Sie die Filter zurück.",
|
|
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",
|
|
@@ -1729,6 +1757,12 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1729
1757
|
"Ihre Zahlung wird verarbeitet. Ihr Tarif beginnt, sobald sie bestätigt ist; diese Seite aktualisiert sich von selbst.",
|
|
1730
1758
|
choosePlan: "Tarif wählen",
|
|
1731
1759
|
updatePayment: "Zahlungsmethode aktualisieren",
|
|
1760
|
+
// 0.42 (§16.7 A): for an admin who isn't the buyer. "Das Admin-Konto", which gives the
|
|
1761
|
+
// reader no gender.
|
|
1762
|
+
paymentFailedAskBuyer: (buyer) =>
|
|
1763
|
+
buyer
|
|
1764
|
+
? `Die Zahlung ist fehlgeschlagen. ${buyer} kann die Zahlungsmethode aktualisieren.`
|
|
1765
|
+
: "Die Zahlung ist fehlgeschlagen. Die Zahlungsmethode kann über das Admin-Konto aktualisiert werden, mit dem das Abonnement abgeschlossen wurde.",
|
|
1732
1766
|
checkAgain: "Erneut prüfen",
|
|
1733
1767
|
lockReason: "Ihr Tarif ist abgelaufen. Wählen Sie einen Tarif, um wieder Änderungen vorzunehmen.",
|
|
1734
1768
|
// As `characterCount.limitReached` ("Zeichenlimit erreicht").
|
|
@@ -1763,6 +1797,22 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1763
1797
|
switchCredited: "Gutschrift auf die nächsten Belastungen",
|
|
1764
1798
|
switchNextCharge: (date) => `Nächste Belastung am ${date}`,
|
|
1765
1799
|
switchConfirm: "Tarif ändern",
|
|
1800
|
+
// 0.42 (§16): the portal for the buyer only, the in-app cancel, the grant warning.
|
|
1801
|
+
buyerHolds: (buyer) =>
|
|
1802
|
+
buyer
|
|
1803
|
+
? `Zahlungsmethode und Rechnungen liegen bei ${buyer}: Das Abonnement wurde über dieses Konto abgeschlossen.`
|
|
1804
|
+
: "Zahlungsmethode und Rechnungen liegen beim Admin-Konto, mit dem das Abonnement abgeschlossen wurde.",
|
|
1805
|
+
cancelConfirmTitle: "Abonnement kündigen",
|
|
1806
|
+
cancelConfirmBody: (date) => `Ihr Abonnement endet am ${date}. Bis dahin funktioniert alles wie bisher.`,
|
|
1807
|
+
// §312k BGB: the confirmation button says exactly this.
|
|
1808
|
+
cancelConfirmAction: "Jetzt kündigen",
|
|
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"}.`,
|
|
1812
|
+
grantWarningTitle: "Ihr kostenloser Zugang läuft weiter",
|
|
1813
|
+
grantWarningBody:
|
|
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.",
|
|
1815
|
+
grantWarningAction: "Trotzdem kaufen",
|
|
1766
1816
|
waitingChanges: (count) =>
|
|
1767
1817
|
count === 1
|
|
1768
1818
|
? `${n(count)} Änderung wartet auf einen Tarif`
|
|
@@ -1839,5 +1889,82 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1839
1889
|
more: "Mehr",
|
|
1840
1890
|
moreTitle: "Weitere Seiten",
|
|
1841
1891
|
},
|
|
1892
|
+
// 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Verwaltung"
|
|
1893
|
+
// as settings §5 names the page; "mit Administratorrechten" rather than "für
|
|
1894
|
+
// Administratoren", which would give the reader a gender (as `transferOwnership`).
|
|
1895
|
+
adminArea: {
|
|
1896
|
+
title: "Verwaltung",
|
|
1897
|
+
waiting: (count) => `${n(count)} offen`,
|
|
1898
|
+
forbiddenTitle: "Nur mit Administratorrechten",
|
|
1899
|
+
forbiddenBody: (appName) => `Hier wird ${appName} verwaltet. Ihr Konto kann diese Seite nicht öffnen.`,
|
|
1900
|
+
forbiddenHome: "Zur Startseite",
|
|
1901
|
+
forbiddenReviewer: "Übersetzungen prüfen",
|
|
1902
|
+
sections: {
|
|
1903
|
+
overview: { title: "Übersicht", help: "Was gerade wartet, auf einen Blick." },
|
|
1904
|
+
users: { title: "Benutzer", help: "Wer ein Konto hat und was jede Person darf." },
|
|
1905
|
+
invitations: { title: "Einladungen", help: "Laden Sie Personen per E-Mail in ihrer Sprache ein; senden Sie offene Einladungen erneut oder widerrufen Sie sie." },
|
|
1906
|
+
billing: { title: "Abrechnung", help: "Tarif, Zahlung und Rechnungen." },
|
|
1907
|
+
activity: { title: "Aktivität", help: "Was in der Verwaltung getan wurde, das Neueste zuerst." },
|
|
1908
|
+
},
|
|
1909
|
+
tiles: {
|
|
1910
|
+
feedbackWaiting: "Zu sichten",
|
|
1911
|
+
deletionRequests: "Löschanfragen",
|
|
1912
|
+
invitationsPending: "Offene Einladungen",
|
|
1913
|
+
users: "Benutzer",
|
|
1914
|
+
usersNew: (count) => `${n(count)} neu in 7 Tagen`,
|
|
1915
|
+
},
|
|
1916
|
+
overviewError: "Die Übersicht konnte nicht geladen werden.",
|
|
1917
|
+
retry: "Erneut versuchen",
|
|
1918
|
+
tools: "Werkzeuge",
|
|
1919
|
+
},
|
|
1920
|
+
// 0.42.0 — docs/top-bar-harmonization.md §3.1: the top bar's shared parts. keksdose's
|
|
1921
|
+
// reviewed sync words where they are not about its budgets; "Tarif" as `billing`.
|
|
1922
|
+
topBarParts: {
|
|
1923
|
+
tours: {
|
|
1924
|
+
menu: "Geführte Touren",
|
|
1925
|
+
newDot: "Neue Touren",
|
|
1926
|
+
thisPage: "Auf dieser Seite",
|
|
1927
|
+
newTours: "Neue Touren",
|
|
1928
|
+
allTours: "Alle Touren…",
|
|
1929
|
+
badgeNew: "Neu",
|
|
1930
|
+
completed: "Abgeschlossen",
|
|
1931
|
+
badgeUpdated: "Aktualisiert",
|
|
1932
|
+
updatedDot: "Aktualisierte Touren",
|
|
1933
|
+
newAndUpdated: "Neu und aktualisiert",
|
|
1934
|
+
},
|
|
1935
|
+
sync: {
|
|
1936
|
+
blocked: (count) =>
|
|
1937
|
+
count === 1
|
|
1938
|
+
? `${n(count)} Änderung kann von diesem Gerät nicht gesendet werden`
|
|
1939
|
+
: `${n(count)} Änderungen können von diesem Gerät nicht gesendet werden`,
|
|
1940
|
+
offline: "Offline – Änderungen werden auf diesem Gerät gespeichert und bei erneuter Verbindung synchronisiert",
|
|
1941
|
+
refused: "Der Server hat den letzten Abgleich abgelehnt",
|
|
1942
|
+
syncing: "Wird synchronisiert…",
|
|
1943
|
+
pending: (count) =>
|
|
1944
|
+
count === 1
|
|
1945
|
+
? `${n(count)} Änderung wartet auf Synchronisierung`
|
|
1946
|
+
: `${n(count)} Änderungen warten auf Synchronisierung`,
|
|
1947
|
+
syncedAt: (time) => `Synchronisiert um ${time}`,
|
|
1948
|
+
idle: "Aktuell",
|
|
1949
|
+
syncHint: "Tippen, um jetzt zu synchronisieren",
|
|
1950
|
+
offlineHint: "offline gibt es nichts zu synchronisieren",
|
|
1951
|
+
withHint: (state, hint) => `${state} – ${hint}`,
|
|
1952
|
+
scoped: (scope, state) => `${scope}: ${state}`,
|
|
1953
|
+
offlineShort: "Offline",
|
|
1954
|
+
syncingShort: "Sync läuft…",
|
|
1955
|
+
waitingShort: (count) => `${n(count)} ausstehend`,
|
|
1956
|
+
refusedShort: "Nicht synchronisiert",
|
|
1957
|
+
offlineUnsaved: "Offline – Änderungen können erst nach erneuter Verbindung gespeichert werden",
|
|
1958
|
+
saving: "Wird gespeichert…",
|
|
1959
|
+
},
|
|
1960
|
+
dev: {
|
|
1961
|
+
roleHeading: "Dev · Rolle",
|
|
1962
|
+
switchPlan: "Tarif wechseln",
|
|
1963
|
+
plan: (value) => `Tarif: ${value}`,
|
|
1964
|
+
planHeading: "Dev · Tarif",
|
|
1965
|
+
backTo: (role) => `Zurück zu ${role}`,
|
|
1966
|
+
billingOff: "Die Abrechnung ist lokal aus: Tarife ändern nichts",
|
|
1967
|
+
},
|
|
1968
|
+
},
|
|
1842
1969
|
};
|
|
1843
1970
|
}
|
package/src/i18n/kit-labels.tsx
CHANGED
|
@@ -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";
|
|
@@ -118,6 +119,8 @@ import type { PhoneInputLabels } from "../components/phone-input";
|
|
|
118
119
|
import type { SignChipLabels } from "../components/sign-chip";
|
|
119
120
|
import type { ColumnMapperLabels } from "../components/column-mapper";
|
|
120
121
|
import type { NotificationsLabels } from "../notifications/notifications-labels";
|
|
122
|
+
import type { AdminAreaLabels } from "../admin/admin-area-labels";
|
|
123
|
+
import type { TopBarPartsLabels } from "../shell/top-bar-parts-labels";
|
|
121
124
|
|
|
122
125
|
/**
|
|
123
126
|
* EVERY string the kit renders, as one typed tree — and an optional provider that
|
|
@@ -319,6 +322,8 @@ export interface UiKitLabels {
|
|
|
319
322
|
file: FileLabels;
|
|
320
323
|
wizard: WizardLabels;
|
|
321
324
|
tour: TourLabels;
|
|
325
|
+
/** 0.43.0: the tours page — `TourCatalogue` and its card (docs/tours-harmonization.md §3). */
|
|
326
|
+
tourCatalogue: TourCatalogueLabels;
|
|
322
327
|
commandPalette: CommandPaletteLabels;
|
|
323
328
|
globalSearch: GlobalSearchLabels;
|
|
324
329
|
seriesChart: SeriesChartLabels;
|
|
@@ -475,6 +480,13 @@ export interface UiKitLabels {
|
|
|
475
480
|
* device's push, the device list, and the unsubscribe page
|
|
476
481
|
* (docs/notifications-harmonization.md §4.2). */
|
|
477
482
|
notifications: NotificationsLabels;
|
|
483
|
+
/** 0.42.0: the admin area's shared words — the account menu's "Administration" row, the
|
|
484
|
+
* 403 page, the shared sections, the overview's tiles and the Tools list
|
|
485
|
+
* (docs/admin-harmonization.md §3.1). */
|
|
486
|
+
adminArea: AdminAreaLabels;
|
|
487
|
+
/** 0.42.0: the top bar's shared parts — `TourMenu`, `SyncStatusIndicator`, `DevSwitchers`
|
|
488
|
+
* (docs/top-bar-harmonization.md §3.1). */
|
|
489
|
+
topBarParts: TopBarPartsLabels;
|
|
478
490
|
}
|
|
479
491
|
|
|
480
492
|
/**
|