@eifi1/ui-kit 0.45.0 → 0.46.1
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 -14
- package/dist/admin/admin-area-labels.d.ts +9 -0
- package/dist/admin/admin-area-labels.js +5 -1
- package/dist/admin/admin-area-labels.js.map +1 -1
- package/dist/admin/admin-overview.d.ts +16 -1
- package/dist/admin/admin-overview.js +12 -1
- package/dist/admin/admin-overview.js.map +1 -1
- package/dist/admin/mail-test-card.d.ts +3 -2
- package/dist/admin/mail-test-card.js +3 -1
- package/dist/admin/mail-test-card.js.map +1 -1
- package/dist/admin/user-roster.d.ts +6 -2
- package/dist/admin/user-roster.js +7 -3
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/components/appearance-labels.d.ts +10 -0
- package/dist/components/appearance-labels.js +6 -1
- package/dist/components/appearance-labels.js.map +1 -1
- package/dist/components/data-table-column-settings.js +2 -2
- package/dist/components/data-table-column-settings.js.map +1 -1
- package/dist/components/data-table-head.js +2 -2
- package/dist/components/data-table-head.js.map +1 -1
- package/dist/components/data-table-row-actions.d.ts +35 -2
- package/dist/components/data-table-row-actions.js +98 -5
- package/dist/components/data-table-row-actions.js.map +1 -1
- package/dist/components/data-table-shared.js +9 -2
- package/dist/components/data-table-shared.js.map +1 -1
- package/dist/components/data-table.d.ts +58 -2
- package/dist/components/data-table.js +11 -4
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-format-setting.d.ts +29 -0
- package/dist/components/date-format-setting.js +36 -0
- package/dist/components/date-format-setting.js.map +1 -0
- package/dist/components/row-actions.d.ts +12 -5
- package/dist/components/row-actions.js +11 -4
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/swipeable-row.d.ts +8 -0
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/tabs.d.ts +3 -2
- package/dist/components/tabs.js +1 -1
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/translation-review-editor.js +1 -0
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review-labels.d.ts +5 -0
- package/dist/components/translation-review-labels.js +2 -0
- package/dist/components/translation-review-labels.js.map +1 -1
- package/dist/components/translation-review.d.ts +18 -8
- package/dist/components/translation-review.js +67 -8
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/write-lock-labels.js +2 -1
- package/dist/components/write-lock-labels.js.map +1 -1
- package/dist/components/write-lock.d.ts +33 -3
- package/dist/components/write-lock.js +9 -0
- package/dist/components/write-lock.js.map +1 -1
- package/dist/data-table.d.ts +2 -1
- package/dist/data-table.js +2 -0
- package/dist/data-table.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/feedback/feedback-row-detail.d.ts +4 -2
- package/dist/feedback/feedback-row-detail.js +17 -26
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback/feedback-swipe.d.ts +18 -0
- package/dist/feedback/feedback-swipe.js +10 -1
- package/dist/feedback/feedback-swipe.js.map +1 -1
- package/dist/i18n/german.js +41 -5
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +12 -1
- package/dist/i18n/kit-labels.js +8 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +40 -6
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/es.js +41 -5
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +41 -5
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +41 -5
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +41 -5
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +41 -5
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +3 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +17 -4
- package/dist/index.js +38 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/date-format.d.ts +81 -0
- package/dist/lib/date-format.js +56 -0
- package/dist/lib/date-format.js.map +1 -0
- package/dist/lib/format.d.ts +3 -0
- package/dist/lib/format.js +5 -4
- package/dist/lib/format.js.map +1 -1
- package/dist/notifications/device-zone-report.d.ts +34 -0
- package/dist/notifications/device-zone-report.js +31 -0
- package/dist/notifications/device-zone-report.js.map +1 -0
- package/dist/notifications/feedback-notification-types-labels.d.ts +7 -1
- package/dist/notifications/feedback-notification-types-labels.js +4 -1
- package/dist/notifications/feedback-notification-types-labels.js.map +1 -1
- package/dist/notifications/feedback-notification-types.d.ts +24 -5
- package/dist/notifications/feedback-notification-types.js +7 -2
- package/dist/notifications/feedback-notification-types.js.map +1 -1
- package/dist/notifications/notification-bell.d.ts +33 -0
- package/dist/notifications/notification-bell.js +74 -0
- package/dist/notifications/notification-bell.js.map +1 -0
- package/dist/notifications/notification-inbox.d.ts +86 -0
- package/dist/notifications/notification-inbox.js +206 -0
- package/dist/notifications/notification-inbox.js.map +1 -0
- package/dist/notifications/notification-types-setting.d.ts +6 -4
- package/dist/notifications/notification-types-setting.js +103 -45
- package/dist/notifications/notification-types-setting.js.map +1 -1
- package/dist/notifications/notification-types.d.ts +19 -7
- package/dist/notifications/notifications-labels.d.ts +44 -8
- package/dist/notifications/notifications-labels.js +18 -1
- package/dist/notifications/notifications-labels.js.map +1 -1
- package/dist/notifications/push-devices-setting.d.ts +20 -3
- package/dist/notifications/push-devices-setting.js +93 -33
- package/dist/notifications/push-devices-setting.js.map +1 -1
- package/dist/notifications/push-worker.d.ts +108 -0
- package/dist/notifications/push-worker.js +62 -0
- package/dist/notifications/push-worker.js.map +1 -0
- package/dist/notifications/quiet-hours-setting.d.ts +29 -17
- package/dist/notifications/quiet-hours-setting.js +9 -41
- package/dist/notifications/quiet-hours-setting.js.map +1 -1
- package/dist/notifications/time-zone-select.d.ts +8 -4
- package/dist/notifications/time-zone-select.js +2 -7
- package/dist/notifications/time-zone-select.js.map +1 -1
- package/dist/notifications/use-push-device.d.ts +4 -17
- package/dist/notifications/use-push-device.js +7 -15
- package/dist/notifications/use-push-device.js.map +1 -1
- package/dist/offline/local-first.d.ts +42 -0
- package/dist/offline/local-first.js +53 -0
- package/dist/offline/local-first.js.map +1 -0
- package/dist/offline/query-persistence.d.ts +26 -1
- package/dist/offline/query-persistence.js +27 -23
- package/dist/offline/query-persistence.js.map +1 -1
- package/dist/offline/service-worker-updates.d.ts +46 -0
- package/dist/offline/service-worker-updates.js +39 -0
- package/dist/offline/service-worker-updates.js.map +1 -0
- package/dist/offline.d.ts +5 -2
- package/dist/offline.js +12 -2
- package/dist/offline.js.map +1 -1
- package/dist/push-worker.d.ts +2 -0
- package/dist/push-worker.js +14 -0
- package/dist/push-worker.js.map +1 -0
- package/dist/shell/account-menu-text-size.d.ts +33 -0
- package/dist/shell/account-menu-text-size.js +51 -0
- package/dist/shell/account-menu-text-size.js.map +1 -0
- package/dist/shell/top-bar-actions.d.ts +6 -2
- package/dist/shell/top-bar-actions.js +1 -0
- package/dist/shell/top-bar-actions.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/sync/db.d.ts +93 -0
- package/dist/sync/db.js +33 -0
- package/dist/sync/db.js.map +1 -0
- package/dist/sync/engine.d.ts +126 -0
- package/dist/sync/engine.js +474 -0
- package/dist/sync/engine.js.map +1 -0
- package/dist/sync/hooks.d.ts +35 -0
- package/dist/sync/hooks.js +67 -0
- package/dist/sync/hooks.js.map +1 -0
- package/dist/sync/testing.d.ts +28 -0
- package/dist/sync/testing.js +41 -0
- package/dist/sync/testing.js.map +1 -0
- package/dist/sync/wire.d.ts +50 -0
- package/dist/sync/wire.js +2 -0
- package/dist/sync/wire.js.map +1 -0
- package/dist/sync.d.ts +7 -0
- package/dist/sync.js +25 -0
- package/dist/sync.js.map +1 -0
- package/dist/tour/tour-labels.js +1 -0
- package/dist/tour/tour-labels.js.map +1 -1
- package/dist/tour/tour.d.ts +30 -0
- package/dist/tour/tour.js +50 -6
- package/dist/tour/tour.js.map +1 -1
- package/package.json +19 -1
- package/src/admin/admin-area-labels.ts +13 -0
- package/src/admin/admin-overview.tsx +37 -2
- package/src/admin/mail-test-card.tsx +7 -3
- package/src/admin/user-roster.tsx +12 -3
- package/src/components/appearance-labels.ts +16 -0
- package/src/components/data-table-column-settings.tsx +2 -2
- package/src/components/data-table-head.tsx +2 -2
- package/src/components/data-table-row-actions.tsx +145 -5
- package/src/components/data-table-shared.tsx +21 -2
- package/src/components/data-table.tsx +68 -6
- package/src/components/date-format-setting.tsx +64 -0
- package/src/components/row-actions.tsx +33 -9
- package/src/components/swipeable-row.tsx +8 -0
- package/src/components/tabs.tsx +4 -3
- package/src/components/translation-review-editor.tsx +2 -0
- package/src/components/translation-review-labels.ts +7 -0
- package/src/components/translation-review.tsx +105 -15
- package/src/components/write-lock-labels.ts +1 -0
- package/src/components/write-lock.tsx +36 -3
- package/src/data-table.ts +4 -0
- package/src/feedback/feedback-row-detail.tsx +18 -23
- package/src/feedback/feedback-swipe.tsx +28 -1
- package/src/i18n/german.ts +44 -4
- package/src/i18n/kit-labels.tsx +18 -1
- package/src/i18n/locales/en.ts +1 -0
- package/src/i18n/locales/es.ts +44 -4
- package/src/i18n/locales/fr.ts +44 -4
- package/src/i18n/locales/hu.ts +44 -4
- package/src/i18n/locales/it.ts +44 -4
- package/src/i18n/locales/zh.ts +44 -2
- package/src/i18n/review.ts +3 -1
- package/src/index.ts +50 -1
- package/src/lib/date-format.ts +151 -0
- package/src/lib/format.ts +10 -4
- package/src/notifications/device-zone-report.ts +67 -0
- package/src/notifications/feedback-notification-types-labels.ts +10 -1
- package/src/notifications/feedback-notification-types.ts +30 -5
- package/src/notifications/notification-bell.tsx +116 -0
- package/src/notifications/notification-inbox.tsx +314 -0
- package/src/notifications/notification-types-setting.tsx +151 -55
- package/src/notifications/notification-types.ts +20 -8
- package/src/notifications/notifications-labels.ts +72 -12
- package/src/notifications/push-devices-setting.tsx +123 -34
- package/src/notifications/push-worker.ts +160 -0
- package/src/notifications/quiet-hours-setting.tsx +41 -58
- package/src/notifications/time-zone-select.tsx +9 -10
- package/src/notifications/use-push-device.ts +20 -33
- package/src/offline/local-first.ts +100 -0
- package/src/offline/query-persistence.ts +57 -24
- package/src/offline/service-worker-updates.ts +81 -0
- package/src/offline.ts +7 -1
- package/src/push-worker.ts +25 -0
- package/src/shell/account-menu-text-size.tsx +77 -0
- package/src/shell/top-bar-actions.tsx +6 -1
- package/src/shell.ts +3 -0
- package/src/sync/db.ts +125 -0
- package/src/sync/engine.ts +701 -0
- package/src/sync/hooks.ts +98 -0
- package/src/sync/testing.ts +73 -0
- package/src/sync/wire.ts +61 -0
- package/src/sync.ts +29 -0
- package/src/tour/tour-labels.ts +1 -0
- package/src/tour/tour.tsx +100 -7
package/README.md
CHANGED
|
@@ -67,6 +67,7 @@ when it chooses. To work on the design system and an app together, use
|
|
|
67
67
|
| `modern-screenshot` | peer, `^4.7.0`, **optional** | The feedback dialog's "Capture screenshot" (`captureAppScreenshot`), reached only through `await import()`. Without it the capture rejects and the dialog toasts `feedbackToast.captureFailed`. |
|
|
68
68
|
| `@tanstack/react-query`, `@tanstack/react-query-persist-client`, `@tanstack/query-async-storage-persister` | peer, `^5`, **optional** | Only `@eifi1/ui-kit/offline` imports them (0.45, the persisted query cache, `createQueryPersistence`); the main barrel does not. |
|
|
69
69
|
| `idb-keyval` | peer, `^6`, **optional** | The persisted cache's default IndexedDB store, in `@eifi1/ui-kit/offline` only. |
|
|
70
|
+
| `dexie` | peer, `^4`, **optional** | The sync engine's device copy, in `@eifi1/ui-kit/sync` only (0.46); `/offline` loads without it. |
|
|
70
71
|
| `clsx`, `tailwind-merge`, `zustand` | dependency | Small, imported directly, and the kit owns the version. Add `zustand` to Vite's `dedupe` (below) — the appearance stores are singletons. |
|
|
71
72
|
| `flag-icons` | **nothing — you supply the stylesheet** | See below. |
|
|
72
73
|
|
|
@@ -558,20 +559,20 @@ columns, strings and permissions (see the note at the top of `src/feedback/feedb
|
|
|
558
559
|
|
|
559
560
|
<!-- BEGIN GENERATED: exports — node scripts/gen-export-inventory.mjs -->
|
|
560
561
|
|
|
561
|
-
**
|
|
562
|
+
**2136 names from 350 modules** — 987 values and 1149 types. _Italic_ is a type-only export.
|
|
562
563
|
|
|
563
564
|
Generated from `dist/index.d.ts` by `node scripts/gen-export-inventory.mjs`; the count
|
|
564
565
|
is pinned by `src/__tests__/public-surface.test.ts`. Do not edit between the markers.
|
|
565
566
|
|
|
566
567
|
| Entry point | Names |
|
|
567
568
|
|---|---|
|
|
568
|
-
| `@eifi1/ui-kit` |
|
|
569
|
+
| `@eifi1/ui-kit` | 2136 |
|
|
569
570
|
| `@eifi1/ui-kit/chart` | 92 |
|
|
570
|
-
| `@eifi1/ui-kit/shell` |
|
|
571
|
-
| `@eifi1/ui-kit/data-table` |
|
|
571
|
+
| `@eifi1/ui-kit/shell` | 114 |
|
|
572
|
+
| `@eifi1/ui-kit/data-table` | 53 |
|
|
572
573
|
| `@eifi1/ui-kit/wizard` | 24 |
|
|
573
574
|
| `@eifi1/ui-kit/tour` | 37 |
|
|
574
|
-
| `@eifi1/ui-kit/feedback` |
|
|
575
|
+
| `@eifi1/ui-kit/feedback` | 206 |
|
|
575
576
|
| `@eifi1/ui-kit/search` | 23 |
|
|
576
577
|
| `@eifi1/ui-kit/dates` | 22 |
|
|
577
578
|
| `@eifi1/ui-kit/table-text` | 14 |
|
|
@@ -579,7 +580,10 @@ is pinned by `src/__tests__/public-surface.test.ts`. Do not edit between the mar
|
|
|
579
580
|
| `@eifi1/ui-kit/eslint` | 1 |
|
|
580
581
|
| `@eifi1/ui-kit/testing` | 11 |
|
|
581
582
|
| `@eifi1/ui-kit/rhf` | 48 |
|
|
582
|
-
| `@eifi1/ui-kit/offline` |
|
|
583
|
+
| `@eifi1/ui-kit/offline` | 33 |
|
|
584
|
+
| `@eifi1/ui-kit/sync` | 38 |
|
|
585
|
+
| `@eifi1/ui-kit/sync/testing` | 3 |
|
|
586
|
+
| `@eifi1/ui-kit/push-worker` | 10 |
|
|
583
587
|
|
|
584
588
|
Everything below is reachable from the main `@eifi1/ui-kit` barrel. The subpaths are a
|
|
585
589
|
re-slicing of it, never a second API.
|
|
@@ -605,6 +609,7 @@ re-slicing of it, never a second API.
|
|
|
605
609
|
| `lib/isin` | `formatIsin`, `isinCheckDigit`, `isValidIsin` |
|
|
606
610
|
| `lib/phone` | `formatNationalPhone`, `formatPhone`, `isE164`, `parsePhone`, `PHONE_COUNTRIES`, `PHONE_DIAL_CODES`, _`ParsedPhone`_, _`PhoneCountry`_, _`PhoneCountryCode`_ |
|
|
607
611
|
| `lib/column-mapping` | `assignColumnRole`, `guessMapping`, `missingRoles`, `readMappedTable`, `readTextFile`, `roleOfColumn`, _`ColumnMapperResult`_, _`ColumnMapping`_, _`ColumnRole`_ |
|
|
612
|
+
| `lib/date-format` | `DATE_FORMAT_PREFERENCES`, `dateFormatSample`, `dateFormatterFor`, `formatDateWithPreference`, `isDateFormatPreference`, `resolveDateFormat`, _`DateFormatOptions`_, _`DateFormatPreference`_, _`ResolvedDateFormat`_ |
|
|
608
613
|
| `lib/device-settings` | `deviceSettings`, `registerDeviceSetting`, `resetDeviceSettings`, _`DeviceSetting`_, _`DeviceSettingKind`_ |
|
|
609
614
|
| `lib/clipping` | `CLIPS_ATTRIBUTE` |
|
|
610
615
|
| `lib/chip-edge-snap` | `refreshChipEdges` |
|
|
@@ -652,7 +657,7 @@ re-slicing of it, never a second API.
|
|
|
652
657
|
|
|
653
658
|
| Module | Exports |
|
|
654
659
|
|---|---|
|
|
655
|
-
| `i18n/kit-labels` | `DEFAULT_APP_SHELL_LABELS`, `DEFAULT_CALCULATOR_LABELS`, `DEFAULT_COMBOBOX_LABELS`, `DEFAULT_COMMON_LABELS`, `DEFAULT_CURRENCY_LABELS`, `DEFAULT_DATE_PICKER_LABELS`, `DEFAULT_FILE_LABELS`, `DEFAULT_MULTI_SELECT_LABELS`, `DEFAULT_PICKER_SHEET_LABELS`, `DEFAULT_SWIPEABLE_ROW_LABELS`, `DEFAULT_TOP_BAR_LABELS`, `formatFileSize`, `missingKitLabels`, `UiKitProvider`, `useKitChartTooltipPlacement`, `useKitDateFormatter`, `useKitFileLabels`, `useKitLabelOverrides`, `useKitLabels`, `useKitLink`, `useKitLocale`, `useKitTableStorageKeyPrefix`, `useKitWeekStart`, _`AppShellLabels`_, _`CalculatorLabels`_, _`ChartTooltipPlacement`_, _`ComboboxLabels`_, _`CommonLabels`_, _`CurrencyLabels`_, _`DatePickerLabels`_, _`FileLabels`_, _`KitDateFormatContext`_, _`KitDateFormatter`_, _`KitLinkComponent`_, _`KitLinkProps`_, _`LabelOverride`_, _`MultiSelectLabels`_, _`PickerSheetLabels`_, _`SwipeableRowLabels`_, _`TopBarLabels`_, _`UiKitLabelOverrides`_, _`UiKitLabels`_, _`UiKitProviderProps`_ |
|
|
660
|
+
| `i18n/kit-labels` | `DEFAULT_APP_SHELL_LABELS`, `DEFAULT_CALCULATOR_LABELS`, `DEFAULT_COMBOBOX_LABELS`, `DEFAULT_COMMON_LABELS`, `DEFAULT_CURRENCY_LABELS`, `DEFAULT_DATE_PICKER_LABELS`, `DEFAULT_FILE_LABELS`, `DEFAULT_MULTI_SELECT_LABELS`, `DEFAULT_PICKER_SHEET_LABELS`, `DEFAULT_SWIPEABLE_ROW_LABELS`, `DEFAULT_TOP_BAR_LABELS`, `formatFileSize`, `missingKitLabels`, `UiKitProvider`, `useKitChartTooltipPlacement`, `useKitDateFormatter`, `useKitFileLabels`, `useKitLabelOverrides`, `useKitLabels`, `useKitLink`, `useKitLocale`, `useKitTableStorageKeyPrefix`, `useKitTimeZone`, `useKitWeekStart`, _`AppShellLabels`_, _`CalculatorLabels`_, _`ChartTooltipPlacement`_, _`ComboboxLabels`_, _`CommonLabels`_, _`CurrencyLabels`_, _`DatePickerLabels`_, _`FileLabels`_, _`KitDateFormatContext`_, _`KitDateFormatter`_, _`KitLinkComponent`_, _`KitLinkProps`_, _`LabelOverride`_, _`MultiSelectLabels`_, _`PickerSheetLabels`_, _`SwipeableRowLabels`_, _`TopBarLabels`_, _`UiKitLabelOverrides`_, _`UiKitLabels`_, _`UiKitProviderProps`_ |
|
|
656
661
|
| `i18n/defaults` | `DEFAULT_UI_KIT_LABELS` |
|
|
657
662
|
| `i18n/review` | `kitLabelStrings`, _`KitLabelStringsOptions`_ |
|
|
658
663
|
| `i18n/languages` | `formatLocaleOf`, `KIT_LANGUAGES`, `languageOptions`, `loadUiKitLabels`, `peekUiKitLabels`, `resolveLanguage`, `useUiKitLabels`, _`KitLanguage`_, _`KitLanguageCode`_ |
|
|
@@ -748,7 +753,7 @@ re-slicing of it, never a second API.
|
|
|
748
753
|
| `components/tree-view` | `TreeRow`, `TreeView`, _`TreeItemState`_, _`TreeNode`_, _`TreeRowProps`_, _`TreeViewProps`_ |
|
|
749
754
|
| `components/description-list` | `DEFAULT_DESCRIPTION_LIST_LABELS`, `DescriptionItem`, `DescriptionList`, _`DescriptionItemProps`_, _`DescriptionListColumns`_, _`DescriptionListDensity`_, _`DescriptionListLabels`_, _`DescriptionListLayout`_, _`DescriptionListProps`_ |
|
|
750
755
|
| `components/form-actions` | `DEFAULT_FORM_ACTIONS_LABELS`, `FormActions`, _`FormActionsAlign`_, _`FormActionsDestructive`_, _`FormActionsLabels`_, _`FormActionsPlacement`_, _`FormActionsProps`_, _`FormActionsStack`_, _`FormActionsStickyWithin`_, _`FormActionsSubmitProps`_, _`FormActionsSubmitShortcut`_, _`ResponsiveFormActionsPlacement`_ |
|
|
751
|
-
| `components/write-lock` | `COMMIT_EXCEPT_BILLING`, `DEFAULT_WRITE_LOCK_LABELS`, `useWriteLock`, `writeLockFor`, `WriteLockProvider`, _`CommitScope`_, _`WriteLock`_, _`WriteLockHold`_, _`WriteLockKind`_, _`WriteLockLabels`_, _`WriteLockProviderProps`_ |
|
|
756
|
+
| `components/write-lock` | `COMMIT_EXCEPT_BILLING`, `COMMIT_EXCEPT_OFFLINE`, `DEFAULT_WRITE_LOCK_LABELS`, `OFFLINE_LOCK`, `useOfflineWriteLock`, `useWriteLock`, `writeLockFor`, `WriteLockProvider`, _`CommitScope`_, _`WriteLock`_, _`WriteLockHold`_, _`WriteLockKind`_, _`WriteLockLabels`_, _`WriteLockProviderProps`_ |
|
|
752
757
|
| `components/account-chips` | `ACCOUNT_STATE_TONES`, `AccountRoleChip`, `AccountStateChip`, `dateColumn`, `DateMark`, `DEFAULT_ACCOUNT_STATE_LABELS`, `RoleChip`, _`AccountRoleChipProps`_, _`AccountState`_, _`AccountStateChipProps`_, _`AccountStateLabels`_, _`DateColumnOptions`_, _`DateMarkProps`_, _`RoleChipProps`_, _`RoleDefinition`_, _`RoleVocabulary`_ |
|
|
753
758
|
| `components/server-wake` | `DEFAULT_SERVER_WAKE_LABELS`, `ServerWakeNotice`, `useServerWakeStage`, _`ServerWakeLabels`_, _`ServerWakeNoticeProps`_ |
|
|
754
759
|
| `components/share-card` | `DEFAULT_SHARE_CARD_LABELS`, `ShareCard`, `ShareDialog`, `SharePanel`, _`ShareAddRequest`_, _`ShareCandidate`_, _`ShareCardLabels`_, _`ShareCardProps`_, _`ShareDialogProps`_, _`ShareGrantee`_, _`SharePanelProps`_, _`SharePendingGrant`_, _`ShareRole`_ |
|
|
@@ -778,6 +783,7 @@ re-slicing of it, never a second API.
|
|
|
778
783
|
| `components/legal` | `DEFAULT_LEGAL_LABELS`, `LEGAL_HREFS`, `LegalFooter`, `LegalLayout`, `LegalLinks`, `legalOperatorText`, `LegalSection`, `useLegalLabels`, _`LegalDisclaimerSectionLabels`_, _`LegalFooterProps`_, _`LegalFramedSectionLabels`_, _`LegalLabels`_, _`LegalLayoutProps`_, _`LegalLink`_, _`LegalLinksProps`_, _`LegalOperator`_, _`LegalOperatorSectionLabels`_, _`LegalOperatorText`_, _`LegalPageKey`_, _`LegalSectionProps`_, _`LegalTextSectionLabels`_ |
|
|
779
784
|
| `components/legal-page` | `LEGAL_SKELETON`, `LegalAcceptCheckbox`, `LegalKitSection`, `LegalPage`, _`LegalAcceptCheckboxProps`_, _`LegalDisclaimerVariant`_, _`LegalKitSectionKey`_, _`LegalKitSectionProps`_, _`LegalPageProps`_, _`LegalSectionOwner`_, _`LegalSkeletonEntry`_, _`LegalSkeletonPage`_ |
|
|
780
785
|
| `components/appearance-labels` | `DEFAULT_APPEARANCE_LABELS`, `useAppearanceLabels`, _`AppearanceLabels`_ |
|
|
786
|
+
| `components/date-format-setting` | `DateFormatSetting`, _`DateFormatSettingProps`_ |
|
|
781
787
|
| `components/row-actions` | `DEFAULT_ROW_ACTIONS_LABELS`, `RowActions`, `rowActionsColumn`, _`RowAction`_, _`RowActionList`_, _`RowActionsCollapse`_, _`RowActionsColumnOptions`_, _`RowActionsLabels`_, _`RowActionsProps`_, _`RowActionsSize`_, _`RowActionTone`_ |
|
|
782
788
|
| `components/checkbox-group` | `CheckboxGroup`, _`CheckboxGroupLegendVisibility`_, _`CheckboxGroupOption`_, _`CheckboxGroupProps`_ |
|
|
783
789
|
| `components/one-time-code-input` | `OneTimeCodeInput`, _`OneTimeCodeInputProps`_ |
|
|
@@ -795,7 +801,8 @@ re-slicing of it, never a second API.
|
|
|
795
801
|
| `components/data-table-filters` | `dateFilter`, `decodeFilterValue`, `decodeFilterValueOfType`, `defaultFilterState`, `encodeFilterValue`, `filterHref`, `isFilterActive`, `numberFilter`, `resolveFilter`, `rowMatches`, `selectFilter`, `textFilter`, _`ColumnFilter`_, _`FilterValue`_ |
|
|
796
802
|
| `components/data-table-pagination` | `PAGE_SIZE_OPTIONS`, `Pagination` |
|
|
797
803
|
| `components/data-table-filter-popover` | `FilterPopover` |
|
|
798
|
-
| `components/data-table` | `DataTable`, `DEFAULT_DATA_TABLE_SORT_LABELS`, _`DataTableCellProps`_, _`DataTableChrome`_, _`DataTableColumn`_, _`DataTableDensity`_, _`DataTableHeadProps`_, _`DataTableProps`_, _`DataTableRowAction`_, _`DataTableSortLabels`_, _`FilterState`_, _`MobileSwipeActions`_, _`ServerPagination`_ |
|
|
804
|
+
| `components/data-table` | `DataTable`, `DEFAULT_DATA_TABLE_SORT_LABELS`, _`DataTableCellProps`_, _`DataTableChrome`_, _`DataTableColumn`_, _`DataTableDensity`_, _`DataTableDesktopSwipeActions`_, _`DataTableHeadProps`_, _`DataTableProps`_, _`DataTableRowAction`_, _`DataTableSortLabels`_, _`FilterState`_, _`MobileSwipeActions`_, _`ServerPagination`_ |
|
|
805
|
+
| `components/data-table-row-actions` | `swipePlanRowActions` |
|
|
799
806
|
| `components/data-table-cells` | `booleanColumn`, `BooleanMark`, _`BooleanColumnOptions`_, _`BooleanMarkProps`_ |
|
|
800
807
|
| `components/use-table-state` | `readTableUrlState`, `useTableUrlState`, _`TableUrlState`_, _`TableUrlStateProps`_, _`TableUrlSync`_, _`UseTableUrlStateOptions`_, _`UseTableUrlStateReturn`_ |
|
|
801
808
|
| `components/translation-review-editor` | `TranslationReviewEditor`, _`TranslationReviewEditorProps`_ |
|
|
@@ -825,6 +832,7 @@ re-slicing of it, never a second API.
|
|
|
825
832
|
| `shell/admin-menu` | `adminMenuEntry`, `AdminMenuRow`, `AdminWaitingBadge`, `useAdminMenuEntry`, _`AdminMenuEntryOptions`_, _`AdminMenuRowProps`_, _`AdminWaitingBadgeProps`_, _`UseAdminMenuEntryOptions`_ |
|
|
826
833
|
| `shell/top-bar-actions` | `TOP_BAR_ACTION_ORDER`, `TopBarActions`, _`TopBarActionsProps`_ |
|
|
827
834
|
| `shell/account-menu-language` | `AccountMenuLanguage`, _`AccountMenuLanguageOption`_, _`AccountMenuLanguageProps`_ |
|
|
835
|
+
| `shell/account-menu-text-size` | `AccountMenuTextSize`, _`AccountMenuTextSizeProps`_ |
|
|
828
836
|
| `shell/tour-menu` | `TourMenu`, _`TourMenuProps`_, _`TourMenuTour`_ |
|
|
829
837
|
| `shell/sync-status-indicator` | `syncState`, `SyncStatusIndicator`, _`SyncState`_, _`SyncStatus`_, _`SyncStatusIndicatorProps`_, _`SyncStatusScope`_ |
|
|
830
838
|
| `shell/dev-switchers` | `DEV_ORIGINAL_ROLE_KEY`, `DevSwitchers`, _`DevSwitchersProps`_ |
|
|
@@ -870,7 +878,7 @@ re-slicing of it, never a second API.
|
|
|
870
878
|
| `feedback/feedback-record` | `appendRework`, `attachmentName`, `FEEDBACK_ATTACHMENT_ACCEPT`, `FEEDBACK_AUTHOR_EDITABLE_STATUSES`, `FEEDBACK_AWAITING_STATUSES`, `FEEDBACK_COMMENT_MAX`, `FEEDBACK_DECIDED_STATUSES`, `FEEDBACK_PICKABLE_CATEGORIES`, `FEEDBACK_QUEUE_STATUSES`, `FEEDBACK_REPLY_MAX`, `FEEDBACK_REWORKABLE_STATUSES`, `feedbackAttachmentLine`, `feedbackReworkCount`, `isImageAttachment`, `replaceDescription`, `reworkCount`, `splitBodyAttachments`, `splitDescription`, _`FeedbackContext`_, _`FeedbackEnvironment`_, _`FeedbackRecord`_ |
|
|
871
879
|
| `feedback/feedback-labels` | `DEFAULT_FEEDBACK_CATEGORY_LABELS`, `DEFAULT_FEEDBACK_STATUS_LABELS`, `DEFAULT_FEEDBACK_TOAST_LABELS`, `useFeedbackCategoryLabels`, `useFeedbackStatusLabels`, `useFeedbackToastLabels`, _`FeedbackCategoryLabels`_, _`FeedbackStatusLabels`_, _`FeedbackToastLabels`_ |
|
|
872
880
|
| `feedback/feedback-status-undo` | `FEEDBACK_UNDO_DURATION`, `useFeedbackStatusUndo`, _`FeedbackStatusChange`_, _`FeedbackStatusMutate`_, _`FeedbackStatusPatch`_, _`FeedbackStatusRow`_, _`FeedbackStatusUndoOptions`_ |
|
|
873
|
-
| `feedback/feedback-swipe` | `DEFAULT_FEEDBACK_SWIPE`, `FEEDBACK_SWIPE_ACTIONS`, `feedbackSwipePlan`, _`FeedbackSwipeAction`_, _`FeedbackSwipeBinding`_, _`FeedbackSwipeOptions`_ |
|
|
881
|
+
| `feedback/feedback-swipe` | `DEFAULT_FEEDBACK_SWIPE`, `FEEDBACK_SWIPE_ACTIONS`, `feedbackSwipeOmitShown`, `feedbackSwipePlan`, _`FeedbackSwipeAction`_, _`FeedbackSwipeBinding`_, _`FeedbackSwipeOptions`_ |
|
|
874
882
|
| `feedback/feedback-menu` | `DEFAULT_FEEDBACK_MENU_LABELS`, `FeedbackMenu`, _`FeedbackMenuLabels`_, _`FeedbackMenuProps`_ |
|
|
875
883
|
| `feedback/feedback-context` | `DEFAULT_FEEDBACK_CONTEXT_LABELS`, `feedbackContext`, `FeedbackContextBox`, _`FeedbackContextBoxProps`_, _`FeedbackContextInput`_, _`FeedbackContextLabels`_, _`FeedbackSubmitter`_ |
|
|
876
884
|
| `feedback/feedback-capture` | `captureAppScreenshot`, _`CaptureAppScreenshotOptions`_ |
|
|
@@ -960,7 +968,7 @@ re-slicing of it, never a second API.
|
|
|
960
968
|
| `admin/admin-area-labels` | `DEFAULT_ADMIN_AREA_LABELS`, `useAdminAreaLabels`, _`AdminAreaLabels`_, _`AdminSharedSection`_, _`AdminTileLabels`_ |
|
|
961
969
|
| `admin/admin-sections` | `ADMIN_SECTIONS`, `adminSectionOrder`, `adminSections`, _`AdminKeptSection`_, _`AdminOwnSection`_, _`AdminSectionDefinition`_, _`AdminSectionOrderOptions`_, _`AdminSectionPlace`_, _`AdminSectionsOptions`_ |
|
|
962
970
|
| `admin/admin-forbidden` | `AdminForbidden`, _`AdminForbiddenProps`_ |
|
|
963
|
-
| `admin/admin-overview` | `AdminOverview`, `adminWaitingCount`, `AttentionTile`, `DEFAULT_ADMIN_ATTENTION_HREFS`, _`AdminAttention`_, _`AdminAttentionTile`_, _`AdminExtraTile`_, _`AdminOverviewProps`_, _`AttentionTileProps`_ |
|
|
971
|
+
| `admin/admin-overview` | `AdminOverview`, `adminWaitingCount`, `AttentionTile`, `DEFAULT_ADMIN_ATTENTION_HREFS`, _`AdminAttention`_, _`AdminAttentionTile`_, _`AdminExtraTile`_, _`AdminFeedbackTile`_, _`AdminFeedbackTileKey`_, _`AdminOverviewProps`_, _`AdminOwnTile`_, _`AttentionTileProps`_ |
|
|
964
972
|
| `admin/admin-tool-links` | `AdminToolLinks`, _`AdminToolLink`_, _`AdminToolLinksProps`_ |
|
|
965
973
|
| `admin/mail-test-card` | `DEFAULT_MAIL_TEST_CARD_LABELS`, `MAIL_TEST_ANCHOR`, `MailTestCard`, `maskMailAddress`, _`MailTestAnswer`_, _`MailTestCardLabels`_, _`MailTestCardProps`_ |
|
|
966
974
|
| `admin/plan-change` | `DEFAULT_PLAN_CHANGE_LABELS`, `PlanChangeConfirm`, `planColumn`, `usePlanChangeResult`, `usePlanColumn`, _`PlanChange`_, _`PlanChangeConfirmProps`_, _`PlanChangeLabels`_, _`PlanChangeLine`_, _`PlanChangeOutcome`_, _`PlanChangeResultOptions`_, _`PlanChangeTone`_, _`PlanColumnOptions`_, _`PlanColumnUsage`_ |
|
|
@@ -994,15 +1002,19 @@ re-slicing of it, never a second API.
|
|
|
994
1002
|
| Module | Exports |
|
|
995
1003
|
|---|---|
|
|
996
1004
|
| `notifications/notifications-labels` | `DEFAULT_NOTIFICATIONS_LABELS`, _`NotificationRegionLabels`_, _`NotificationsLabels`_ |
|
|
997
|
-
| `notifications/notification-types` | _`DigestCadence`_, _`NotificationSettingsPatch`_, _`NotificationSwitches`_, _`NotificationTypeEntry`_, _`NotificationTypeGroup`_, _`NotificationTypesValue`_, _`PushDeviceItem`_ |
|
|
1005
|
+
| `notifications/notification-types` | _`DigestCadence`_, _`NotificationChannel`_, _`NotificationSettingsPatch`_, _`NotificationSwitches`_, _`NotificationTypeEntry`_, _`NotificationTypeGroup`_, _`NotificationTypesValue`_, _`PushDeviceItem`_ |
|
|
998
1006
|
| `notifications/notification-types-setting` | `NotificationTypesSetting`, _`NotificationTypesSettingProps`_ |
|
|
999
1007
|
| `notifications/quiet-hours-setting` | `QuietHoursSetting`, _`QuietHoursSettingProps`_ |
|
|
1000
|
-
| `notifications/time-zone-select` | `
|
|
1008
|
+
| `notifications/time-zone-select` | `timeZoneOptions`, `TimeZoneSelect`, _`TimeZoneSelectProps`_ |
|
|
1009
|
+
| `notifications/notification-inbox` | `INBOX_BADGE_INTERVAL`, `NotificationInbox`, _`NotificationInboxAction`_, _`NotificationInboxItem`_, _`NotificationInboxProps`_ |
|
|
1010
|
+
| `notifications/notification-bell` | `NotificationBell`, _`NotificationBellProps`_ |
|
|
1011
|
+
| `notifications/device-zone-report` | `deviceTimeZone`, `useDeviceZoneReport`, _`DeviceZoneReportOptions`_ |
|
|
1001
1012
|
| `notifications/use-push-device` | `notifyPushSubscriptionChanged`, `PUSH_SUBSCRIPTION_CHANGED`, `PushDeviceError`, `pushSupported`, `usePushDevice`, _`PushDevice`_, _`PushDeviceErrorCode`_, _`PushPermission`_, _`PushSubscriptionInput`_, _`UsePushDeviceOptions`_ |
|
|
1013
|
+
| `notifications/push-worker` | `handlePushSubscriptionChange`, `pushSubscriptionInput`, `resubscribeAfterChange`, _`PushReplaceInput`_, _`PushResubscribeOutcome`_, _`PushSubscriptionChangeEventLike`_, _`PushSubscriptionChangeOptions`_, _`PushWorkerScope`_ |
|
|
1002
1014
|
| `notifications/push-device-setting` | `PushDeviceSetting`, _`PushDeviceAction`_, _`PushDeviceSettingProps`_ |
|
|
1003
1015
|
| `notifications/push-devices-setting` | `describeUserAgent`, `PushDevicesSetting`, _`PushDevicesSettingProps`_ |
|
|
1004
1016
|
| `notifications/unsubscribe-panel` | `UnsubscribePanel`, `unsubscribeTokenType`, _`UnsubscribePanelProps`_ |
|
|
1005
|
-
| `notifications/feedback-notification-types` | `DEFAULT_FEEDBACK_NOTIFICATION_TYPES_LABELS`, `useFeedbackNotificationTypes`, _`FeedbackNotificationTypeId`_, _`FeedbackNotificationTypesLabels`_ |
|
|
1017
|
+
| `notifications/feedback-notification-types` | `DEFAULT_FEEDBACK_NOTIFICATION_TYPES_LABELS`, `useFeedbackNotificationTypes`, `useFeedbackNotificationTypesLabels`, _`FeedbackNotificationTypeId`_, _`FeedbackNotificationTypesLabels`_ |
|
|
1006
1018
|
|
|
1007
1019
|
### demo
|
|
1008
1020
|
|
|
@@ -21,6 +21,15 @@ export interface AdminTileLabels {
|
|
|
21
21
|
users: string;
|
|
22
22
|
/** `users_new_7d`, the line under the users tile. */
|
|
23
23
|
usersNew: (count: number) => string;
|
|
24
|
+
/** 0.46 (Kurvenschmiede 1, admin §9.5): server-kit's `awaiting_verdict` as an `extra`
|
|
25
|
+
* tile — answered reports that wait on a verdict (IN_EVALUATION, NEEDS_LIVE_TEST). */
|
|
26
|
+
awaitingVerdict: string;
|
|
27
|
+
/** Its line under the count. */
|
|
28
|
+
awaitingVerdictHint: string;
|
|
29
|
+
/** 0.46: server-kit's `released_for_work` — the reports released for the work queue. */
|
|
30
|
+
releasedForWork: string;
|
|
31
|
+
/** Its line under the count. */
|
|
32
|
+
releasedForWorkHint: string;
|
|
24
33
|
}
|
|
25
34
|
export interface AdminAreaLabels {
|
|
26
35
|
/** "Administration": the account menu's row, and the admin page's heading. */
|
|
@@ -20,7 +20,11 @@ const DEFAULT_ADMIN_AREA_LABELS = {
|
|
|
20
20
|
deletionRequests: "Deletion requests",
|
|
21
21
|
invitationsPending: "Pending invitations",
|
|
22
22
|
users: "Users",
|
|
23
|
-
usersNew: (count) => `${count} new in 7 days
|
|
23
|
+
usersNew: (count) => `${count} new in 7 days`,
|
|
24
|
+
awaitingVerdict: "Awaiting a verdict",
|
|
25
|
+
awaitingVerdictHint: "Answered, and waiting for you or the reporter to confirm the fix",
|
|
26
|
+
releasedForWork: "Released for work",
|
|
27
|
+
releasedForWorkHint: "Ready, rework requested or in progress"
|
|
24
28
|
},
|
|
25
29
|
overviewError: "The overview couldn\u2019t be loaded.",
|
|
26
30
|
retry: "Try again",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/admin-area-labels.ts"],"sourcesContent":["import { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport type { SettingsGroupLabels } from \"../settings/settings-labels\";\n\n/**\n * The `adminArea` namespace (0.42, docs/admin-harmonization.md §3.1): the kit's words of\n * the admin area every app shares — the account menu's \"Administration\" row, the 403\n * page, the shared sections' names and help lines, the overview's tiles and the Tools\n * list. A module of words with no component code, like every `*-labels.ts` (0.34, KS U8).\n * An app's own sections and tiles bring their own words.\n */\n\n/** The sections every app's admin area shares, in the template's order (§2.3). */\nexport type AdminSharedSection = \"overview\" | \"users\" | \"invitations\" | \"billing\" | \"activity\";\n\n/** The overview's shared tiles (§2.4, §6.1). */\nexport interface AdminTileLabels {\n /** `feedback_waiting`: OPEN reports, crash reports included (§6.1). */\n feedbackWaiting: string;\n /** `deletion_requests`. */\n deletionRequests: string;\n /** `invitations_pending`. */\n invitationsPending: string;\n /** `users_total`. */\n users: string;\n /** `users_new_7d`, the line under the users tile. */\n usersNew: (count: number) => string;\n}\n\nexport interface AdminAreaLabels {\n /** \"Administration\": the account menu's row, and the admin page's heading. */\n title: string;\n /** The row's waiting count, for a reader (\"Administration, 3 waiting\"); the eye sees\n * the number in the badge. */\n waiting: (count: number) => string;\n /**\n * 0.45 (admin §9.3.4): a button's whole name with its count — \"Platform, 2 waiting\" —\n * for an `IconButton` whose `badge` is an `AdminWaitingBadge`: the badge is decorative\n * there, so the name carries the count. `label` is the button's own name.\n */\n waitingLabel: (label: string, count: number) => string;\n /** The 403 page's heading (§2.2 decision 8). */\n forbiddenTitle: string;\n /**\n * Under it. Says only that THIS area is closed to the account — never that the person\n * administers nothing anywhere (§6.7: a kastlan superuser who isn't ADMIN in the acting\n * company gets this page).\n */\n forbiddenBody: (appName: string) => string;\n /** The button back to the app's start, unless the app names its own (`homeLabel`). */\n forbiddenHome: string;\n /** A reviewer's second way out, to the translations review. */\n forbiddenReviewer: string;\n /** The shared sections' names and help lines (§2.3 decision 11). */\n sections: Record<AdminSharedSection, SettingsGroupLabels>;\n /** The overview's tiles. */\n tiles: AdminTileLabels;\n /** 0.43: `AdminOverview`'s `error` — the counts could not be loaded. */\n overviewError: string;\n /** 0.43: its button, which calls `onRetry`. */\n retry: string;\n /** The Tools list's heading and name (§2.5 decision 16). */\n tools: string;\n}\n\nexport const DEFAULT_ADMIN_AREA_LABELS: AdminAreaLabels = {\n title: \"Administration\",\n waiting: (count) => `${count} waiting`,\n waitingLabel: (label, count) => `${label}, ${count} waiting`,\n forbiddenTitle: \"Only for administrators\",\n forbiddenBody: (appName) => `This is where administrators run ${appName}. Your account cannot open this page.`,\n forbiddenHome: \"Go to the start page\",\n forbiddenReviewer: \"Review translations\",\n sections: {\n overview: { title: \"Overview\", help: \"What is waiting, at a glance.\" },\n users: { title: \"Users\", help: \"Who has an account, and what each may do.\" },\n invitations: { title: \"Invitations\", help: \"Invite people by e-mail in their language; resend or revoke what’s still open.\" },\n billing: { title: \"Billing\", help: \"The plan, the payment and the invoices.\" },\n activity: { title: \"Activity\", help: \"What the admins did, newest first.\" },\n },\n tiles: {\n feedbackWaiting: \"To triage\",\n deletionRequests: \"Deletion requests\",\n invitationsPending: \"Pending invitations\",\n users: \"Users\",\n usersNew: (count) => `${count} new in 7 days`,\n },\n overviewError: \"The overview couldn’t be loaded.\",\n retry: \"Try again\",\n tools: \"Tools\",\n};\n\n/**\n * The `adminArea` namespace, resolved — English, then the provider, then `labels`. What\n * an app reads to hand {@link adminSections} its words from inside a component.\n */\nexport function useAdminAreaLabels(labels?: LabelOverride<AdminAreaLabels>): AdminAreaLabels {\n return useKitLabels(\"adminArea\", DEFAULT_ADMIN_AREA_LABELS, labels);\n}\n"],"mappings":";AAAA,SAAS,oBAAwC;
|
|
1
|
+
{"version":3,"sources":["../../src/admin/admin-area-labels.ts"],"sourcesContent":["import { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport type { SettingsGroupLabels } from \"../settings/settings-labels\";\n\n/**\n * The `adminArea` namespace (0.42, docs/admin-harmonization.md §3.1): the kit's words of\n * the admin area every app shares — the account menu's \"Administration\" row, the 403\n * page, the shared sections' names and help lines, the overview's tiles and the Tools\n * list. A module of words with no component code, like every `*-labels.ts` (0.34, KS U8).\n * An app's own sections and tiles bring their own words.\n */\n\n/** The sections every app's admin area shares, in the template's order (§2.3). */\nexport type AdminSharedSection = \"overview\" | \"users\" | \"invitations\" | \"billing\" | \"activity\";\n\n/** The overview's shared tiles (§2.4, §6.1). */\nexport interface AdminTileLabels {\n /** `feedback_waiting`: OPEN reports, crash reports included (§6.1). */\n feedbackWaiting: string;\n /** `deletion_requests`. */\n deletionRequests: string;\n /** `invitations_pending`. */\n invitationsPending: string;\n /** `users_total`. */\n users: string;\n /** `users_new_7d`, the line under the users tile. */\n usersNew: (count: number) => string;\n /** 0.46 (Kurvenschmiede 1, admin §9.5): server-kit's `awaiting_verdict` as an `extra`\n * tile — answered reports that wait on a verdict (IN_EVALUATION, NEEDS_LIVE_TEST). */\n awaitingVerdict: string;\n /** Its line under the count. */\n awaitingVerdictHint: string;\n /** 0.46: server-kit's `released_for_work` — the reports released for the work queue. */\n releasedForWork: string;\n /** Its line under the count. */\n releasedForWorkHint: string;\n}\n\nexport interface AdminAreaLabels {\n /** \"Administration\": the account menu's row, and the admin page's heading. */\n title: string;\n /** The row's waiting count, for a reader (\"Administration, 3 waiting\"); the eye sees\n * the number in the badge. */\n waiting: (count: number) => string;\n /**\n * 0.45 (admin §9.3.4): a button's whole name with its count — \"Platform, 2 waiting\" —\n * for an `IconButton` whose `badge` is an `AdminWaitingBadge`: the badge is decorative\n * there, so the name carries the count. `label` is the button's own name.\n */\n waitingLabel: (label: string, count: number) => string;\n /** The 403 page's heading (§2.2 decision 8). */\n forbiddenTitle: string;\n /**\n * Under it. Says only that THIS area is closed to the account — never that the person\n * administers nothing anywhere (§6.7: a kastlan superuser who isn't ADMIN in the acting\n * company gets this page).\n */\n forbiddenBody: (appName: string) => string;\n /** The button back to the app's start, unless the app names its own (`homeLabel`). */\n forbiddenHome: string;\n /** A reviewer's second way out, to the translations review. */\n forbiddenReviewer: string;\n /** The shared sections' names and help lines (§2.3 decision 11). */\n sections: Record<AdminSharedSection, SettingsGroupLabels>;\n /** The overview's tiles. */\n tiles: AdminTileLabels;\n /** 0.43: `AdminOverview`'s `error` — the counts could not be loaded. */\n overviewError: string;\n /** 0.43: its button, which calls `onRetry`. */\n retry: string;\n /** The Tools list's heading and name (§2.5 decision 16). */\n tools: string;\n}\n\nexport const DEFAULT_ADMIN_AREA_LABELS: AdminAreaLabels = {\n title: \"Administration\",\n waiting: (count) => `${count} waiting`,\n waitingLabel: (label, count) => `${label}, ${count} waiting`,\n forbiddenTitle: \"Only for administrators\",\n forbiddenBody: (appName) => `This is where administrators run ${appName}. Your account cannot open this page.`,\n forbiddenHome: \"Go to the start page\",\n forbiddenReviewer: \"Review translations\",\n sections: {\n overview: { title: \"Overview\", help: \"What is waiting, at a glance.\" },\n users: { title: \"Users\", help: \"Who has an account, and what each may do.\" },\n invitations: { title: \"Invitations\", help: \"Invite people by e-mail in their language; resend or revoke what’s still open.\" },\n billing: { title: \"Billing\", help: \"The plan, the payment and the invoices.\" },\n activity: { title: \"Activity\", help: \"What the admins did, newest first.\" },\n },\n tiles: {\n feedbackWaiting: \"To triage\",\n deletionRequests: \"Deletion requests\",\n invitationsPending: \"Pending invitations\",\n users: \"Users\",\n usersNew: (count) => `${count} new in 7 days`,\n awaitingVerdict: \"Awaiting a verdict\",\n awaitingVerdictHint: \"Answered, and waiting for you or the reporter to confirm the fix\",\n releasedForWork: \"Released for work\",\n releasedForWorkHint: \"Ready, rework requested or in progress\",\n },\n overviewError: \"The overview couldn’t be loaded.\",\n retry: \"Try again\",\n tools: \"Tools\",\n};\n\n/**\n * The `adminArea` namespace, resolved — English, then the provider, then `labels`. What\n * an app reads to hand {@link adminSections} its words from inside a component.\n */\nexport function useAdminAreaLabels(labels?: LabelOverride<AdminAreaLabels>): AdminAreaLabels {\n return useKitLabels(\"adminArea\", DEFAULT_ADMIN_AREA_LABELS, labels);\n}\n"],"mappings":";AAAA,SAAS,oBAAwC;AAyE1C,MAAM,4BAA6C;AAAA,EACxD,OAAO;AAAA,EACP,SAAS,CAAC,UAAU,GAAG,KAAK;AAAA,EAC5B,cAAc,CAAC,OAAO,UAAU,GAAG,KAAK,KAAK,KAAK;AAAA,EAClD,gBAAgB;AAAA,EAChB,eAAe,CAAC,YAAY,oCAAoC,OAAO;AAAA,EACvE,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,UAAU;AAAA,IACR,UAAU,EAAE,OAAO,YAAY,MAAM,gCAAgC;AAAA,IACrE,OAAO,EAAE,OAAO,SAAS,MAAM,4CAA4C;AAAA,IAC3E,aAAa,EAAE,OAAO,eAAe,MAAM,sFAAiF;AAAA,IAC5H,SAAS,EAAE,OAAO,WAAW,MAAM,0CAA0C;AAAA,IAC7E,UAAU,EAAE,OAAO,YAAY,MAAM,qCAAqC;AAAA,EAC5E;AAAA,EACA,OAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,OAAO;AAAA,IACP,UAAU,CAAC,UAAU,GAAG,KAAK;AAAA,IAC7B,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,EACvB;AAAA,EACA,eAAe;AAAA,EACf,OAAO;AAAA,EACP,OAAO;AACT;AAMO,SAAS,mBAAmB,QAA0D;AAC3F,SAAO,aAAa,aAAa,2BAA2B,MAAM;AACpE;","names":[]}
|
|
@@ -54,8 +54,16 @@ export interface AttentionTileProps extends Omit<StatTileProps, "href" | "onClic
|
|
|
54
54
|
* is a quiet count, no link.
|
|
55
55
|
*/
|
|
56
56
|
export declare function AttentionTile({ count, href, ...tile }: AttentionTileProps): import("react").JSX.Element;
|
|
57
|
+
/**
|
|
58
|
+
* 0.46 (Kurvenschmiede 1, admin §9.5): the `extra` keys server-kit's
|
|
59
|
+
* `feedback_attention` fills, whose tiles have the kit's words
|
|
60
|
+
* (`adminArea.tiles.awaitingVerdict`, `releasedForWork`, with a line each).
|
|
61
|
+
*/
|
|
62
|
+
export type AdminFeedbackTileKey = "awaiting_verdict" | "released_for_work";
|
|
57
63
|
/** One of the app's own tiles (§2.4 decision 13: keksdose's "Support waiting"). */
|
|
58
|
-
export
|
|
64
|
+
export type AdminExtraTile = AdminOwnTile | AdminFeedbackTile;
|
|
65
|
+
/** A tile of the app's own: it brings its words. */
|
|
66
|
+
export interface AdminOwnTile {
|
|
59
67
|
/** Unique among the tiles; usually the `extra` key it shows. */
|
|
60
68
|
key: string;
|
|
61
69
|
label: ReactNode;
|
|
@@ -67,6 +75,13 @@ export interface AdminExtraTile {
|
|
|
67
75
|
/** A line under the count. */
|
|
68
76
|
description?: ReactNode;
|
|
69
77
|
}
|
|
78
|
+
/** 0.46: a tile of server-kit's feedback counts — `label` and `description` default to
|
|
79
|
+
* the kit's words; an app's own still win. The kit's `description` fills in only when
|
|
80
|
+
* `label` is left out too: an app that names the tile gets no hint line it did not ask for. */
|
|
81
|
+
export interface AdminFeedbackTile extends Omit<AdminOwnTile, "key" | "label"> {
|
|
82
|
+
key: AdminFeedbackTileKey;
|
|
83
|
+
label?: ReactNode;
|
|
84
|
+
}
|
|
70
85
|
export interface AdminOverviewProps {
|
|
71
86
|
/** The server's answer; `undefined` while it loads (the grid shows its skeletons). */
|
|
72
87
|
attention: AdminAttention | null | undefined;
|
|
@@ -60,9 +60,20 @@ function AdminOverview({
|
|
|
60
60
|
description: attention && typeof attention.users_new_7d === "number" ? labels.tiles.usersNew(attention.users_new_7d) : void 0
|
|
61
61
|
}
|
|
62
62
|
];
|
|
63
|
+
const feedbackWords = {
|
|
64
|
+
awaiting_verdict: { label: labels.tiles.awaitingVerdict, description: labels.tiles.awaitingVerdictHint },
|
|
65
|
+
released_for_work: { label: labels.tiles.releasedForWork, description: labels.tiles.releasedForWorkHint }
|
|
66
|
+
};
|
|
63
67
|
const tiles = [
|
|
64
68
|
...shared.map((t) => ({ ...t, count: attention ? attention[t.key] : void 0, href: href[t.key] })),
|
|
65
|
-
...extraTiles
|
|
69
|
+
...extraTiles.map((t) => {
|
|
70
|
+
const words = Object.prototype.hasOwnProperty.call(feedbackWords, t.key) ? feedbackWords[t.key] : void 0;
|
|
71
|
+
return words ? {
|
|
72
|
+
...t,
|
|
73
|
+
label: t.label ?? words.label,
|
|
74
|
+
description: t.description ?? (t.label === void 0 ? words.description : void 0)
|
|
75
|
+
} : t;
|
|
76
|
+
})
|
|
66
77
|
].filter((t) => loading || t.count !== null && t.count !== void 0);
|
|
67
78
|
if (!loading && tiles.length === 0) return error ? errorBox(true) : null;
|
|
68
79
|
const grid = /* @__PURE__ */ jsx(StatTileGrid, { "data-slot": error ? void 0 : "admin-overview", loading, className: error ? void 0 : className, children: tiles.map((t) => /* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/admin-overview.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { AlertBanner } from \"../components/alert-banner\";\nimport { Button } from \"../components/button\";\nimport { StatTile, StatTileGrid } from \"../components/stat-tile\";\nimport type { StatTileProps } from \"../components/stat-tile\";\nimport type { LabelOverride } from \"../i18n/kit-labels\";\nimport { cn } from \"../lib/cn\";\nimport { useAdminAreaLabels } from \"./admin-area-labels\";\nimport type { AdminAreaLabels } from \"./admin-area-labels\";\n\n/**\n * The admin's start page (0.42.0, docs/admin-harmonization.md §2.4, §6.1–§6.5): a grid\n * of tiles, each a count of what is waiting and a link to where the work is, fed by one\n * server answer.\n */\n\n/**\n * `GET /admin/attention` (§2.4 decision 14, §6.4) — server-kit's `AdminAttention`\n * (`eifi1_server_kit.user_admin.attention`), as the API sends it.\n *\n * Each count is `null` where the app has no such thing or this viewer cannot act on it,\n * and every count is `null` in a demo session. Demo accounts never count. `extra` holds\n * the app's own counts, keyed as its overview names them (keksdose `support_waiting`,\n * Kurvenschmiede `released_for_work`).\n */\nexport interface AdminAttention {\n /** \"To triage\": OPEN reports, crash reports included (§6.1). */\n feedback_waiting: number | null;\n /** Accounts that asked to be deleted — only where this viewer acts on them (§6.2). */\n deletion_requests: number | null;\n invitations_pending: number | null;\n users_total: number | null;\n /** Accounts made in the last seven days. */\n users_new_7d: number | null;\n extra: Record<string, number | null>;\n}\n\n/** The shared counts that are a tile of their own (`users_new_7d` is the users tile's line). */\nexport type AdminAttentionTile = \"feedback_waiting\" | \"deletion_requests\" | \"invitations_pending\" | \"users_total\";\n\n/** Where each shared tile links when the app names nothing else (§2.4 decision 13). */\nexport const DEFAULT_ADMIN_ATTENTION_HREFS: Readonly<Record<AdminAttentionTile, string>> = Object.freeze({\n feedback_waiting: \"/feedback\",\n deletion_requests: \"/admin/users?state=deletion_requested\",\n invitations_pending: \"/admin/invitations\",\n users_total: \"/admin/users\",\n});\n\n/**\n * The account menu's badge (§2.4 decision 15, §6.3): the sum of the counts that are work\n * waiting on THIS admin — each app names its own (keksdose `[\"feedback_waiting\",\n * \"support_waiting\", \"awaiting_verdict\"]`, kastlan `[\"feedback_waiting\"]`, Kurvenschmiede\n * `[\"feedback_waiting\", \"deletion_requests\"]`). A name is a shared count or an `extra`\n * key; a `null` or missing count adds nothing. Invitations and users never belong in it.\n * `null` while there is no answer yet, so the badge shows nothing rather than a 0.\n */\nexport function adminWaitingCount(\n attention: AdminAttention | null | undefined,\n counts: readonly (AdminAttentionTile | (string & {}))[],\n): number | null {\n if (!attention) return null;\n let sum = 0;\n for (const key of counts) {\n const shared = (attention as unknown as Record<string, unknown>)[key];\n const value = typeof shared === \"number\" ? shared : attention.extra?.[key];\n if (typeof value === \"number\" && value > 0) sum += value;\n }\n return sum;\n}\n\nexport interface AttentionTileProps extends Omit<StatTileProps, \"href\" | \"onClick\" | \"value\"> {\n /** The count. `null` shows the tile's dash; {@link AdminOverview} leaves such a tile out. */\n count: number | null | undefined;\n /** Where the work is — the tile is a link to it. Since 0.43 it may be left out: a\n * quiet count, no link (Kurvenschmiede). */\n href?: string;\n}\n\n/**\n * One tile of the overview: a {@link StatTile} that is a LINK to where the work is, its\n * count the headline. The whole tile is the link, named by its label; route it through\n * `renderLink` or the provider's link component, as any StatTile. Without `href` (0.43) it\n * is a quiet count, no link.\n */\nexport function AttentionTile({ count, href, ...tile }: AttentionTileProps) {\n return <StatTile data-slot=\"attention-tile\" {...tile} value={count ?? null} href={href} />;\n}\n\n/** One of the app's own tiles (§2.4 decision 13: keksdose's \"Support waiting\"). */\nexport interface AdminExtraTile {\n /** Unique among the tiles; usually the `extra` key it shows. */\n key: string;\n label: ReactNode;\n /** `null` or `undefined` leaves the tile out, as for a shared one. */\n count: number | null | undefined;\n /** Where the work is. Left out (0.43, Kurvenschmiede): a quiet count with no link —\n * a number worth seeing that has no page of its own. */\n href?: string;\n /** A line under the count. */\n description?: ReactNode;\n}\n\nexport interface AdminOverviewProps {\n /** The server's answer; `undefined` while it loads (the grid shows its skeletons). */\n attention: AdminAttention | null | undefined;\n /**\n * Where the shared tiles link, over {@link DEFAULT_ADMIN_ATTENTION_HREFS} — the\n * inbox's address filtered to OPEN (§6.1) is the app's own:\n * `{ feedback_waiting: \"/feedback?status=OPEN\" }`.\n */\n hrefs?: Partial<Record<AdminAttentionTile, string>>;\n /** The app's own tiles, after the shared ones, in this order. */\n extraTiles?: readonly AdminExtraTile[];\n /** Passed to every tile — `renderLink` for the router, `size`, `variant`. */\n tileProps?: Pick<StatTileProps, \"renderLink\" | \"size\" | \"variant\" | \"locale\">;\n /**\n * 0.43 (keksdose): the answer failed — the query's `error`, or `true`. Anything truthy\n * shows `adminArea.overviewError` in a danger box, with \"Try again\" when\n * {@link onRetry} is given. A failed poll keeps the last good tiles: with `attention`\n * still there (a query keeps its last data), the box shows ALONGSIDE them, above, not\n * instead (build review 0.43, keksdose N7) — pass `attention.data` as it is. Without\n * an answer yet, the box alone. Without `error`: `undefined` is the skeletons and\n * `null` nothing, as before.\n */\n error?: unknown;\n /** \"Try again\" in the error box — the query's `refetch`. */\n onRetry?: () => void;\n /** Prop > `<UiKitProvider labels={{ adminArea }}>` > English. */\n labels?: LabelOverride<AdminAreaLabels>;\n className?: string;\n}\n\n/**\n * The overview section (§2.4): the shared tiles in their order — To triage, Deletion\n * requests, Pending invitations, Users (with the new accounts of the last 7 days under\n * the total) — then the app's `extraTiles`. A tile whose count is `null` is left out,\n * so each app shows only what it has; a `0` is a quiet tile, \"nothing waiting\".\n *\n * The overview is the admin area's landing page, standalone: it is NOT wrapped in a\n * `SettingsSection` (`adminSections` gives it no entry; render it as the page's body, as\n * Kurvenschmiede's `OverviewPanel` does). keksdose links its metrics from here with at\n * most two tiles (§6.5).\n */\nexport function AdminOverview({\n attention,\n hrefs,\n extraTiles = [],\n tileProps,\n error,\n onRetry,\n labels: labelsProp,\n className,\n}: AdminOverviewProps) {\n const labels = useAdminAreaLabels(labelsProp);\n /** The error box: on its own (`alone`) it is the overview's root. */\n const errorBox = (alone: boolean) => (\n <AlertBanner\n data-slot={alone ? \"admin-overview\" : undefined}\n data-state={alone ? \"error\" : undefined}\n variant=\"box\"\n tone=\"danger\"\n className={alone ? className : undefined}\n action={\n onRetry ? (\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={() => onRetry()}>\n {labels.retry}\n </Button>\n ) : undefined\n }\n >\n {labels.overviewError}\n </AlertBanner>\n );\n // Nothing good to keep: the box alone.\n if (error && !attention) return errorBox(true);\n const href = { ...DEFAULT_ADMIN_ATTENTION_HREFS, ...hrefs };\n const loading = attention === undefined;\n const shared: { key: AdminAttentionTile; label: string; description?: ReactNode }[] = [\n { key: \"feedback_waiting\", label: labels.tiles.feedbackWaiting },\n { key: \"deletion_requests\", label: labels.tiles.deletionRequests },\n { key: \"invitations_pending\", label: labels.tiles.invitationsPending },\n {\n key: \"users_total\",\n label: labels.tiles.users,\n description:\n attention && typeof attention.users_new_7d === \"number\" ? labels.tiles.usersNew(attention.users_new_7d) : undefined,\n },\n ];\n const tiles = [\n ...shared.map((t) => ({ ...t, count: attention ? attention[t.key] : undefined, href: href[t.key] })),\n ...extraTiles,\n ].filter((t) => loading || (t.count !== null && t.count !== undefined));\n if (!loading && tiles.length === 0) return error ? errorBox(true) : null;\n const grid = (\n <StatTileGrid data-slot={error ? undefined : \"admin-overview\"} loading={loading} className={error ? undefined : className}>\n {tiles.map((t) => (\n <AttentionTile\n key={t.key}\n {...tileProps}\n data-attention={t.key}\n label={t.label}\n count={t.count}\n href={t.href}\n description={t.description}\n />\n ))}\n </StatTileGrid>\n );\n if (!error) return grid;\n // A failed poll: the error above the last good tiles.\n return (\n <div data-slot=\"admin-overview\" data-state=\"error\" className={cn(\"space-y-4\", className)}>\n {errorBox(false)}\n {grid}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/admin/admin-overview.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { AlertBanner } from \"../components/alert-banner\";\nimport { Button } from \"../components/button\";\nimport { StatTile, StatTileGrid } from \"../components/stat-tile\";\nimport type { StatTileProps } from \"../components/stat-tile\";\nimport type { LabelOverride } from \"../i18n/kit-labels\";\nimport { cn } from \"../lib/cn\";\nimport { useAdminAreaLabels } from \"./admin-area-labels\";\nimport type { AdminAreaLabels } from \"./admin-area-labels\";\n\n/**\n * The admin's start page (0.42.0, docs/admin-harmonization.md §2.4, §6.1–§6.5): a grid\n * of tiles, each a count of what is waiting and a link to where the work is, fed by one\n * server answer.\n */\n\n/**\n * `GET /admin/attention` (§2.4 decision 14, §6.4) — server-kit's `AdminAttention`\n * (`eifi1_server_kit.user_admin.attention`), as the API sends it.\n *\n * Each count is `null` where the app has no such thing or this viewer cannot act on it,\n * and every count is `null` in a demo session. Demo accounts never count. `extra` holds\n * the app's own counts, keyed as its overview names them (keksdose `support_waiting`,\n * Kurvenschmiede `released_for_work`).\n */\nexport interface AdminAttention {\n /** \"To triage\": OPEN reports, crash reports included (§6.1). */\n feedback_waiting: number | null;\n /** Accounts that asked to be deleted — only where this viewer acts on them (§6.2). */\n deletion_requests: number | null;\n invitations_pending: number | null;\n users_total: number | null;\n /** Accounts made in the last seven days. */\n users_new_7d: number | null;\n extra: Record<string, number | null>;\n}\n\n/** The shared counts that are a tile of their own (`users_new_7d` is the users tile's line). */\nexport type AdminAttentionTile = \"feedback_waiting\" | \"deletion_requests\" | \"invitations_pending\" | \"users_total\";\n\n/** Where each shared tile links when the app names nothing else (§2.4 decision 13). */\nexport const DEFAULT_ADMIN_ATTENTION_HREFS: Readonly<Record<AdminAttentionTile, string>> = Object.freeze({\n feedback_waiting: \"/feedback\",\n deletion_requests: \"/admin/users?state=deletion_requested\",\n invitations_pending: \"/admin/invitations\",\n users_total: \"/admin/users\",\n});\n\n/**\n * The account menu's badge (§2.4 decision 15, §6.3): the sum of the counts that are work\n * waiting on THIS admin — each app names its own (keksdose `[\"feedback_waiting\",\n * \"support_waiting\", \"awaiting_verdict\"]`, kastlan `[\"feedback_waiting\"]`, Kurvenschmiede\n * `[\"feedback_waiting\", \"deletion_requests\"]`). A name is a shared count or an `extra`\n * key; a `null` or missing count adds nothing. Invitations and users never belong in it.\n * `null` while there is no answer yet, so the badge shows nothing rather than a 0.\n */\nexport function adminWaitingCount(\n attention: AdminAttention | null | undefined,\n counts: readonly (AdminAttentionTile | (string & {}))[],\n): number | null {\n if (!attention) return null;\n let sum = 0;\n for (const key of counts) {\n const shared = (attention as unknown as Record<string, unknown>)[key];\n const value = typeof shared === \"number\" ? shared : attention.extra?.[key];\n if (typeof value === \"number\" && value > 0) sum += value;\n }\n return sum;\n}\n\nexport interface AttentionTileProps extends Omit<StatTileProps, \"href\" | \"onClick\" | \"value\"> {\n /** The count. `null` shows the tile's dash; {@link AdminOverview} leaves such a tile out. */\n count: number | null | undefined;\n /** Where the work is — the tile is a link to it. Since 0.43 it may be left out: a\n * quiet count, no link (Kurvenschmiede). */\n href?: string;\n}\n\n/**\n * One tile of the overview: a {@link StatTile} that is a LINK to where the work is, its\n * count the headline. The whole tile is the link, named by its label; route it through\n * `renderLink` or the provider's link component, as any StatTile. Without `href` (0.43) it\n * is a quiet count, no link.\n */\nexport function AttentionTile({ count, href, ...tile }: AttentionTileProps) {\n return <StatTile data-slot=\"attention-tile\" {...tile} value={count ?? null} href={href} />;\n}\n\n/**\n * 0.46 (Kurvenschmiede 1, admin §9.5): the `extra` keys server-kit's\n * `feedback_attention` fills, whose tiles have the kit's words\n * (`adminArea.tiles.awaitingVerdict`, `releasedForWork`, with a line each).\n */\nexport type AdminFeedbackTileKey = \"awaiting_verdict\" | \"released_for_work\";\n\n/** One of the app's own tiles (§2.4 decision 13: keksdose's \"Support waiting\"). */\nexport type AdminExtraTile = AdminOwnTile | AdminFeedbackTile;\n\n/** A tile of the app's own: it brings its words. */\nexport interface AdminOwnTile {\n /** Unique among the tiles; usually the `extra` key it shows. */\n key: string;\n label: ReactNode;\n /** `null` or `undefined` leaves the tile out, as for a shared one. */\n count: number | null | undefined;\n /** Where the work is. Left out (0.43, Kurvenschmiede): a quiet count with no link —\n * a number worth seeing that has no page of its own. */\n href?: string;\n /** A line under the count. */\n description?: ReactNode;\n}\n\n/** 0.46: a tile of server-kit's feedback counts — `label` and `description` default to\n * the kit's words; an app's own still win. The kit's `description` fills in only when\n * `label` is left out too: an app that names the tile gets no hint line it did not ask for. */\nexport interface AdminFeedbackTile extends Omit<AdminOwnTile, \"key\" | \"label\"> {\n key: AdminFeedbackTileKey;\n label?: ReactNode;\n}\n\nexport interface AdminOverviewProps {\n /** The server's answer; `undefined` while it loads (the grid shows its skeletons). */\n attention: AdminAttention | null | undefined;\n /**\n * Where the shared tiles link, over {@link DEFAULT_ADMIN_ATTENTION_HREFS} — the\n * inbox's address filtered to OPEN (§6.1) is the app's own:\n * `{ feedback_waiting: \"/feedback?status=OPEN\" }`.\n */\n hrefs?: Partial<Record<AdminAttentionTile, string>>;\n /** The app's own tiles, after the shared ones, in this order. */\n extraTiles?: readonly AdminExtraTile[];\n /** Passed to every tile — `renderLink` for the router, `size`, `variant`. */\n tileProps?: Pick<StatTileProps, \"renderLink\" | \"size\" | \"variant\" | \"locale\">;\n /**\n * 0.43 (keksdose): the answer failed — the query's `error`, or `true`. Anything truthy\n * shows `adminArea.overviewError` in a danger box, with \"Try again\" when\n * {@link onRetry} is given. A failed poll keeps the last good tiles: with `attention`\n * still there (a query keeps its last data), the box shows ALONGSIDE them, above, not\n * instead (build review 0.43, keksdose N7) — pass `attention.data` as it is. Without\n * an answer yet, the box alone. Without `error`: `undefined` is the skeletons and\n * `null` nothing, as before.\n */\n error?: unknown;\n /** \"Try again\" in the error box — the query's `refetch`. */\n onRetry?: () => void;\n /** Prop > `<UiKitProvider labels={{ adminArea }}>` > English. */\n labels?: LabelOverride<AdminAreaLabels>;\n className?: string;\n}\n\n/**\n * The overview section (§2.4): the shared tiles in their order — To triage, Deletion\n * requests, Pending invitations, Users (with the new accounts of the last 7 days under\n * the total) — then the app's `extraTiles`. A tile whose count is `null` is left out,\n * so each app shows only what it has; a `0` is a quiet tile, \"nothing waiting\".\n *\n * The overview is the admin area's landing page, standalone: it is NOT wrapped in a\n * `SettingsSection` (`adminSections` gives it no entry; render it as the page's body, as\n * Kurvenschmiede's `OverviewPanel` does). keksdose links its metrics from here with at\n * most two tiles (§6.5).\n */\nexport function AdminOverview({\n attention,\n hrefs,\n extraTiles = [],\n tileProps,\n error,\n onRetry,\n labels: labelsProp,\n className,\n}: AdminOverviewProps) {\n const labels = useAdminAreaLabels(labelsProp);\n /** The error box: on its own (`alone`) it is the overview's root. */\n const errorBox = (alone: boolean) => (\n <AlertBanner\n data-slot={alone ? \"admin-overview\" : undefined}\n data-state={alone ? \"error\" : undefined}\n variant=\"box\"\n tone=\"danger\"\n className={alone ? className : undefined}\n action={\n onRetry ? (\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={() => onRetry()}>\n {labels.retry}\n </Button>\n ) : undefined\n }\n >\n {labels.overviewError}\n </AlertBanner>\n );\n // Nothing good to keep: the box alone.\n if (error && !attention) return errorBox(true);\n const href = { ...DEFAULT_ADMIN_ATTENTION_HREFS, ...hrefs };\n const loading = attention === undefined;\n const shared: { key: AdminAttentionTile; label: string; description?: ReactNode }[] = [\n { key: \"feedback_waiting\", label: labels.tiles.feedbackWaiting },\n { key: \"deletion_requests\", label: labels.tiles.deletionRequests },\n { key: \"invitations_pending\", label: labels.tiles.invitationsPending },\n {\n key: \"users_total\",\n label: labels.tiles.users,\n description:\n attention && typeof attention.users_new_7d === \"number\" ? labels.tiles.usersNew(attention.users_new_7d) : undefined,\n },\n ];\n const feedbackWords: Record<AdminFeedbackTileKey, { label: string; description: string }> = {\n awaiting_verdict: { label: labels.tiles.awaitingVerdict, description: labels.tiles.awaitingVerdictHint },\n released_for_work: { label: labels.tiles.releasedForWork, description: labels.tiles.releasedForWorkHint },\n };\n const tiles = [\n ...shared.map((t) => ({ ...t, count: attention ? attention[t.key] : undefined, href: href[t.key] })),\n ...extraTiles.map((t) => {\n const words = Object.prototype.hasOwnProperty.call(feedbackWords, t.key)\n ? feedbackWords[t.key as AdminFeedbackTileKey]\n : undefined;\n // The kit's hint comes with the kit's label only: an app that names the tile\n // itself gets no line it did not ask for (keksdose S5, round 0.46).\n return words\n ? {\n ...t,\n label: t.label ?? words.label,\n description: t.description ?? (t.label === undefined ? words.description : undefined),\n }\n : t;\n }),\n ].filter((t) => loading || (t.count !== null && t.count !== undefined));\n if (!loading && tiles.length === 0) return error ? errorBox(true) : null;\n const grid = (\n <StatTileGrid data-slot={error ? undefined : \"admin-overview\"} loading={loading} className={error ? undefined : className}>\n {tiles.map((t) => (\n <AttentionTile\n key={t.key}\n {...tileProps}\n data-attention={t.key}\n label={t.label}\n count={t.count}\n href={t.href}\n description={t.description}\n />\n ))}\n </StatTileGrid>\n );\n if (!error) return grid;\n // A failed poll: the error above the last good tiles.\n return (\n <div data-slot=\"admin-overview\" data-state=\"error\" className={cn(\"space-y-4\", className)}>\n {errorBox(false)}\n {grid}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -37,8 +37,9 @@ export interface MailTestAnswer {
|
|
|
37
37
|
export declare const MAIL_TEST_ANCHOR = "admin-mail-test";
|
|
38
38
|
export interface MailTestCardProps extends Omit<SettingsSectionProps, "anchor" | "title" | "description" | "children"> {
|
|
39
39
|
/** The installation's transport, as the admin page has it (`mail_backend`): the chip
|
|
40
|
-
* before a send. After one, the answer's.
|
|
41
|
-
|
|
40
|
+
* before a send. After one, the answer's. Optional (0.46): without it the chip shows
|
|
41
|
+
* from the first answer on, and an app needs no query only for the chip. */
|
|
42
|
+
mailBackend?: string;
|
|
42
43
|
/** The app's `POST /admin/mail/test` (kastlan `/platform/mail/test`), no body: it
|
|
43
44
|
* awaits the app's own send and answers `{sent, mail_backend}`. A rejection is read
|
|
44
45
|
* for a 429 (the limiter); anything else is "could not send". */
|
|
@@ -28,6 +28,7 @@ function MailTestCard({
|
|
|
28
28
|
const labels = useKitLabels("mailTestCard", DEFAULT_MAIL_TEST_CARD_LABELS, labelsProp);
|
|
29
29
|
const [busy, setBusy] = useState(false);
|
|
30
30
|
const [outcome, setOutcome] = useState(null);
|
|
31
|
+
const [answered, setAnswered] = useState(void 0);
|
|
31
32
|
const run = async () => {
|
|
32
33
|
if (busy) return;
|
|
33
34
|
setBusy(true);
|
|
@@ -35,6 +36,7 @@ function MailTestCard({
|
|
|
35
36
|
try {
|
|
36
37
|
const answer = await send();
|
|
37
38
|
const backend2 = answer.mail_backend;
|
|
39
|
+
if (backend2) setAnswered(backend2);
|
|
38
40
|
if (backend2 === "console") setOutcome({ kind: "console", backend: backend2 });
|
|
39
41
|
else setOutcome(answer.sent ? { kind: "sent", backend: backend2 } : { kind: "failed", backend: backend2 });
|
|
40
42
|
} catch (error) {
|
|
@@ -43,7 +45,7 @@ function MailTestCard({
|
|
|
43
45
|
setBusy(false);
|
|
44
46
|
}
|
|
45
47
|
};
|
|
46
|
-
const backend =
|
|
48
|
+
const backend = answered ?? mailBackend;
|
|
47
49
|
return /* @__PURE__ */ jsx(
|
|
48
50
|
SettingsSection,
|
|
49
51
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/mail-test-card.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Send } from \"lucide-react\";\n\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { isRateLimited, retryAfterSeconds } from \"../auth/auth-errors\";\nimport { AlertBanner } from \"../components/alert-banner\";\nimport { Button } from \"../components/button\";\nimport { Chip } from \"../components/chip\";\nimport { SettingsSection, type SettingsSectionProps } from \"../settings/settings-section\";\nimport { DEFAULT_MAIL_TEST_CARD_LABELS } from \"./mail-test-card-labels\";\n\n/* ── Labels ──────────────────────────────────────────────────────────────── */\n\n/** Every string {@link MailTestCard} renders — the `mailTestCard` namespace of\n * `<UiKitProvider labels>`, overridable per instance through `labels`\n * (docs/admin-harmonization.md §9.2.4). */\nexport interface MailTestCardLabels {\n /** The card's title, and the app's catalogue entry's. */\n title: string;\n /** The sentence under it. */\n description: string;\n /** The transport's chip: \"Transport: resend\". `backend` is the server's `mail_backend`. */\n transport: (backend: string) => string;\n /** The button. */\n action: string;\n /** The answer said \"sent\" through a real transport. `address` is masked (\"a…@example.com\"). */\n sent: (address: string) => string;\n /** `mail_backend` is `console`: said whatever `sent` is — a console answers `true`. */\n console: string;\n /** Not sent, or the request failed. The provider's error stays in the server log. */\n failed: string;\n /** HTTP 429 — the app's limiter (server-kit's `MAIL_TEST_LIMIT`, 3 per 10 minutes);\n * `seconds` from `Retry-After` when the response has one. */\n rateLimited: (seconds?: number) => string;\n /** For the app's catalogue entry (`keywords`); the card never reads it. */\n keywords: string;\n}\n\n// Defined in ./mail-test-card-labels, which holds no component code.\nexport { DEFAULT_MAIL_TEST_CARD_LABELS };\n\n/* ── The card ────────────────────────────────────────────────────────────── */\n\n/** The answer of `POST /admin/mail/test` — server-kit 0.15's `MailTestResult`. */\nexport interface MailTestAnswer {\n sent: boolean;\n /** The transport that took it: `console`, `resend`, `smtp`, … */\n mail_backend: string;\n}\n\n/** The card's default DOM id: its catalogue entry's `anchor`, the `?focus=` target. */\nexport const MAIL_TEST_ANCHOR = \"admin-mail-test\";\n\nexport interface MailTestCardProps\n extends Omit<SettingsSectionProps, \"anchor\" | \"title\" | \"description\" | \"children\"> {\n /** The installation's transport, as the admin page has it (`mail_backend`): the chip\n * before a send. After one, the answer's. */\n mailBackend: string;\n /** The app's `POST /admin/mail/test` (kastlan `/platform/mail/test`), no body: it\n * awaits the app's own send and answers `{sent, mail_backend}`. A rejection is read\n * for a 429 (the limiter); anything else is \"could not send\". */\n send: () => Promise<MailTestAnswer>;\n /** The signed-in admin's own address — where the mail goes. Shown masked. */\n address: string;\n /** Default {@link MAIL_TEST_ANCHOR}; the app's catalogue entry names the same. */\n anchor?: string;\n /** The app's sentence after the kit's description. */\n description?: ReactNode;\n labels?: LabelOverride<MailTestCardLabels>;\n}\n\n/**\n * An address with its local part masked to its first character: \"ada@example.com\" →\n * \"a…@example.com\". The domain stays, so the admin sees which mailbox to open.\n */\nexport function maskMailAddress(address: string): string {\n const text = address.trim();\n const at = text.lastIndexOf(\"@\");\n if (at <= 0) return text === \"\" ? \"\" : `${Array.from(text)[0]}…`;\n return `${Array.from(text.slice(0, at))[0]}…${text.slice(at)}`;\n}\n\ntype Outcome =\n | { kind: \"sent\"; backend: string }\n | { kind: \"console\"; backend: string }\n | { kind: \"failed\"; backend?: string }\n | { kind: \"limited\"; seconds?: number };\n\n/**\n * \"Send a test mail to me\" (docs/admin-harmonization.md §9.2): a `SettingsSection` with a\n * sentence, the transport as a chip, the button and the result line. One mail goes to the\n * signed-in admin's own address, never another, through the app's own send.\n *\n * **The result line reads `mail_backend` first**: with `console` it says \"nothing was sent\"\n * whatever `sent` is, since every console path answers `true` (§9.2 Today). Otherwise\n * `sent` says \"Sent to a…@example.com. Check the inbox.\" or \"Could not send.\" — the\n * provider's error stays in the server log (§9.2.7). A 429 says the limiter's line. The\n * button is busy while the request runs; the line is a live region.\n *\n * Not a commit: the test writes nothing, so it is not under the write lock (a demo never\n * reaches the admin area, and the server refuses it anyway). The app adds the catalogue\n * entry `{ id, anchor: MAIL_TEST_ANCHOR, title, keywords }` from these labels.\n */\nexport function MailTestCard({\n mailBackend,\n send,\n address,\n anchor = MAIL_TEST_ANCHOR,\n description,\n labels: labelsProp,\n ...rest\n}: MailTestCardProps) {\n const labels = useKitLabels(\"mailTestCard\", DEFAULT_MAIL_TEST_CARD_LABELS, labelsProp);\n const [busy, setBusy] = useState(false);\n const [outcome, setOutcome] = useState<Outcome | null>(null);\n\n const run = async () => {\n if (busy) return;\n setBusy(true);\n setOutcome(null);\n try {\n const answer = await send();\n const backend = answer.mail_backend;\n if (backend === \"console\") setOutcome({ kind: \"console\", backend });\n else setOutcome(answer.sent ? { kind: \"sent\", backend } : { kind: \"failed\", backend });\n } catch (error) {\n setOutcome(isRateLimited(error) ? { kind: \"limited\", seconds: retryAfterSeconds(error) } : { kind: \"failed\" });\n } finally {\n setBusy(false);\n }\n };\n\n const backend = outcome && \"backend\" in outcome && outcome.backend ? outcome.backend : mailBackend;\n\n return (\n <SettingsSection\n {...rest}\n anchor={anchor}\n title={labels.title}\n description={\n description != null ? (\n <>\n {labels.description} {description}\n </>\n ) : (\n labels.description\n )\n }\n >\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button variant=\"secondary\" pending={busy} onClick={() => void run()}>\n <Send className=\"size-4\" aria-hidden />\n {labels.action}\n </Button>\n {backend && (\n <Chip size=\"sm\" data-slot=\"mail-backend\">\n {labels.transport(backend)}\n </Chip>\n )}\n </div>\n {outcome?.kind === \"sent\" && (\n <AlertBanner variant=\"inline\" block tone=\"success\">\n <span data-private>{labels.sent(maskMailAddress(address))}</span>\n </AlertBanner>\n )}\n {outcome?.kind === \"console\" && (\n <AlertBanner variant=\"inline\" block tone=\"info\">\n {labels.console}\n </AlertBanner>\n )}\n {outcome?.kind === \"failed\" && (\n <AlertBanner variant=\"inline\" block tone=\"danger\">\n {labels.failed}\n </AlertBanner>\n )}\n {outcome?.kind === \"limited\" && (\n <AlertBanner variant=\"inline\" block tone=\"warning\">\n {labels.rateLimited(outcome.seconds)}\n </AlertBanner>\n )}\n </div>\n </SettingsSection>\n );\n}\n"],"mappings":";AA8IU,mBAWE,KAXF;AA9IV,SAAS,gBAAgB;AAEzB,SAAS,YAAY;AAErB,SAAS,oBAAwC;AACjD,SAAS,eAAe,yBAAyB;AACjD,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,uBAAkD;AAC3D,SAAS,qCAAqC;AA0CvC,MAAM,mBAAmB;AAwBzB,SAAS,gBAAgB,SAAyB;AACvD,QAAM,OAAO,QAAQ,KAAK;AAC1B,QAAM,KAAK,KAAK,YAAY,GAAG;AAC/B,MAAI,MAAM,EAAG,QAAO,SAAS,KAAK,KAAK,GAAG,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;AAC7D,SAAO,GAAG,MAAM,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,SAAI,KAAK,MAAM,EAAE,CAAC;AAC9D;AAuBO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,EACR,GAAG;AACL,GAAsB;AACpB,QAAM,SAAS,aAAa,gBAAgB,+BAA+B,UAAU;AACrF,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAyB,IAAI;AAE3D,QAAM,MAAM,YAAY;AACtB,QAAI,KAAM;AACV,YAAQ,IAAI;AACZ,eAAW,IAAI;AACf,QAAI;AACF,YAAM,SAAS,MAAM,KAAK;AAC1B,YAAMA,WAAU,OAAO;AACvB,UAAIA,aAAY,UAAW,YAAW,EAAE,MAAM,WAAW,SAAAA,SAAQ,CAAC;AAAA,UAC7D,YAAW,OAAO,OAAO,EAAE,MAAM,QAAQ,SAAAA,SAAQ,IAAI,EAAE,MAAM,UAAU,SAAAA,SAAQ,CAAC;AAAA,IACvF,SAAS,OAAO;AACd,iBAAW,cAAc,KAAK,IAAI,EAAE,MAAM,WAAW,SAAS,kBAAkB,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAAA,IAC/G,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,UAAU,WAAW,aAAa,WAAW,QAAQ,UAAU,QAAQ,UAAU;AAEvF,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ;AAAA,MACA,OAAO,OAAO;AAAA,MACd,aACE,eAAe,OACb,iCACG;AAAA,eAAO;AAAA,QAAY;AAAA,QAAE;AAAA,SACxB,IAEA,OAAO;AAAA,MAIX,+BAAC,SAAI,WAAU,aACb;AAAA,6BAAC,SAAI,WAAU,qCACb;AAAA,+BAAC,UAAO,SAAQ,aAAY,SAAS,MAAM,SAAS,MAAM,KAAK,IAAI,GACjE;AAAA,gCAAC,QAAK,WAAU,UAAS,eAAW,MAAC;AAAA,YACpC,OAAO;AAAA,aACV;AAAA,UACC,WACC,oBAAC,QAAK,MAAK,MAAK,aAAU,gBACvB,iBAAO,UAAU,OAAO,GAC3B;AAAA,WAEJ;AAAA,QACC,SAAS,SAAS,UACjB,oBAAC,eAAY,SAAQ,UAAS,OAAK,MAAC,MAAK,WACvC,8BAAC,UAAK,gBAAY,MAAE,iBAAO,KAAK,gBAAgB,OAAO,CAAC,GAAE,GAC5D;AAAA,QAED,SAAS,SAAS,aACjB,oBAAC,eAAY,SAAQ,UAAS,OAAK,MAAC,MAAK,QACtC,iBAAO,SACV;AAAA,QAED,SAAS,SAAS,YACjB,oBAAC,eAAY,SAAQ,UAAS,OAAK,MAAC,MAAK,UACtC,iBAAO,QACV;AAAA,QAED,SAAS,SAAS,aACjB,oBAAC,eAAY,SAAQ,UAAS,OAAK,MAAC,MAAK,WACtC,iBAAO,YAAY,QAAQ,OAAO,GACrC;AAAA,SAEJ;AAAA;AAAA,EACF;AAEJ;","names":["backend"]}
|
|
1
|
+
{"version":3,"sources":["../../src/admin/mail-test-card.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Send } from \"lucide-react\";\n\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { isRateLimited, retryAfterSeconds } from \"../auth/auth-errors\";\nimport { AlertBanner } from \"../components/alert-banner\";\nimport { Button } from \"../components/button\";\nimport { Chip } from \"../components/chip\";\nimport { SettingsSection, type SettingsSectionProps } from \"../settings/settings-section\";\nimport { DEFAULT_MAIL_TEST_CARD_LABELS } from \"./mail-test-card-labels\";\n\n/* ── Labels ──────────────────────────────────────────────────────────────── */\n\n/** Every string {@link MailTestCard} renders — the `mailTestCard` namespace of\n * `<UiKitProvider labels>`, overridable per instance through `labels`\n * (docs/admin-harmonization.md §9.2.4). */\nexport interface MailTestCardLabels {\n /** The card's title, and the app's catalogue entry's. */\n title: string;\n /** The sentence under it. */\n description: string;\n /** The transport's chip: \"Transport: resend\". `backend` is the server's `mail_backend`. */\n transport: (backend: string) => string;\n /** The button. */\n action: string;\n /** The answer said \"sent\" through a real transport. `address` is masked (\"a…@example.com\"). */\n sent: (address: string) => string;\n /** `mail_backend` is `console`: said whatever `sent` is — a console answers `true`. */\n console: string;\n /** Not sent, or the request failed. The provider's error stays in the server log. */\n failed: string;\n /** HTTP 429 — the app's limiter (server-kit's `MAIL_TEST_LIMIT`, 3 per 10 minutes);\n * `seconds` from `Retry-After` when the response has one. */\n rateLimited: (seconds?: number) => string;\n /** For the app's catalogue entry (`keywords`); the card never reads it. */\n keywords: string;\n}\n\n// Defined in ./mail-test-card-labels, which holds no component code.\nexport { DEFAULT_MAIL_TEST_CARD_LABELS };\n\n/* ── The card ────────────────────────────────────────────────────────────── */\n\n/** The answer of `POST /admin/mail/test` — server-kit 0.15's `MailTestResult`. */\nexport interface MailTestAnswer {\n sent: boolean;\n /** The transport that took it: `console`, `resend`, `smtp`, … */\n mail_backend: string;\n}\n\n/** The card's default DOM id: its catalogue entry's `anchor`, the `?focus=` target. */\nexport const MAIL_TEST_ANCHOR = \"admin-mail-test\";\n\nexport interface MailTestCardProps\n extends Omit<SettingsSectionProps, \"anchor\" | \"title\" | \"description\" | \"children\"> {\n /** The installation's transport, as the admin page has it (`mail_backend`): the chip\n * before a send. After one, the answer's. Optional (0.46): without it the chip shows\n * from the first answer on, and an app needs no query only for the chip. */\n mailBackend?: string;\n /** The app's `POST /admin/mail/test` (kastlan `/platform/mail/test`), no body: it\n * awaits the app's own send and answers `{sent, mail_backend}`. A rejection is read\n * for a 429 (the limiter); anything else is \"could not send\". */\n send: () => Promise<MailTestAnswer>;\n /** The signed-in admin's own address — where the mail goes. Shown masked. */\n address: string;\n /** Default {@link MAIL_TEST_ANCHOR}; the app's catalogue entry names the same. */\n anchor?: string;\n /** The app's sentence after the kit's description. */\n description?: ReactNode;\n labels?: LabelOverride<MailTestCardLabels>;\n}\n\n/**\n * An address with its local part masked to its first character: \"ada@example.com\" →\n * \"a…@example.com\". The domain stays, so the admin sees which mailbox to open.\n */\nexport function maskMailAddress(address: string): string {\n const text = address.trim();\n const at = text.lastIndexOf(\"@\");\n if (at <= 0) return text === \"\" ? \"\" : `${Array.from(text)[0]}…`;\n return `${Array.from(text.slice(0, at))[0]}…${text.slice(at)}`;\n}\n\ntype Outcome =\n | { kind: \"sent\"; backend: string }\n | { kind: \"console\"; backend: string }\n | { kind: \"failed\"; backend?: string }\n | { kind: \"limited\"; seconds?: number };\n\n/**\n * \"Send a test mail to me\" (docs/admin-harmonization.md §9.2): a `SettingsSection` with a\n * sentence, the transport as a chip, the button and the result line. One mail goes to the\n * signed-in admin's own address, never another, through the app's own send.\n *\n * **The result line reads `mail_backend` first**: with `console` it says \"nothing was sent\"\n * whatever `sent` is, since every console path answers `true` (§9.2 Today). Otherwise\n * `sent` says \"Sent to a…@example.com. Check the inbox.\" or \"Could not send.\" — the\n * provider's error stays in the server log (§9.2.7). A 429 says the limiter's line. The\n * button is busy while the request runs; the line is a live region.\n *\n * Not a commit: the test writes nothing, so it is not under the write lock (a demo never\n * reaches the admin area, and the server refuses it anyway). The app adds the catalogue\n * entry `{ id, anchor: MAIL_TEST_ANCHOR, title, keywords }` from these labels.\n */\nexport function MailTestCard({\n mailBackend,\n send,\n address,\n anchor = MAIL_TEST_ANCHOR,\n description,\n labels: labelsProp,\n ...rest\n}: MailTestCardProps) {\n const labels = useKitLabels(\"mailTestCard\", DEFAULT_MAIL_TEST_CARD_LABELS, labelsProp);\n const [busy, setBusy] = useState(false);\n const [outcome, setOutcome] = useState<Outcome | null>(null);\n // The last transport an answer named: a later send with no answer (429, network) keeps it.\n const [answered, setAnswered] = useState<string | undefined>(undefined);\n\n const run = async () => {\n if (busy) return;\n setBusy(true);\n setOutcome(null);\n try {\n const answer = await send();\n const backend = answer.mail_backend;\n if (backend) setAnswered(backend);\n if (backend === \"console\") setOutcome({ kind: \"console\", backend });\n else setOutcome(answer.sent ? { kind: \"sent\", backend } : { kind: \"failed\", backend });\n } catch (error) {\n setOutcome(isRateLimited(error) ? { kind: \"limited\", seconds: retryAfterSeconds(error) } : { kind: \"failed\" });\n } finally {\n setBusy(false);\n }\n };\n\n const backend = answered ?? mailBackend;\n\n return (\n <SettingsSection\n {...rest}\n anchor={anchor}\n title={labels.title}\n description={\n description != null ? (\n <>\n {labels.description} {description}\n </>\n ) : (\n labels.description\n )\n }\n >\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button variant=\"secondary\" pending={busy} onClick={() => void run()}>\n <Send className=\"size-4\" aria-hidden />\n {labels.action}\n </Button>\n {backend && (\n <Chip size=\"sm\" data-slot=\"mail-backend\">\n {labels.transport(backend)}\n </Chip>\n )}\n </div>\n {outcome?.kind === \"sent\" && (\n <AlertBanner variant=\"inline\" block tone=\"success\">\n <span data-private>{labels.sent(maskMailAddress(address))}</span>\n </AlertBanner>\n )}\n {outcome?.kind === \"console\" && (\n <AlertBanner variant=\"inline\" block tone=\"info\">\n {labels.console}\n </AlertBanner>\n )}\n {outcome?.kind === \"failed\" && (\n <AlertBanner variant=\"inline\" block tone=\"danger\">\n {labels.failed}\n </AlertBanner>\n )}\n {outcome?.kind === \"limited\" && (\n <AlertBanner variant=\"inline\" block tone=\"warning\">\n {labels.rateLimited(outcome.seconds)}\n </AlertBanner>\n )}\n </div>\n </SettingsSection>\n );\n}\n"],"mappings":"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","names":["backend"]}
|
|
@@ -180,6 +180,10 @@ export interface UserRosterColumnsOptions<T extends UserRosterRow, R extends str
|
|
|
180
180
|
extra?: readonly DataTableColumn<T>[];
|
|
181
181
|
/** The row's action menu (§4). Left out: no actions column. */
|
|
182
182
|
actions?: (row: T) => UserRowActionList;
|
|
183
|
+
/** The actions column's heading (0.46.1, keksdose dev#598 rework — a VISIBLE "Actions"
|
|
184
|
+
* on every table with an actions column). Default: `labels.actions`, shown. A node
|
|
185
|
+
* replaces it; `false` keeps the pre-0.46.1 heading, a screen reader's name only. */
|
|
186
|
+
actionsHeader?: ReactNode | false;
|
|
183
187
|
/**
|
|
184
188
|
* Whether this row's role is a select rather than a chip, and with which reasons.
|
|
185
189
|
* Left out, or `false`: a chip. kastlan's several roles are never a select here — they
|
|
@@ -216,10 +220,10 @@ export interface UserRosterColumnsOptions<T extends UserRosterRow, R extends str
|
|
|
216
220
|
* Under `serverPagination` the `sortBy`s only make the headers clickable; the app sends
|
|
217
221
|
* the sort ({@link userRosterSort}).
|
|
218
222
|
*/
|
|
219
|
-
export declare function userRosterColumns<T extends UserRosterRow, R extends string = string>({ roles, stateOf, extra, actions, editableRole, onRoleChange, roleOf, emailColumn, labels: labelsProp, }: UserRosterColumnsOptions<T, R>): DataTableColumn<T>[];
|
|
223
|
+
export declare function userRosterColumns<T extends UserRosterRow, R extends string = string>({ roles, stateOf, extra, actions, actionsHeader, editableRole, onRoleChange, roleOf, emailColumn, labels: labelsProp, }: UserRosterColumnsOptions<T, R>): DataTableColumn<T>[];
|
|
220
224
|
/**
|
|
221
225
|
* {@link userRosterColumns} with the `userRoster` words from the provider, memoised on
|
|
222
226
|
* the options — pass stable callbacks (`useCallback`) and a module-level vocabulary, or
|
|
223
227
|
* the columns are rebuilt on every render.
|
|
224
228
|
*/
|
|
225
|
-
export declare function useUserRosterColumns<T extends UserRosterRow, R extends string = string>({ roles, stateOf, extra, actions, editableRole, onRoleChange, roleOf, emailColumn, labels: labelsProp, }: UserRosterColumnsOptions<T, R>): DataTableColumn<T>[];
|
|
229
|
+
export declare function useUserRosterColumns<T extends UserRosterRow, R extends string = string>({ roles, stateOf, extra, actions, actionsHeader, editableRole, onRoleChange, roleOf, emailColumn, labels: labelsProp, }: UserRosterColumnsOptions<T, R>): DataTableColumn<T>[];
|
|
@@ -86,6 +86,7 @@ function userRosterColumns({
|
|
|
86
86
|
stateOf = adminUserStates,
|
|
87
87
|
extra = [],
|
|
88
88
|
actions,
|
|
89
|
+
actionsHeader,
|
|
89
90
|
editableRole,
|
|
90
91
|
onRoleChange,
|
|
91
92
|
roleOf,
|
|
@@ -183,13 +184,14 @@ function userRosterColumns({
|
|
|
183
184
|
if (actions) {
|
|
184
185
|
columns.push({
|
|
185
186
|
key: "actions",
|
|
186
|
-
|
|
187
|
+
// On screen, as every actions column's heading is (0.46.1, keksdose dev#598 rework).
|
|
188
|
+
header: actionsHeader === false ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels.actions }) : actionsHeader ?? labels.actions,
|
|
187
189
|
headerText: labels.actions,
|
|
188
190
|
// The caller's own words only: the menu reads the provider's itself, and the
|
|
189
191
|
// English defaults merged in above must not shadow them.
|
|
190
192
|
cell: (row) => /* @__PURE__ */ jsx(RowName, { row, children: (name) => /* @__PURE__ */ jsx(UserRowActions, { actions: actions(row), name, labels: labelsProp }) }),
|
|
191
193
|
className: "w-px whitespace-nowrap text-end",
|
|
192
|
-
headClassName: "w-px",
|
|
194
|
+
headClassName: "w-px text-end",
|
|
193
195
|
// The menu button keeps its own click: a row link must not wrap it.
|
|
194
196
|
noRowLink: true
|
|
195
197
|
});
|
|
@@ -201,6 +203,7 @@ function useUserRosterColumns({
|
|
|
201
203
|
stateOf,
|
|
202
204
|
extra,
|
|
203
205
|
actions,
|
|
206
|
+
actionsHeader,
|
|
204
207
|
editableRole,
|
|
205
208
|
onRoleChange,
|
|
206
209
|
roleOf,
|
|
@@ -214,13 +217,14 @@ function useUserRosterColumns({
|
|
|
214
217
|
stateOf,
|
|
215
218
|
extra,
|
|
216
219
|
actions,
|
|
220
|
+
actionsHeader,
|
|
217
221
|
editableRole,
|
|
218
222
|
onRoleChange,
|
|
219
223
|
roleOf,
|
|
220
224
|
emailColumn,
|
|
221
225
|
labels
|
|
222
226
|
}),
|
|
223
|
-
[roles, stateOf, extra, actions, editableRole, onRoleChange, roleOf, emailColumn, labels]
|
|
227
|
+
[roles, stateOf, extra, actions, actionsHeader, editableRole, onRoleChange, roleOf, emailColumn, labels]
|
|
224
228
|
);
|
|
225
229
|
}
|
|
226
230
|
export {
|