@saasicat/ui-vue 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/README.md +64 -4
  2. package/dist/chunk-A3W2N26K.js +714 -0
  3. package/dist/chunk-QZOSDCNP.js +98 -0
  4. package/dist/chunk-X5SQ5HMB.js +3231 -0
  5. package/dist/client/index.cjs +3988 -0
  6. package/dist/client/index.d.cts +321 -0
  7. package/dist/client/index.d.ts +321 -0
  8. package/dist/client/index.js +70 -0
  9. package/dist/index.cjs +4446 -1249
  10. package/dist/index.d.cts +297 -888
  11. package/dist/index.d.ts +297 -888
  12. package/dist/index.js +609 -1348
  13. package/dist/messages-7b0P8W75.d.cts +1652 -0
  14. package/dist/messages-7b0P8W75.d.ts +1652 -0
  15. package/dist/quasar/index.cjs +3340 -0
  16. package/dist/quasar/index.d.cts +95 -0
  17. package/dist/quasar/index.d.ts +95 -0
  18. package/dist/quasar/index.js +96 -0
  19. package/dist/testing-e2e/admin-pages-suite.cjs +14 -11
  20. package/dist/testing-e2e/admin-pages-suite.js +14 -11
  21. package/dist/use-super-admin-i18n-B3v3-SWZ.d.ts +210 -0
  22. package/dist/use-super-admin-i18n-DMktRVEt.d.cts +210 -0
  23. package/package.json +23 -3
  24. package/src/{action-registry.ts → client/action-registry.ts} +4 -4
  25. package/src/{batch-column-fetcher.ts → client/batch-column-fetcher.ts} +4 -4
  26. package/src/{boot-loader.ts → client/boot-loader.ts} +3 -3
  27. package/src/client/i18n/currency.ts +42 -0
  28. package/src/client/i18n/define.ts +59 -0
  29. package/src/client/i18n/format.ts +12 -0
  30. package/src/client/i18n/index.ts +14 -0
  31. package/src/client/i18n/locale.ts +15 -0
  32. package/src/client/i18n/messages/audit.ts +42 -0
  33. package/src/client/i18n/messages/bundles.ts +459 -0
  34. package/src/client/i18n/messages/common.ts +147 -0
  35. package/src/client/i18n/messages/dashboard.ts +16 -0
  36. package/src/client/i18n/messages/discovery.ts +229 -0
  37. package/src/client/i18n/messages/email.ts +125 -0
  38. package/src/client/i18n/messages/marketing.ts +270 -0
  39. package/src/client/i18n/messages/nav.ts +72 -0
  40. package/src/client/i18n/messages/pilots.ts +192 -0
  41. package/src/client/i18n/messages/plan-detail.ts +266 -0
  42. package/src/client/i18n/messages/plan-editor.ts +177 -0
  43. package/src/client/i18n/messages/plan-versions.ts +251 -0
  44. package/src/client/i18n/messages/plans.ts +503 -0
  45. package/src/client/i18n/messages/promos.ts +202 -0
  46. package/src/client/i18n/messages/shell.ts +145 -0
  47. package/src/client/i18n/messages/tenants.ts +104 -0
  48. package/src/client/i18n/messages/users.ts +78 -0
  49. package/src/client/i18n/messages.ts +84 -0
  50. package/src/client/index.ts +27 -0
  51. package/src/{manifest-loader.ts → client/manifest-loader.ts} +4 -4
  52. package/src/{nav-builder.ts → client/nav-builder.ts} +44 -51
  53. package/src/{plan-versions-catalog.ts → client/plan-versions-catalog.ts} +42 -30
  54. package/src/components/BundleVersionPublishDialog.vue +35 -24
  55. package/src/components/FeatureGate.vue +1 -1
  56. package/src/components/MarketingPromotionsTab.vue +112 -59
  57. package/src/components/MfaPromptDialog.vue +11 -11
  58. package/src/components/TenantActionConfirmDialog.vue +19 -17
  59. package/src/components/VersionDiffPreview.vue +72 -43
  60. package/src/components/bundle-editor/BundleCreatePanel.vue +76 -63
  61. package/src/components/bundle-editor/BundleFeaturesEditor.vue +13 -6
  62. package/src/components/bundle-editor/BundlePlanCompatPicker.vue +19 -14
  63. package/src/components/bundle-editor/BundleQuotasEditor.vue +7 -4
  64. package/src/components/bundle-editor/BundleStatusBanner.vue +36 -19
  65. package/src/components/bundle-editor/BundleVersionInlineEditor.vue +51 -35
  66. package/src/components/bundle-editor/BundleVersionStrip.vue +24 -12
  67. package/src/components/bundle-editor/bundle-version-status.ts +29 -29
  68. package/src/components/dialogs/PilotCreateDialog.vue +58 -63
  69. package/src/components/dialogs/PilotEditDialog.vue +40 -34
  70. package/src/components/dialogs/PromoCodeCreateDialog.vue +89 -63
  71. package/src/components/dialogs/PromoCodeEditDialog.vue +98 -66
  72. package/src/components/dialogs/types.ts +3 -13
  73. package/src/components/plan/PlanCycleToggle.vue +21 -10
  74. package/src/components/plan/PlanGrid.vue +2 -2
  75. package/src/components/plan/PriceSummary.vue +1 -1
  76. package/src/components/plan/PromoCodeInput.vue +1 -1
  77. package/src/components/plan/PublicBundleGrid.vue +1 -1
  78. package/src/components/plan-cockpit/PlanCockpit.vue +34 -15
  79. package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +13 -6
  80. package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +18 -9
  81. package/src/components/plan-cockpit/PlanCockpitHeader.vue +9 -6
  82. package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +24 -11
  83. package/src/components/plan-cockpit/PlanCockpitKpis.vue +28 -17
  84. package/src/components/plan-cockpit/PlanCockpitVersions.vue +50 -25
  85. package/src/components/plan-create-dialog/PlanCreateDialog.vue +45 -35
  86. package/src/components/plan-detail/PlanAuditLog.vue +11 -6
  87. package/src/components/plan-detail/PlanDetail.vue +21 -23
  88. package/src/components/plan-detail/PlanDetailHeader.vue +20 -11
  89. package/src/components/plan-detail/PlanDetailKpis.vue +30 -17
  90. package/src/components/plan-detail/PlanTerminateDialog.vue +22 -9
  91. package/src/components/plan-detail/PlanVersionDiffPanel.vue +61 -27
  92. package/src/components/plan-detail/PlanVersionsPanel.vue +71 -36
  93. package/src/components/plan-list/PlanList.vue +99 -57
  94. package/src/components/plan-matrix/PlanMatrix.vue +85 -38
  95. package/src/components/plan-review/PlanReview.vue +105 -80
  96. package/src/components/plan-version-editor/PlanCatalogPreview.vue +43 -20
  97. package/src/components/plan-version-editor/PlanComponentPool.vue +15 -7
  98. package/src/components/plan-version-editor/PlanVersionBasket.vue +45 -24
  99. package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +8 -7
  100. package/src/components/plan-version-editor/PlanVersionEditor.vue +74 -40
  101. package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +24 -12
  102. package/src/index.ts +82 -79
  103. package/src/pages-standard/AdminLayout.vue +43 -18
  104. package/src/pages-standard/AdminManifestErrorPage.vue +10 -10
  105. package/src/pages-standard/AuditPage.vue +58 -19
  106. package/src/pages-standard/BundlesPage.vue +29 -18
  107. package/src/pages-standard/DashboardPage.vue +31 -17
  108. package/src/pages-standard/DiscoveryPage.vue +40 -28
  109. package/src/pages-standard/EmailHistoryPage.vue +98 -69
  110. package/src/pages-standard/MarketingCatalogPage.vue +40 -21
  111. package/src/pages-standard/PilotsPage.vue +64 -52
  112. package/src/pages-standard/PlanVersionsPage.vue +9 -5
  113. package/src/pages-standard/PlansPage.vue +91 -59
  114. package/src/pages-standard/PlatformEmailPage.vue +120 -53
  115. package/src/pages-standard/PromoCodeDetailPage.vue +66 -63
  116. package/src/pages-standard/PromoCodesPage.vue +51 -54
  117. package/src/pages-standard/SubscriptionsPage.vue +14 -9
  118. package/src/pages-standard/SuperAdminLoginPage.vue +19 -16
  119. package/src/pages-standard/SuperAdminSetupWizard.vue +53 -49
  120. package/src/pages-standard/TenantDetailPage.vue +54 -40
  121. package/src/pages-standard/TenantsPage.vue +57 -44
  122. package/src/pages-standard/UsersPage.vue +70 -52
  123. package/src/pages-standard/bundles-page/BundleAccordionList.vue +20 -8
  124. package/src/pages-standard/bundles-page/BundleDetailPanel.vue +28 -18
  125. package/src/pages-standard/bundles-page/BundlesFilterBar.vue +4 -1
  126. package/src/pages-standard/bundles-page/BundlesHeader.vue +9 -8
  127. package/src/pages-standard/bundles-page/BundlesKpis.vue +29 -11
  128. package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +20 -8
  129. package/src/pages-standard/discovery-page/DiscoveryCapList.vue +5 -3
  130. package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +43 -24
  131. package/src/pages-standard/discovery-page/DiscoveryHeader.vue +7 -4
  132. package/src/pages-standard/discovery-page/DiscoveryKpis.vue +28 -15
  133. package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +20 -10
  134. package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +9 -6
  135. package/src/pages-standard/discovery-page/discovery-ui.ts +40 -52
  136. package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +81 -49
  137. package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +13 -10
  138. package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +24 -20
  139. package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +6 -3
  140. package/src/pages-standard/plan-versions/PlanDiffCard.vue +16 -6
  141. package/src/pages-standard/plan-versions/PlanVersionHeader.vue +42 -26
  142. package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +46 -27
  143. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +8 -6
  144. package/src/pages-standard/plan-versions/PlanVersionsList.vue +40 -17
  145. package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +1 -1
  146. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +25 -13
  147. package/src/pages-standard/plan-versions/format.ts +33 -17
  148. package/src/pages-standard/plans-page/PlanArchiveDialog.vue +12 -8
  149. package/src/pages-standard/plans-page/PlanBundleOverview.vue +15 -6
  150. package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +18 -7
  151. package/src/pages-standard/plans-page/PlanPublishDialog.vue +23 -12
  152. package/src/pages-standard/tenants/format.ts +3 -2
  153. package/src/pages-tenant/MySubscriptionBundlesPage.vue +73 -38
  154. package/src/pages-tenant/OnboardingConfigurator.vue +3 -3
  155. package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
  156. package/src/pages-tenant/PlanChangeWizard.vue +10 -6
  157. package/src/pages-tenant/TenantPlanSection.vue +20 -16
  158. package/src/pages-tenant/default-i18n.ts +217 -1
  159. package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +9 -7
  160. package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +11 -12
  161. package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +4 -1
  162. package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +1 -1
  163. package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +1 -1
  164. package/src/quasar/create-super-admin-app.ts +192 -0
  165. package/src/quasar/index.ts +11 -0
  166. package/src/quasar/notify.ts +30 -0
  167. package/src/testing-e2e/admin-pages-suite.ts +14 -11
  168. package/src/{manifest-store-factory.ts → vue/manifest-store-factory.ts} +1 -1
  169. package/src/{platform-loaders.ts → vue/platform-loaders.ts} +4 -4
  170. package/src/{project-page-host.ts → vue/project-page-host.ts} +6 -4
  171. package/src/{create-super-admin-app.ts → vue/super-admin-context.ts} +20 -139
  172. package/src/vue/ui-notify.ts +30 -0
  173. package/src/{use-actions.ts → vue/use-actions.ts} +1 -1
  174. package/src/{use-api-list.ts → vue/use-api-list.ts} +1 -1
  175. package/src/{use-audit-entries.ts → vue/use-audit-entries.ts} +3 -3
  176. package/src/{use-batch-columns.ts → vue/use-batch-columns.ts} +1 -1
  177. package/src/{use-bulk-publish.ts → vue/use-bulk-publish.ts} +7 -5
  178. package/src/{use-bundle-versions-map.ts → vue/use-bundle-versions-map.ts} +7 -5
  179. package/src/{use-bundles.ts → vue/use-bundles.ts} +14 -14
  180. package/src/{use-catalog-entries.ts → vue/use-catalog-entries.ts} +14 -11
  181. package/src/{use-discovery.ts → vue/use-discovery.ts} +9 -6
  182. package/src/{use-entitlement.ts → vue/use-entitlement.ts} +4 -4
  183. package/src/{use-live-plan-versions.ts → vue/use-live-plan-versions.ts} +5 -5
  184. package/src/{use-manifest.ts → vue/use-manifest.ts} +1 -1
  185. package/src/{use-marketing-projections.ts → vue/use-marketing-projections.ts} +10 -7
  186. package/src/{use-nav.ts → vue/use-nav.ts} +9 -3
  187. package/src/{use-plan-editor.ts → vue/use-plan-editor.ts} +2 -2
  188. package/src/{use-plan-versions.ts → vue/use-plan-versions.ts} +3 -3
  189. package/src/{use-plans.ts → vue/use-plans.ts} +13 -13
  190. package/src/{use-platform-tenant-actions.ts → vue/use-platform-tenant-actions.ts} +9 -6
  191. package/src/{use-promotions.ts → vue/use-promotions.ts} +10 -11
  192. package/src/{use-public-boot.ts → vue/use-public-boot.ts} +1 -1
  193. package/src/{use-subscription-draft.ts → vue/use-subscription-draft.ts} +2 -3
  194. package/src/{use-super-admin-context.ts → vue/use-super-admin-context.ts} +5 -5
  195. package/src/vue/use-super-admin-i18n.ts +82 -0
  196. package/src/{use-tenant-action-flow.ts → vue/use-tenant-action-flow.ts} +12 -7
  197. package/src/{use-tenant-billing-catalog.ts → vue/use-tenant-billing-catalog.ts} +2 -2
  198. package/src/{use-tenant-billing.ts → vue/use-tenant-billing.ts} +2 -2
  199. package/src/{use-tenant-manifest.ts → vue/use-tenant-manifest.ts} +2 -4
  200. package/src/{use-tenant-subscription-bundles.ts → vue/use-tenant-subscription-bundles.ts} +5 -5
  201. package/src/{use-tenants.ts → vue/use-tenants.ts} +3 -3
  202. package/dist/testing-e2e/admin-pages-suite.d.cts +0 -37
  203. package/dist/testing-e2e/admin-pages-suite.d.ts +0 -37
  204. package/src/components/BusinessTypeVersionEditorDialog.vue +0 -417
  205. package/src/components/BusinessTypeVersionPublishDialog.vue +0 -258
  206. package/src/pages-standard/BusinessTypesPage.vue +0 -662
  207. package/src/pages-standard/plan-versions/VersionDiffPreview.vue +0 -167
  208. package/src/use-business-types.ts +0 -269
  209. /package/src/{http-json.ts → client/http-json.ts} +0 -0
  210. /package/src/{types.ts → client/types.ts} +0 -0
  211. /package/src/{version.ts → client/version.ts} +0 -0
  212. /package/src/{create-admin-routes.ts → vue/create-admin-routes.ts} +0 -0
  213. /package/src/{entitlement-provider.ts → vue/entitlement-provider.ts} +0 -0
  214. /package/src/{feature-router-guard.ts → vue/feature-router-guard.ts} +0 -0
@@ -0,0 +1,210 @@
1
+ import { Component, App, InjectionKey, Ref, ComputedRef } from 'vue';
2
+ import { NavigationGuardWithThis } from 'vue-router';
3
+ import { ActionKey, ComponentKey, AdminManifest } from '@saasicat/types';
4
+ import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaMessagesOverrides } from './messages-7b0P8W75.cjs';
5
+
6
+ /**
7
+ * App-specific branding data that the platform `AdminLayout` and other
8
+ * consumers read via `useSuperAdminBrand()`.
9
+ */
10
+ interface SuperAdminBrand {
11
+ /** 2-letter abbreviation in the logo badge (`'ma'`, `'da'`, …). */
12
+ logoText: string;
13
+ /** Full display name (`'DemoApp'`, `'ClubApp'`, …). */
14
+ name: string;
15
+ /** Optional: tag to the right of the name, default `'SuperAdmin'`. */
16
+ tag?: string;
17
+ }
18
+ /**
19
+ * Endpoint configuration. `apiBase` is the shared prefix under which
20
+ * `/manifest`, `/boot` and extra routes live.
21
+ */
22
+ interface SuperAdminEndpoints {
23
+ /** Shared prefix, e.g. `'/api/admin'` or `'/api/v1/admin'`. */
24
+ apiBase: string;
25
+ /** Pre-login branding endpoint, default `${apiBase}/boot`. */
26
+ publicBootEndpoint?: string;
27
+ /** Post-login full-manifest endpoint, default `${apiBase}/manifest`. */
28
+ manifestEndpoint?: string;
29
+ }
30
+ type ExtensionLoader = () => Promise<Component | {
31
+ default: Component;
32
+ }>;
33
+ type ExtensionsMap = Record<ComponentKey, ExtensionLoader>;
34
+ type ActionsMap = Record<ActionKey, ActionHandler>;
35
+ interface SuperAdminAuthGuardOptions {
36
+ /** App provides: is the user currently logged in? */
37
+ isAuthenticated: () => boolean;
38
+ /** App provides: does the user have the SuperAdmin role? Default: only check `isAuthenticated`. */
39
+ isSuperAdmin?: () => boolean;
40
+ /** App provides: redirect path for unauthenticated calls (e.g. `'/login'`). */
41
+ onUnauthenticated: () => string;
42
+ }
43
+ /**
44
+ * Result of a login attempt. Apps pass this back from their auth store to the
45
+ * platform LoginPage; the page renders an appropriate error message.
46
+ *
47
+ * `ok: true` → login succeeded, page redirects to `redirectAfterLogin`.
48
+ * `ok: false` → page shows `message` or a translation derived from `code`.
49
+ *
50
+ * Known codes:
51
+ * - `BAD_CREDENTIALS` — wrong email/password combination.
52
+ * - `NOT_SUPER_ADMIN` — account does not have the SuperAdmin role.
53
+ * - otherwise — app-specific; `message` is displayed directly.
54
+ */
55
+ type SuperAdminLoginResult = {
56
+ ok: true;
57
+ } | {
58
+ ok: false;
59
+ code?: 'BAD_CREDENTIALS' | 'NOT_SUPER_ADMIN' | string;
60
+ message?: string;
61
+ };
62
+ /**
63
+ * Login adapter. The app passes its auth-store call through here. The platform
64
+ * LoginPage consumes it via `useSuperAdminLoginAdapter()`, without knowledge
65
+ * of app-specific stores (Pinia, auth API routes, MFA hooks).
66
+ */
67
+ interface SuperAdminLoginAdapter {
68
+ /**
69
+ * Performs the login. The app store encapsulates the API call, token
70
+ * storage, MFA hops etc.
71
+ */
72
+ login(email: string, password: string): Promise<SuperAdminLoginResult>;
73
+ /**
74
+ * Target route after a successful login. Default: `/admin/dashboard`
75
+ * (platform convention for the standard pages — apps with a different
76
+ * default mount override this here).
77
+ */
78
+ redirectAfterLogin?: string;
79
+ /**
80
+ * Optional: dev hint (test account), shown below the form. Deliberately
81
+ * rendered only when `environment !== 'production'`.
82
+ */
83
+ devHint?: {
84
+ email: string;
85
+ password: string;
86
+ };
87
+ }
88
+ interface SuperAdminManifestGuardOptions {
89
+ /**
90
+ * App provides: loads the manifest into the app store. The router guard
91
+ * `await`s the promise before the route is resolved.
92
+ *
93
+ * **On loader error:** the promise REJECTS. The router guard catches the
94
+ * rejection and decides depending on `errorRoute`:
95
+ * - `errorRoute` set → redirect to this route (fail-closed).
96
+ * - `errorRoute` not set → `console.error` + render allowed
97
+ * (defensive default behavior; the app must render the manifest gap
98
+ * itself).
99
+ */
100
+ ensureLoaded: () => Promise<void>;
101
+ /**
102
+ * Optional: read accessor on the loaded manifest. When set, it is exposed
103
+ * via `provide(SUPER_ADMIN_MANIFEST_KEY)` — the `<ProjectPageHost>`
104
+ * resolves manifest `projectPages` through it against the
105
+ * `extensions:` map.
106
+ */
107
+ getManifest?: () => AdminManifest | null;
108
+ /**
109
+ * Optional: path that the router guard redirects to on a manifest load
110
+ * error (fail-closed mode). The app must register the route in `appRoutes`
111
+ * and mark it as `meta.public = true`, otherwise it runs through the
112
+ * manifest guard again and produces a redirect loop.
113
+ */
114
+ errorRoute?: string;
115
+ }
116
+ /**
117
+ * Guard configuration shared by `buildNavigationGuard()` and
118
+ * `createSuperAdminApp()` (which embeds both fields in its options).
119
+ */
120
+ interface SuperAdminGuardOptions {
121
+ /**
122
+ * Optional: auth guard. When set, `router.beforeEach` is wired up
123
+ * automatically — `to.meta.public === true` bypasses the guard.
124
+ */
125
+ authGuard?: SuperAdminAuthGuardOptions;
126
+ /**
127
+ * Optional: manifest guard. Runs after a successful auth guard, blocks the
128
+ * render until the manifest is loaded (prevents sidebar flicker).
129
+ */
130
+ manifestGuard?: SuperAdminManifestGuardOptions;
131
+ }
132
+ /** Vue inject key for `useSuperAdminBrand()`. */
133
+ declare const SUPER_ADMIN_BRAND_KEY: InjectionKey<SuperAdminBrand>;
134
+ /** Vue inject key for `useSuperAdminEndpoints()`. */
135
+ declare const SUPER_ADMIN_ENDPOINTS_KEY: InjectionKey<Required<SuperAdminEndpoints>>;
136
+ /** Vue inject key for `useSuperAdminExtensions()`. */
137
+ declare const SUPER_ADMIN_EXTENSIONS_KEY: InjectionKey<ExtensionsMap>;
138
+ /** Vue inject key for `useSuperAdminActions()`. */
139
+ declare const SUPER_ADMIN_ACTIONS_KEY: InjectionKey<ActionsMap>;
140
+ /**
141
+ * Vue inject key for the manifest accessor. Only provided when
142
+ * `manifestGuard.getManifest` was passed to `createSuperAdminApp()` — the
143
+ * `<ProjectPageHost>` needs it to resolve project pages.
144
+ */
145
+ declare const SUPER_ADMIN_MANIFEST_KEY: InjectionKey<() => AdminManifest | null>;
146
+ /** Vue inject key for `useSuperAdminLoginAdapter()`. */
147
+ declare const SUPER_ADMIN_LOGIN_ADAPTER_KEY: InjectionKey<SuperAdminLoginAdapter>;
148
+ /** Vue inject key for `useSuperAdminHttp()` (pre-login HttpClient). */
149
+ declare const SUPER_ADMIN_HTTP_KEY: InjectionKey<HttpClient>;
150
+ /**
151
+ * Additional Vue plugins (e.g. an app's own NotificationCenter) that a
152
+ * bootstrap installs after the platform setup, before the mount.
153
+ */
154
+ type InstallPlugin = (app: App) => void;
155
+ /**
156
+ * Internal helper, exported for isolated unit tests of the navigation
157
+ * behavior (auth redirect, manifest fail-closed path). Consumers should call
158
+ * `createSuperAdminApp()`, not this helper directly.
159
+ */
160
+ declare function buildNavigationGuard(options: SuperAdminGuardOptions): NavigationGuardWithThis<undefined> | null;
161
+
162
+ /**
163
+ * Feedback kind, aligned with the established page conventions
164
+ * (`positive` = success, `negative` = error).
165
+ */
166
+ type UiNotifyKind = 'positive' | 'negative' | 'warning' | 'info';
167
+ interface UiNotifyOptions {
168
+ /** Secondary line below the message (e.g. changed fields, one-time password). */
169
+ caption?: string;
170
+ /** Display duration in ms; implementations fall back to their default when unset. */
171
+ timeoutMs?: number;
172
+ }
173
+ /** Notify port. Compatible with the `(kind, message)` callbacks pages already accept. */
174
+ type UiNotify = (kind: UiNotifyKind, message: string, options?: UiNotifyOptions) => void;
175
+ /** Vue inject key for the notify port (see `Symbol.for` note in super-admin-context.ts). */
176
+ declare const SUPER_ADMIN_NOTIFY_KEY: InjectionKey<UiNotify>;
177
+
178
+ interface SuperAdminI18n {
179
+ /** Active UI locale — mutable; switching re-renders all catalog texts. */
180
+ locale: Ref<SaLocale>;
181
+ /** Resolved catalog for the active locale (platform + app overrides). */
182
+ messages: ComputedRef<SaMessages>;
183
+ /** BCP-47 tag for `Intl`/`toLocaleString` formatting in the active locale. */
184
+ intlLocale: ComputedRef<string>;
185
+ }
186
+ interface SuperAdminI18nOptions {
187
+ /**
188
+ * Initial UI locale, default `'de'`. Pass a `Ref` when the app wants to
189
+ * keep control (e.g. a user-profile setting or a header switcher).
190
+ */
191
+ locale?: SaLocale | Ref<SaLocale>;
192
+ /** Per-locale string overrides layered over the platform catalog. */
193
+ overrides?: Partial<Record<SaLocale, SaMessagesOverrides>>;
194
+ }
195
+ declare const SUPER_ADMIN_I18N_KEY: InjectionKey<SuperAdminI18n>;
196
+ declare function createSuperAdminI18n(options?: SuperAdminI18nOptions): SuperAdminI18n;
197
+ /**
198
+ * Returns the i18n context provided by `createSuperAdminApp()`. Outside a
199
+ * shell (isolated mounts, tests) a shared German default instance is returned
200
+ * so callers never have to guard for a missing context.
201
+ */
202
+ declare function useSuperAdminI18n(): SuperAdminI18n;
203
+ /**
204
+ * Focused accessor for one catalog namespace:
205
+ * `const msg = useSaMessages('plans')` → `msg.value.title` /
206
+ * template `msg.title`.
207
+ */
208
+ declare function useSaMessages<K extends keyof SaMessages>(namespace: K): ComputedRef<SaMessages[K]>;
209
+
210
+ export { type ActionsMap as A, type ExtensionsMap as E, type InstallPlugin as I, type SuperAdminGuardOptions as S, type UiNotify as U, type SuperAdminBrand as a, type SuperAdminEndpoints as b, type SuperAdminLoginAdapter as c, type SuperAdminI18nOptions as d, type SuperAdminI18n as e, type ExtensionLoader as f, SUPER_ADMIN_ACTIONS_KEY as g, SUPER_ADMIN_BRAND_KEY as h, SUPER_ADMIN_ENDPOINTS_KEY as i, SUPER_ADMIN_EXTENSIONS_KEY as j, SUPER_ADMIN_HTTP_KEY as k, SUPER_ADMIN_I18N_KEY as l, SUPER_ADMIN_LOGIN_ADAPTER_KEY as m, SUPER_ADMIN_MANIFEST_KEY as n, SUPER_ADMIN_NOTIFY_KEY as o, type SuperAdminAuthGuardOptions as p, type SuperAdminLoginResult as q, type SuperAdminManifestGuardOptions as r, type UiNotifyKind as s, type UiNotifyOptions as t, buildNavigationGuard as u, createSuperAdminI18n as v, useSaMessages as w, useSuperAdminI18n as x };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasicat/ui-vue",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Vue 3 components + composables for the SuperAdmin UI shell. Provides boot loader, manifest loader (ETag cache), nav builder, action registry and standard pages.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -17,6 +17,26 @@
17
17
  "default": "./dist/index.cjs"
18
18
  }
19
19
  },
20
+ "./client": {
21
+ "import": {
22
+ "types": "./dist/client/index.d.ts",
23
+ "default": "./dist/client/index.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/client/index.d.cts",
27
+ "default": "./dist/client/index.cjs"
28
+ }
29
+ },
30
+ "./quasar": {
31
+ "import": {
32
+ "types": "./dist/quasar/index.d.ts",
33
+ "default": "./dist/quasar/index.js"
34
+ },
35
+ "require": {
36
+ "types": "./dist/quasar/index.d.cts",
37
+ "default": "./dist/quasar/index.cjs"
38
+ }
39
+ },
20
40
  "./pages/*": "./src/pages-standard/*",
21
41
  "./pages-standard/*": "./src/pages-standard/*",
22
42
  "./pages-tenant/*": "./src/pages-tenant/*",
@@ -33,7 +53,7 @@
33
53
  "src"
34
54
  ],
35
55
  "dependencies": {
36
- "@saasicat/types": "^0.4.0"
56
+ "@saasicat/types": "^0.6.0"
37
57
  },
38
58
  "peerDependencies": {
39
59
  "pinia": "^2.0.0 || ^3.0.0",
@@ -78,7 +98,7 @@
78
98
  "access": "public"
79
99
  },
80
100
  "scripts": {
81
- "build": "tsup src/index.ts --format esm,cjs --dts --clean --external @saasicat/types --external vue --external vue-router --external pinia --external quasar && tsup src/testing-e2e/admin-pages-suite.ts --format esm,cjs --dts --out-dir dist/testing-e2e --external @playwright/test",
101
+ "build": "tsup",
82
102
  "typecheck": "vue-tsc --noEmit -p tsconfig.json",
83
103
  "pretest": "pnpm run build",
84
104
  "test": "node --test tests/**/*.test.js",
@@ -27,8 +27,8 @@ export interface ResolvedAction<TInput = unknown, TResult = unknown> {
27
27
  export class MissingHandlerError extends Error {
28
28
  constructor(actionKey: string) {
29
29
  super(
30
- `Kein Handler für actionKey "${actionKey}" registriert. ` +
31
- `Konsument-Shell muss \`actions["${actionKey}"]\` bereitstellen.`,
30
+ `No handler registered for actionKey "${actionKey}". ` +
31
+ `The consumer shell must provide \`actions["${actionKey}"]\`.`,
32
32
  );
33
33
  this.name = 'MissingHandlerError';
34
34
  }
@@ -37,8 +37,8 @@ export class MissingHandlerError extends Error {
37
37
  export class ActionDefNotInManifestError extends Error {
38
38
  constructor(actionKey: string) {
39
39
  super(
40
- `actionKey "${actionKey}" ist im Manifest nicht deklariert. ` +
41
- `Bitte als TenantAction in einer ManifestContribution registrieren.`,
40
+ `actionKey "${actionKey}" is not declared in the manifest. ` +
41
+ `Register it as a TenantAction in a ManifestContribution.`,
42
42
  );
43
43
  this.name = 'ActionDefNotInManifestError';
44
44
  }
@@ -128,14 +128,14 @@ export class BatchColumnFetcher {
128
128
 
129
129
  private validateBatchEndpoint(col: TenantColumnDef): void {
130
130
  if (!col.endpoint || col.endpoint.trim().length === 0) {
131
- throw new BatchColumnDriftError(col, 'endpoint ist leer');
131
+ throw new BatchColumnDriftError(col, 'endpoint is empty');
132
132
  }
133
133
  if (col.endpoint.includes('{slug}') || col.endpoint.includes('{tenantId}')) {
134
134
  throw new BatchColumnDriftError(
135
135
  col,
136
- 'endpoint enthält per-Tenant-Placeholder ({slug}/{tenantId}). ' +
137
- 'Spalten-Endpoints müssen batchfähig seinbitte Backend ' +
138
- 'auf `?tenantIds=...`-Parameter umstellen.',
136
+ 'endpoint contains per-tenant placeholders ({slug}/{tenantId}). ' +
137
+ 'Column endpoints must be batchable switch the backend ' +
138
+ 'to a `?tenantIds=...` parameter.',
139
139
  );
140
140
  }
141
141
  }
@@ -41,9 +41,9 @@ export class BootLoader {
41
41
  constructor(options: BootLoaderOptions) {
42
42
  if (!options?.endpoint) {
43
43
  throw new Error(
44
- 'BootLoader: `endpoint` ist Pflicht (z. B. "/api/admin/boot" ' +
45
- 'oder "/api/v1/admin/boot"). Plattform hat keinen Default, ' +
46
- 'weil Apps unterschiedliche globalPrefix-Konventionen haben.',
44
+ 'BootLoader: `endpoint` is required (e.g. "/api/admin/boot" ' +
45
+ 'or "/api/v1/admin/boot"). The platform has no default ' +
46
+ 'because apps use different globalPrefix conventions.',
47
47
  );
48
48
  }
49
49
  this.endpoint = options.endpoint;
@@ -0,0 +1,42 @@
1
+ // Currency formatting for the admin UI. Amounts are net euro values; the
2
+ // grouping and decimal separator follow the active UI locale
3
+ // (de → `12.345,60 €`, en → `€12,345.60`).
4
+
5
+ import { DEFAULT_SA_LOCALE, SA_INTL_LOCALES, type SaLocale } from './locale.js';
6
+
7
+ const DEFAULT_CURRENCY = 'EUR';
8
+
9
+ const formatters = new Map<string, Intl.NumberFormat>();
10
+
11
+ function formatterFor(locale: SaLocale, currency: string, decimals: number): Intl.NumberFormat {
12
+ const key = `${locale}:${currency}:${decimals}`;
13
+ let formatter = formatters.get(key);
14
+ if (!formatter) {
15
+ formatter = new Intl.NumberFormat(SA_INTL_LOCALES[locale], {
16
+ style: 'currency',
17
+ currency,
18
+ minimumFractionDigits: decimals,
19
+ maximumFractionDigits: decimals,
20
+ });
21
+ formatters.set(key, formatter);
22
+ }
23
+ return formatter;
24
+ }
25
+
26
+ /**
27
+ * Formats a net amount for display. Whole amounts drop the decimals (`29 €`
28
+ * rather than `29,00 €`) — the plan and bundle prices the admin UI shows are
29
+ * mostly round numbers, and the zeros are noise in dense tables.
30
+ *
31
+ * Returns an em dash for null/undefined and for values that are not finite
32
+ * numbers, so callers can pass raw API fields straight through.
33
+ */
34
+ export function formatCurrency(
35
+ amount: number | string | null | undefined,
36
+ locale: SaLocale = DEFAULT_SA_LOCALE,
37
+ currency: string = DEFAULT_CURRENCY,
38
+ ): string {
39
+ const value = typeof amount === 'string' ? Number(amount) : amount;
40
+ if (value === null || value === undefined || !Number.isFinite(value)) return '—';
41
+ return formatterFor(locale, currency, Number.isInteger(value) ? 0 : 2).format(value);
42
+ }
@@ -0,0 +1,59 @@
1
+ // Typed message-catalog helpers. Every namespace is defined once in German —
2
+ // the reference locale that fixes the key structure — and the English variant
3
+ // must mirror that structure exactly, enforced at compile time via
4
+ // `TranslationOf`.
5
+
6
+ import type { SaLocale } from './locale.js';
7
+
8
+ /** Nested string map — the shape of every message namespace. */
9
+ export interface MessageTree {
10
+ readonly [key: string]: string | MessageTree;
11
+ }
12
+
13
+ /** Maps a reference namespace to "same keys, any string values". */
14
+ export type TranslationOf<T> = {
15
+ readonly [K in keyof T]: T[K] extends string ? string : TranslationOf<T[K]>;
16
+ };
17
+
18
+ /** Deep partial of a namespace — the shape consumers pass as overrides. */
19
+ export type PartialMessages<T> = {
20
+ readonly [K in keyof T]?: T[K] extends string ? string : PartialMessages<T[K]>;
21
+ };
22
+
23
+ /**
24
+ * Couples the German reference catalog with its English translation. Missing
25
+ * or extra keys in the English object are compile errors.
26
+ */
27
+ export function defineMessages<T extends MessageTree>(
28
+ de: T,
29
+ en: TranslationOf<T>,
30
+ ): Record<SaLocale, T> {
31
+ // `TranslationOf<T>` and `T` are structurally identical for string-valued
32
+ // trees; the cast only erases the mapped-type wrapper.
33
+ return { de, en: en as T };
34
+ }
35
+
36
+ /**
37
+ * Recursively overlays `overrides` onto `base` without mutating either side.
38
+ * Only string leaves are replaced; keys not present in `base` are ignored —
39
+ * the catalog structure is fixed by the platform.
40
+ */
41
+ export function mergeMessages<T extends MessageTree>(
42
+ base: T,
43
+ overrides: PartialMessages<T> | undefined,
44
+ ): T {
45
+ if (!overrides) return base;
46
+ const result: Record<string, string | MessageTree> = {};
47
+ for (const [key, baseValue] of Object.entries(base)) {
48
+ const override = (overrides as Record<string, unknown>)[key];
49
+ if (typeof baseValue === 'string') {
50
+ result[key] = typeof override === 'string' ? override : baseValue;
51
+ } else {
52
+ result[key] = mergeMessages(
53
+ baseValue,
54
+ override as PartialMessages<MessageTree> | undefined,
55
+ );
56
+ }
57
+ }
58
+ return result as T;
59
+ }
@@ -0,0 +1,12 @@
1
+ // Placeholder interpolation for catalog messages: `{name}` is replaced with
2
+ // `params.name`. Unknown placeholders stay verbatim so a missing param is
3
+ // visible in the UI instead of silently disappearing.
4
+
5
+ export type MessageParams = Record<string, string | number>;
6
+
7
+ export function formatMessage(template: string, params: MessageParams): string {
8
+ return template.replace(/\{(\w+)\}/g, (match, name: string) => {
9
+ const value = params[name];
10
+ return value === undefined ? match : String(value);
11
+ });
12
+ }
@@ -0,0 +1,14 @@
1
+ // Framework-free i18n core: locale registry, typed catalogs, interpolation.
2
+ // The reactive Vue binding lives in `src/vue/use-super-admin-i18n.ts`.
3
+
4
+ export * from './locale.js';
5
+ export {
6
+ defineMessages,
7
+ mergeMessages,
8
+ type MessageTree,
9
+ type PartialMessages,
10
+ type TranslationOf,
11
+ } from './define.js';
12
+ export * from './format.js';
13
+ export * from './currency.js';
14
+ export * from './messages.js';
@@ -0,0 +1,15 @@
1
+ // Supported display locales of the SuperAdmin UI. The platform ships a German
2
+ // (reference) and an English catalog; consumers pick the locale per app or per
3
+ // user via `createSuperAdminApp({ i18n })`.
4
+
5
+ export type SaLocale = 'de' | 'en';
6
+
7
+ export const SA_LOCALES: readonly SaLocale[] = ['de', 'en'];
8
+
9
+ export const DEFAULT_SA_LOCALE: SaLocale = 'de';
10
+
11
+ /** BCP-47 tags used for `Intl`/`toLocaleString` formatting per UI locale. */
12
+ export const SA_INTL_LOCALES: Record<SaLocale, string> = {
13
+ de: 'de-DE',
14
+ en: 'en-US',
15
+ };
@@ -0,0 +1,42 @@
1
+ import { defineMessages } from '../define.js';
2
+
3
+ export const auditMessages = defineMessages(
4
+ {
5
+ title: 'Audit-Trail',
6
+ subtitle: '{count} Einträge · plattformweit.',
7
+ filterButton: 'Filtern',
8
+ detailActorPrefix: 'Actor:',
9
+ filters: {
10
+ actor: 'Actor (E-Mail)',
11
+ action: 'Action',
12
+ entity: 'Entity',
13
+ since: 'Seit',
14
+ },
15
+ columns: {
16
+ time: 'Zeit',
17
+ actor: 'Actor',
18
+ action: 'Action',
19
+ entity: 'Entity',
20
+ id: 'ID',
21
+ },
22
+ },
23
+ {
24
+ title: 'Audit trail',
25
+ subtitle: '{count} entries · platform-wide.',
26
+ filterButton: 'Filter',
27
+ detailActorPrefix: 'Actor:',
28
+ filters: {
29
+ actor: 'Actor (email)',
30
+ action: 'Action',
31
+ entity: 'Entity',
32
+ since: 'Since',
33
+ },
34
+ columns: {
35
+ time: 'Time',
36
+ actor: 'Actor',
37
+ action: 'Action',
38
+ entity: 'Entity',
39
+ id: 'ID',
40
+ },
41
+ },
42
+ );