@saasicat/ui-vue 0.2.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 (193) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +47 -0
  3. package/dist/index.cjs +3776 -0
  4. package/dist/index.d.cts +2395 -0
  5. package/dist/index.d.ts +2395 -0
  6. package/dist/index.js +3659 -0
  7. package/dist/testing-e2e/admin-pages-suite.cjs +134 -0
  8. package/dist/testing-e2e/admin-pages-suite.d.cts +37 -0
  9. package/dist/testing-e2e/admin-pages-suite.d.ts +37 -0
  10. package/dist/testing-e2e/admin-pages-suite.js +109 -0
  11. package/package.json +87 -0
  12. package/src/action-registry.ts +110 -0
  13. package/src/batch-column-fetcher.ts +152 -0
  14. package/src/boot-loader.ts +60 -0
  15. package/src/components/BundleVersionPublishDialog.vue +359 -0
  16. package/src/components/BusinessTypeVersionEditorDialog.vue +417 -0
  17. package/src/components/BusinessTypeVersionPublishDialog.vue +258 -0
  18. package/src/components/FeatureGate.vue +56 -0
  19. package/src/components/KpiCard.vue +53 -0
  20. package/src/components/KvBlock.vue +36 -0
  21. package/src/components/MarketingPromotionsTab.vue +861 -0
  22. package/src/components/MfaPromptDialog.vue +104 -0
  23. package/src/components/TenantActionConfirmDialog.vue +252 -0
  24. package/src/components/VersionDiffPreview.vue +150 -0
  25. package/src/components/bundle-editor/BundleCreatePanel.vue +681 -0
  26. package/src/components/bundle-editor/BundleFeaturesEditor.vue +215 -0
  27. package/src/components/bundle-editor/BundlePlanCompatPicker.vue +316 -0
  28. package/src/components/bundle-editor/BundleQuotasEditor.vue +219 -0
  29. package/src/components/bundle-editor/BundleStatusBanner.vue +189 -0
  30. package/src/components/bundle-editor/BundleVersionInlineEditor.vue +659 -0
  31. package/src/components/bundle-editor/BundleVersionStrip.vue +207 -0
  32. package/src/components/bundle-editor/bundle-version-status.ts +164 -0
  33. package/src/components/bundle-editor/catalog-i18n.ts +85 -0
  34. package/src/components/dialogs/PilotCreateDialog.vue +780 -0
  35. package/src/components/dialogs/PilotEditDialog.vue +548 -0
  36. package/src/components/dialogs/PromoCodeCreateDialog.vue +737 -0
  37. package/src/components/dialogs/PromoCodeEditDialog.vue +859 -0
  38. package/src/components/dialogs/types.ts +126 -0
  39. package/src/components/plan/PlanCycleToggle.vue +99 -0
  40. package/src/components/plan/PlanGrid.vue +211 -0
  41. package/src/components/plan/PriceSummary.vue +293 -0
  42. package/src/components/plan/PromoCodeInput.vue +192 -0
  43. package/src/components/plan/PublicBundleGrid.vue +287 -0
  44. package/src/components/plan-cockpit/PlanCockpit.vue +993 -0
  45. package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +65 -0
  46. package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +91 -0
  47. package/src/components/plan-cockpit/PlanCockpitHeader.vue +98 -0
  48. package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +99 -0
  49. package/src/components/plan-cockpit/PlanCockpitKpis.vue +57 -0
  50. package/src/components/plan-cockpit/PlanCockpitVersions.vue +226 -0
  51. package/src/components/plan-cockpit/types.ts +72 -0
  52. package/src/components/plan-create-dialog/PlanCreateDialog.vue +508 -0
  53. package/src/components/plan-detail/PlanAuditLog.vue +59 -0
  54. package/src/components/plan-detail/PlanDetail.vue +1195 -0
  55. package/src/components/plan-detail/PlanDetailHeader.vue +185 -0
  56. package/src/components/plan-detail/PlanDetailKpis.vue +57 -0
  57. package/src/components/plan-detail/PlanTerminateDialog.vue +76 -0
  58. package/src/components/plan-detail/PlanVersionDiffPanel.vue +149 -0
  59. package/src/components/plan-detail/PlanVersionsPanel.vue +255 -0
  60. package/src/components/plan-detail/types.ts +59 -0
  61. package/src/components/plan-list/PlanList.vue +1067 -0
  62. package/src/components/plan-matrix/PlanMatrix.vue +1113 -0
  63. package/src/components/plan-review/PlanReview.vue +742 -0
  64. package/src/components/plan-version-editor/PlanCatalogPreview.vue +184 -0
  65. package/src/components/plan-version-editor/PlanComponentPool.vue +272 -0
  66. package/src/components/plan-version-editor/PlanVersionBasket.vue +281 -0
  67. package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +84 -0
  68. package/src/components/plan-version-editor/PlanVersionEditor.vue +1728 -0
  69. package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +94 -0
  70. package/src/components/plan-version-editor/types.ts +96 -0
  71. package/src/components/wizard-stepper/WizardStepper.vue +107 -0
  72. package/src/create-admin-routes.ts +68 -0
  73. package/src/create-super-admin-app.ts +345 -0
  74. package/src/entitlement-provider.ts +36 -0
  75. package/src/feature-router-guard.ts +77 -0
  76. package/src/http-json.ts +62 -0
  77. package/src/index.ts +98 -0
  78. package/src/manifest-loader.ts +139 -0
  79. package/src/manifest-store-factory.ts +114 -0
  80. package/src/nav-builder.ts +274 -0
  81. package/src/pages-standard/AdminLayout.vue +411 -0
  82. package/src/pages-standard/AdminManifestErrorPage.vue +79 -0
  83. package/src/pages-standard/AuditPage.vue +223 -0
  84. package/src/pages-standard/BundlesPage.vue +910 -0
  85. package/src/pages-standard/BusinessTypesPage.vue +662 -0
  86. package/src/pages-standard/DashboardPage.vue +595 -0
  87. package/src/pages-standard/DiscoveryPage.vue +663 -0
  88. package/src/pages-standard/EmailHistoryPage.vue +576 -0
  89. package/src/pages-standard/MarketingCatalogPage.vue +1771 -0
  90. package/src/pages-standard/PilotsPage.vue +577 -0
  91. package/src/pages-standard/PlanVersionsPage.vue +216 -0
  92. package/src/pages-standard/PlansPage.vue +1222 -0
  93. package/src/pages-standard/PlatformEmailPage.vue +434 -0
  94. package/src/pages-standard/PromoCodeDetailPage.vue +331 -0
  95. package/src/pages-standard/PromoCodesPage.vue +589 -0
  96. package/src/pages-standard/SubscriptionsPage.vue +159 -0
  97. package/src/pages-standard/SuperAdminLoginPage.vue +299 -0
  98. package/src/pages-standard/SuperAdminSetupWizard.vue +405 -0
  99. package/src/pages-standard/TenantDetailPage.vue +432 -0
  100. package/src/pages-standard/TenantsPage.vue +732 -0
  101. package/src/pages-standard/UsersPage.vue +504 -0
  102. package/src/pages-standard/bundles-page/BundleAccordionList.vue +116 -0
  103. package/src/pages-standard/bundles-page/BundleDetailPanel.vue +193 -0
  104. package/src/pages-standard/bundles-page/BundlesFilterBar.vue +48 -0
  105. package/src/pages-standard/bundles-page/BundlesHeader.vue +67 -0
  106. package/src/pages-standard/bundles-page/BundlesKpis.vue +39 -0
  107. package/src/pages-standard/bundles-page/types.ts +15 -0
  108. package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +246 -0
  109. package/src/pages-standard/discovery-page/DiscoveryCapList.vue +171 -0
  110. package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +410 -0
  111. package/src/pages-standard/discovery-page/DiscoveryHeader.vue +31 -0
  112. package/src/pages-standard/discovery-page/DiscoveryKpis.vue +47 -0
  113. package/src/pages-standard/discovery-page/DiscoveryMetaBanner.vue +26 -0
  114. package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +247 -0
  115. package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +73 -0
  116. package/src/pages-standard/discovery-page/discovery-ui.ts +166 -0
  117. package/src/pages-standard/email-history.types.ts +48 -0
  118. package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +448 -0
  119. package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +93 -0
  120. package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +138 -0
  121. package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +58 -0
  122. package/src/pages-standard/marketing-catalog/types.ts +43 -0
  123. package/src/pages-standard/plan-versions/PlanDiffCard.vue +132 -0
  124. package/src/pages-standard/plan-versions/PlanVersionHeader.vue +293 -0
  125. package/src/pages-standard/plan-versions/PlanVersionStatusBadge.vue +48 -0
  126. package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +307 -0
  127. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +269 -0
  128. package/src/pages-standard/plan-versions/PlanVersionsKpi.vue +65 -0
  129. package/src/pages-standard/plan-versions/PlanVersionsList.vue +292 -0
  130. package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +261 -0
  131. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +300 -0
  132. package/src/pages-standard/plan-versions/VersionDiffPreview.vue +167 -0
  133. package/src/pages-standard/plan-versions/format.ts +53 -0
  134. package/src/pages-standard/plan-versions/types.ts +33 -0
  135. package/src/pages-standard/plans-page/PlanArchiveDialog.vue +50 -0
  136. package/src/pages-standard/plans-page/PlanBundleOverview.vue +147 -0
  137. package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +46 -0
  138. package/src/pages-standard/plans-page/PlanPublishDialog.vue +106 -0
  139. package/src/pages-standard/plans-page/PlansPageToast.vue +69 -0
  140. package/src/pages-standard/plans-page/types.ts +18 -0
  141. package/src/pages-standard/platform-email.types.ts +41 -0
  142. package/src/pages-standard/sa-theme.css +223 -0
  143. package/src/pages-standard/tenants/StatusPill.vue +62 -0
  144. package/src/pages-standard/tenants/format.ts +38 -0
  145. package/src/pages-tenant/LimitsRow.vue +67 -0
  146. package/src/pages-tenant/MySubscriptionBundlesPage.vue +604 -0
  147. package/src/pages-tenant/OnboardingConfigurator.vue +417 -0
  148. package/src/pages-tenant/PackageSnapshotPanel.vue +241 -0
  149. package/src/pages-tenant/PendingVersionBanner.vue +126 -0
  150. package/src/pages-tenant/PlanChangeWizard.vue +630 -0
  151. package/src/pages-tenant/TenantPlanSection.vue +729 -0
  152. package/src/pages-tenant/UsageBar.vue +107 -0
  153. package/src/pages-tenant/default-i18n.ts +280 -0
  154. package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +304 -0
  155. package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +323 -0
  156. package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +122 -0
  157. package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +67 -0
  158. package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +31 -0
  159. package/src/plan-versions-catalog.ts +368 -0
  160. package/src/platform-loaders.ts +68 -0
  161. package/src/project-page-host.ts +108 -0
  162. package/src/testing-e2e/admin-pages-suite.ts +187 -0
  163. package/src/types.ts +58 -0
  164. package/src/use-actions.ts +25 -0
  165. package/src/use-api-list.ts +151 -0
  166. package/src/use-audit-entries.ts +48 -0
  167. package/src/use-batch-columns.ts +59 -0
  168. package/src/use-bulk-publish.ts +150 -0
  169. package/src/use-bundle-versions-map.ts +112 -0
  170. package/src/use-bundles.ts +297 -0
  171. package/src/use-business-types.ts +269 -0
  172. package/src/use-catalog-entries.ts +243 -0
  173. package/src/use-discovery.ts +158 -0
  174. package/src/use-entitlement.ts +87 -0
  175. package/src/use-live-plan-versions.ts +126 -0
  176. package/src/use-manifest.ts +57 -0
  177. package/src/use-marketing-projections.ts +160 -0
  178. package/src/use-nav.ts +33 -0
  179. package/src/use-plan-editor.ts +162 -0
  180. package/src/use-plan-versions.ts +83 -0
  181. package/src/use-plans.ts +375 -0
  182. package/src/use-platform-tenant-actions.ts +286 -0
  183. package/src/use-promotions.ts +126 -0
  184. package/src/use-public-boot.ts +40 -0
  185. package/src/use-subscription-draft.ts +355 -0
  186. package/src/use-super-admin-context.ts +93 -0
  187. package/src/use-tenant-action-flow.ts +238 -0
  188. package/src/use-tenant-billing-catalog.ts +160 -0
  189. package/src/use-tenant-billing.ts +453 -0
  190. package/src/use-tenant-manifest.ts +94 -0
  191. package/src/use-tenant-subscription-bundles.ts +148 -0
  192. package/src/use-tenants.ts +61 -0
  193. package/src/version.ts +10 -0
@@ -0,0 +1,2395 @@
1
+ import { RouteRecordRaw, NavigationGuardWithThis, RouterHistory, Router } from 'vue-router';
2
+ import { PublicBootResponse, AdminManifest, StandardPageKey, TenantActionDef, TenantColumnDef, TenantListFilter, TenantDto, AuditQuery, AuditEntry, FeatureUiRegistry, PromoPreviewResponse, OnboardingSelectionRequest, PublicMarketingBundle, FeatureDef, FeatureKey, DiscoverySnapshot, CapabilityCatalogEntryRow, FeatureCatalogEntryRow, QuotaCatalogEntryRow, ReviewCatalogEntryData, CatalogEntryI18n, UpdateCatalogEntryBaseData, SyncDiscoveryResult, BundleVersionRow, CreateBundleVersionDraftData, BundleVersionMutationResult, UpdateBundleVersionDraftData, BundleRow, CreateBundleData, UpdateBundleData, SubscriptionBundleRecord, BusinessTypeVersionRow, CreateBusinessTypeVersionDraftData, BusinessTypeVersionMutationResult, UpdateBusinessTypeVersionDraftData, BusinessTypeRow, CreateBusinessTypeData, UpdateBusinessTypeData, MarketingProjectionFilter, MarketingProjectionRow, CreateMarketingProjectionData, UpdateMarketingProjectionData, PromotionRow, CreatePromotionData, UpdatePromotionData, PlanVersionRow as PlanVersionRow$1, CreatePlanVersionDraftData, PlanVersionMutationResult, UpdatePlanVersionDraftData, PlanRow, CreatePlanData, UpdatePlanData, ActionKey, ComponentKey } from '@saasicat/types';
3
+ import * as vue from 'vue';
4
+ import { Ref, InjectionKey, App, ComputedRef, Component } from 'vue';
5
+ import { QuasarPluginOptions } from 'quasar';
6
+ import { Pinia, StoreDefinition } from 'pinia';
7
+
8
+ declare const ADMIN_UI_VERSION = "1.2.0";
9
+
10
+ /**
11
+ * Minimal abstraction over `fetch`. Consumers may pass their own
12
+ * implementation (e.g. an axios wrapper that already injects auth headers
13
+ * and tenant headers).
14
+ */
15
+ type HttpClient = (url: string, init?: {
16
+ method?: string;
17
+ headers?: Record<string, string>;
18
+ body?: string;
19
+ }) => Promise<HttpResponse>;
20
+ interface HttpResponse {
21
+ status: number;
22
+ headers: {
23
+ get(name: string): string | null;
24
+ };
25
+ json(): Promise<unknown>;
26
+ text(): Promise<string>;
27
+ }
28
+ /** Simple persistence adapter; default is `localStorage`. */
29
+ interface KvStore {
30
+ get(key: string): string | null;
31
+ set(key: string, value: string): void;
32
+ remove(key: string): void;
33
+ }
34
+ /**
35
+ * Returns a `KvStore` wrapper around `localStorage` (or `null` under SSR).
36
+ * Tests may pass an in-memory stub.
37
+ */
38
+ declare function defaultKvStore(): KvStore;
39
+ /**
40
+ * Default `HttpClient` over `fetch`. Consumers pass their own variant
41
+ * through when they need auth headers / tenant headers / retry logic.
42
+ */
43
+ declare function defaultHttpClient(): HttpClient;
44
+
45
+ /**
46
+ * Error for non-2xx responses. `code` is the machine-readable error code
47
+ * from the JSON body (`{ code }`), if present — callers map it to
48
+ * a message (e.g. via `SETUP_ERROR_CODES`).
49
+ */
50
+ declare class HttpJsonError extends Error {
51
+ readonly status: number;
52
+ readonly code?: string | undefined;
53
+ constructor(status: number, code?: string | undefined);
54
+ }
55
+ /**
56
+ * Removes trailing slashes so an API prefix can be concatenated with paths
57
+ * that start with `/`. Deliberately index-based instead of
58
+ * `replace(/\/+$/, '')`: the regex backtracks quadratically on inputs that
59
+ * end in many slashes.
60
+ */
61
+ declare function trimTrailingSlashes(url: string): string;
62
+ declare function getJson<T>(http: HttpClient, url: string): Promise<T>;
63
+ declare function postJson<T>(http: HttpClient, url: string, body: unknown): Promise<T>;
64
+
65
+ /** Eager or lazy component loader (`() => import(...)`). */
66
+ type RouteComponent = NonNullable<RouteRecordRaw['component']>;
67
+ interface CreateAdminRoutesOptions {
68
+ /**
69
+ * Loader for the shared login page. The app passes it through as a
70
+ * package-path import (resolved by the app bundler):
71
+ * `() => import('@saasicat/ui-vue/pages-standard/SuperAdminLoginPage.vue')`.
72
+ */
73
+ loginPage: RouteComponent;
74
+ /** Layout component for `/admin` (e.g. `() => import('@/layouts/AdminLayout.vue')`). */
75
+ adminLayout: RouteComponent;
76
+ /** Fail-closed error page for `manifestGuard.errorRoute` (`/admin-error`, public). */
77
+ adminErrorPage: RouteComponent;
78
+ /**
79
+ * App-specific pages under `/admin` (without the `→ dashboard` redirect and
80
+ * the `ProjectPageHost` catch-all — those the factory adds). Exactly the
81
+ * `children` entries that are duplicated per app today.
82
+ */
83
+ children: RouteRecordRaw[];
84
+ /** Default target of the `/admin` index redirect. Default `/admin/dashboard`. */
85
+ dashboardPath?: string;
86
+ }
87
+ declare function createAdminRoutes(options: CreateAdminRoutesOptions): RouteRecordRaw[];
88
+
89
+ interface BootLoaderOptions {
90
+ /**
91
+ * Fully-qualified boot endpoint incl. app globalPrefix
92
+ * (`/api/admin/boot`, `/api/v1/admin/boot`, …). Mandatory.
93
+ */
94
+ endpoint: string;
95
+ /** Defaults to `defaultHttpClient()` (= `fetch`). */
96
+ http?: HttpClient;
97
+ }
98
+ declare class BootLoadError extends Error {
99
+ readonly status: number;
100
+ constructor(status: number, message: string);
101
+ }
102
+ declare class BootLoader {
103
+ private readonly endpoint;
104
+ private readonly http;
105
+ constructor(options: BootLoaderOptions);
106
+ load(): Promise<PublicBootResponse>;
107
+ }
108
+
109
+ interface ManifestLoaderOptions {
110
+ /**
111
+ * Fully-qualified manifest endpoint incl. app globalPrefix
112
+ * (`/api/admin/manifest`, `/api/v1/admin/manifest`, …). Mandatory.
113
+ */
114
+ endpoint: string;
115
+ http?: HttpClient;
116
+ storage?: KvStore;
117
+ /**
118
+ * Storage key prefix — consumers with multiple apps under one domain
119
+ * set this to e.g. `'ma:'` or `'da:'`, so that the caches
120
+ * are separated.
121
+ */
122
+ storageKeyPrefix?: string;
123
+ /**
124
+ * Auth header for `Authorization: Bearer <token>`. Sent with every
125
+ * request. The consumer supplies a function that pulls the
126
+ * current token from the auth store.
127
+ */
128
+ getAuthToken?: () => string | null;
129
+ }
130
+ declare class ManifestLoadError extends Error {
131
+ readonly status: number;
132
+ constructor(status: number, message: string);
133
+ }
134
+ interface CachedManifestEntry {
135
+ etag: string;
136
+ body: AdminManifest;
137
+ }
138
+ declare class ManifestLoader {
139
+ private readonly endpoint;
140
+ private readonly http;
141
+ private readonly storage;
142
+ private readonly bodyKey;
143
+ private readonly etagKey;
144
+ private readonly getAuthToken?;
145
+ constructor(options: ManifestLoaderOptions);
146
+ /**
147
+ * Loads the current manifest. On a cache hit (304) the cached
148
+ * body is returned — otherwise the fresh server body.
149
+ */
150
+ load(): Promise<AdminManifest>;
151
+ /** Reads the cached manifest body from storage; null if absent. */
152
+ readCachedBody(): CachedManifestEntry | null;
153
+ /** Clears the cache — e.g. on logout or after `manifest reload`. */
154
+ clearCache(): void;
155
+ }
156
+
157
+ /**
158
+ * Default routes for the platform standard pages. Consumers may override this
159
+ * via the `standardPageRoutes` option (e.g. `/admin/users` →
160
+ * `/admin/team`).
161
+ */
162
+ declare const DEFAULT_STANDARD_PAGE_ROUTES: Record<StandardPageKey, string>;
163
+ interface BuildRouteEntry {
164
+ /** Stable identifier — the key for standard pages, the `id` for project pages. */
165
+ id: string;
166
+ /** Route path (`/admin/...`). */
167
+ path: string;
168
+ /** Visible for the UI. */
169
+ label: string;
170
+ icon?: string;
171
+ /** Sort key for drawer groups. */
172
+ navSection?: string;
173
+ /** Lookup key in the `extensions:` map (project) or the platform standard map. */
174
+ componentKey: string;
175
+ /** Required capability or null. */
176
+ requiredCapability: string | null;
177
+ /** Platform standard page (`true`) or project page (`false`). */
178
+ isStandard: boolean;
179
+ /** Hint for anticipatory lazy loading. */
180
+ prefetchOnIdle?: boolean;
181
+ }
182
+ interface NavBuilderOptions {
183
+ /**
184
+ * Optional: overrides the default routes for certain standard pages.
185
+ * Consumers set this if they want an alternative URL structure.
186
+ */
187
+ standardPageRoutes?: Partial<Record<StandardPageKey, string>>;
188
+ /**
189
+ * Default labels for standard pages — consumers may localize.
190
+ */
191
+ standardPageLabels?: Partial<Record<StandardPageKey, string>>;
192
+ /** Default icons for standard pages. */
193
+ standardPageIcons?: Partial<Record<StandardPageKey, string>>;
194
+ /** Default `navSection` for standard pages. */
195
+ standardPageNavSection?: Partial<Record<StandardPageKey, string>>;
196
+ /**
197
+ * Optional: set of known `componentKey`s from the shell's `extensions:`
198
+ * map. ProjectPages whose `componentKey` is not contained here
199
+ * are filtered out — they would otherwise appear in the sidebar but on
200
+ * click be redirected by the catch-all (silent dead link).
201
+ * Consumers without an `extensions:` map omit the field → as before.
202
+ */
203
+ availableExtensions?: Set<string>;
204
+ }
205
+ declare const DEFAULT_SECTION_ORDER: readonly string[];
206
+ /**
207
+ * Returns the list of all routes defined by the current manifest —
208
+ * filtered to the capabilities that the logged-in user has.
209
+ *
210
+ * The consumer shell then builds its Vue router configuration and its
211
+ * sidebar drawer from it.
212
+ */
213
+ declare function buildRoutes(manifest: AdminManifest, options?: NavBuilderOptions): BuildRouteEntry[];
214
+ interface SidebarItem {
215
+ id: string;
216
+ path: string;
217
+ label: string;
218
+ icon?: string;
219
+ }
220
+ interface SidebarSection {
221
+ /** `null` for the default section (items without `navSection`). */
222
+ section: string | null;
223
+ items: SidebarItem[];
224
+ }
225
+ /**
226
+ * Groups the routes by `navSection` for the drawer.
227
+ *
228
+ * Section order:
229
+ * 1. Default section (`null`) — items without `navSection`.
230
+ * 2. Sections from `sectionOrder` in exactly this order.
231
+ * 3. Remaining sections alphabetically.
232
+ *
233
+ * The default order matches the plan simulation layout (Übersicht →
234
+ * Produktkatalog → Kunden → System); consumers can override it.
235
+ */
236
+ declare function buildSidebar(routes: BuildRouteEntry[], sectionOrder?: readonly string[]): SidebarSection[];
237
+ /**
238
+ * Returns the Vue component registered for the given `componentKey`.
239
+ * The consumer shell calls this function with its own `extensions:` map.
240
+ * For unknown keys → `null` (the UI then renders a fallback
241
+ * component, e.g. "Component not found in shell build").
242
+ *
243
+ * `extensions` is `Record<string, T>` — `T` is typically a
244
+ * Vue component (either imported directly or as a
245
+ * `defineAsyncComponent` wrapper).
246
+ */
247
+ declare function resolveExtension<T>(componentKey: string, extensions: Record<string, T>): T | null;
248
+
249
+ /** Consumer implementation; receives the action inputs as a generic object. */
250
+ type ActionHandler<TInput = unknown, TResult = unknown> = (input: TInput) => Promise<TResult>;
251
+ interface ResolvedAction<TInput = unknown, TResult = unknown> {
252
+ def: TenantActionDef;
253
+ handler: ActionHandler<TInput, TResult>;
254
+ }
255
+ declare class MissingHandlerError extends Error {
256
+ constructor(actionKey: string);
257
+ }
258
+ declare class ActionDefNotInManifestError extends Error {
259
+ constructor(actionKey: string);
260
+ }
261
+ declare class ActionRegistry {
262
+ private readonly defs;
263
+ private readonly handlers;
264
+ constructor(manifest: AdminManifest, handlers?: Record<string, ActionHandler>);
265
+ /**
266
+ * Registers a handler after the fact (e.g. when consumer code loads
267
+ * lazily). Throws `ActionDefNotInManifestError` if the `actionKey` is not
268
+ * declared in the manifest — prevents dead registrations.
269
+ */
270
+ register<TInput, TResult>(actionKey: string, handler: ActionHandler<TInput, TResult>): void;
271
+ /**
272
+ * Returns the `{def, handler}` pair. Throws if the key is missing from
273
+ * the manifest or no handler is registered. The shell's UI layer calls
274
+ * the method, checks `def.requiresMfa` / `def.confirmType` for the
275
+ * pre-flow, and then calls `handler(input)`.
276
+ */
277
+ get<TInput = unknown, TResult = unknown>(actionKey: string): ResolvedAction<TInput, TResult>;
278
+ /**
279
+ * Convenience: `get(key).handler(input)`. UI convenience for actions
280
+ * that need neither MFA nor confirm.
281
+ */
282
+ dispatch<TInput, TResult>(actionKey: string, input: TInput): Promise<TResult>;
283
+ /**
284
+ * List of actionKeys that are declared in the manifest but have no
285
+ * handler. Consumers use this in a doctor check to detect drift between
286
+ * the manifest and the shell build.
287
+ */
288
+ listOrphanedDefs(): string[];
289
+ /**
290
+ * List of registered handlers that are missing from the manifest. Drift
291
+ * in the other direction.
292
+ */
293
+ listOrphanedHandlers(): string[];
294
+ }
295
+
296
+ type BatchColumnValue = unknown;
297
+ /** One value per `tenantId` (freely structured). */
298
+ type BatchColumnRow = Record<string, BatchColumnValue>;
299
+ /** One `tenantId → value` map per column key. */
300
+ type BatchColumnData = Record<string, BatchColumnRow>;
301
+ type ParamStyle = 'comma' | 'repeat';
302
+ interface BatchColumnFetcherOptions {
303
+ http?: HttpClient;
304
+ /**
305
+ * How the `tenantIds` are passed to the endpoint. Default `'comma'` →
306
+ * `?tenantIds=t1,t2,t3`. `'repeat'` → `?tenantIds=t1&tenantIds=t2`.
307
+ * Consumer backends decide based on their framework (NestJS accepts
308
+ * comma-separated strings by default, or via `@Query() ids: string[]`).
309
+ */
310
+ paramStyle?: ParamStyle;
311
+ /**
312
+ * Auth-token provider for `Authorization: Bearer <token>`. The consumer
313
+ * supplies a function that pulls the current token from the auth store.
314
+ */
315
+ getAuthToken?: () => string | null;
316
+ }
317
+ declare class BatchColumnDriftError extends Error {
318
+ readonly column: TenantColumnDef;
319
+ constructor(column: TenantColumnDef, reason: string);
320
+ }
321
+ declare class BatchColumnFetcher {
322
+ private readonly http;
323
+ private readonly paramStyle;
324
+ private readonly getAuthToken?;
325
+ constructor(options?: BatchColumnFetcherOptions);
326
+ /**
327
+ * Loads the data for all columns declared in the manifest in parallel
328
+ * (one request per column, all `tenantIds` in the batch). Columns without
329
+ * a satisfied capability are ignored.
330
+ */
331
+ fetchAll(manifest: AdminManifest, tenantIds: string[]): Promise<BatchColumnData>;
332
+ /** Fetch a single column (consumers may also use this directly). */
333
+ fetchOne(column: TenantColumnDef, tenantIds: string[]): Promise<BatchColumnRow>;
334
+ /**
335
+ * Column drift against the manifest. Consumers use this for CI smoke
336
+ * tests of the manifest-vs-shell build.
337
+ */
338
+ listDriftIssues(manifest: AdminManifest): BatchColumnDriftError[];
339
+ /**
340
+ * Which columns have a satisfied `requiredCapability`? Manifest =
341
+ * discovery, so a local check is enough.
342
+ */
343
+ eligibleColumns(manifest: AdminManifest): TenantColumnDef[];
344
+ private validateBatchEndpoint;
345
+ private buildUrl;
346
+ }
347
+
348
+ interface ApiListResponse<T> {
349
+ items: T[];
350
+ page?: number;
351
+ pageSize?: number;
352
+ total?: number;
353
+ }
354
+ interface UseApiListOptions<TFilter> {
355
+ endpoint: string;
356
+ /**
357
+ * Reactive filter object. The composable serializes it into `?key=value`
358
+ * pairs (with URL encoding); empty/null values are omitted.
359
+ */
360
+ filter?: Ref<TFilter>;
361
+ http?: HttpClient;
362
+ getAuthToken?: () => string | null;
363
+ /**
364
+ * When `true`, loads automatically on mount. Defaults to `true`.
365
+ * Set to `false` when the consumer wants to trigger the first load
366
+ * explicitly (e.g. after auth-state init).
367
+ */
368
+ autoLoad?: boolean;
369
+ }
370
+ interface UseApiListResult<T> {
371
+ items: Ref<T[]>;
372
+ page: Ref<number>;
373
+ pageSize: Ref<number>;
374
+ total: Ref<number>;
375
+ loading: Ref<boolean>;
376
+ error: Ref<Error | null>;
377
+ /** Reloads fresh (e.g. after a mutation). */
378
+ reload: () => Promise<void>;
379
+ /** Jumps to a specific page (1-based) and loads. */
380
+ goToPage: (page: number) => Promise<void>;
381
+ /** Changes the page size and jumps to page 1. */
382
+ setPageSize: (size: number) => Promise<void>;
383
+ }
384
+ declare function useApiList<T, TFilter extends Record<string, unknown> = Record<string, unknown>>(options: UseApiListOptions<TFilter>): UseApiListResult<T>;
385
+
386
+ interface UseTenantsOptions {
387
+ /**
388
+ * Fully qualified tenants-list endpoint including the app globalPrefix
389
+ * (`/api/admin/tenants`, `/api/v1/admin/tenants`, …). Mandatory — the
390
+ * platform has no uniform default, because apps mount differently
391
+ * (see header comment).
392
+ */
393
+ endpoint: string;
394
+ /** Reactive filter; default is an empty object. */
395
+ filter?: Ref<TenantListFilter>;
396
+ http?: UseApiListOptions<Record<string, unknown>>['http'];
397
+ getAuthToken?: () => string | null;
398
+ autoLoad?: boolean;
399
+ }
400
+ interface UseTenantsResult<T extends TenantDto = TenantDto> extends UseApiListResult<T> {
401
+ filter: Ref<TenantListFilter>;
402
+ }
403
+ /**
404
+ * Composable for the tenants list. Generic over the row shape:
405
+ * consumer apps with extended backend responses (plan/usage/pilot/…)
406
+ * specialize via `useTenants<MyRow>()`.
407
+ */
408
+ declare function useTenants<T extends TenantDto = TenantDto>(options: UseTenantsOptions): UseTenantsResult<T>;
409
+
410
+ interface UseAuditEntriesOptions {
411
+ /**
412
+ * Fully-qualified audit endpoint including the app's globalPrefix
413
+ * (`/api/admin/audit`, `/api/v1/admin/audit`, …). Mandatory.
414
+ */
415
+ endpoint: string;
416
+ filter?: Ref<AuditQuery>;
417
+ http?: UseApiListOptions<Record<string, unknown>>['http'];
418
+ getAuthToken?: () => string | null;
419
+ autoLoad?: boolean;
420
+ }
421
+ interface UseAuditEntriesResult extends UseApiListResult<AuditEntry> {
422
+ filter: Ref<AuditQuery>;
423
+ }
424
+ declare function useAuditEntries(options: UseAuditEntriesOptions): UseAuditEntriesResult;
425
+
426
+ interface EntitlementSnapshotShape {
427
+ plan: string;
428
+ quotas: Record<string, number>;
429
+ features: string[];
430
+ }
431
+ interface UseEntitlementOptions {
432
+ /**
433
+ * Fully-qualified entitlement endpoint including the app globalPrefix
434
+ * (`/api/billing/entitlement`, `/api/v1/billing/entitlement`, …). Mandatory.
435
+ */
436
+ endpoint: string;
437
+ http?: HttpClient;
438
+ getAuthToken?: () => string | null;
439
+ /** Default `true`. */
440
+ autoLoad?: boolean;
441
+ }
442
+ interface UseEntitlementResult {
443
+ entitlement: Ref<EntitlementSnapshotShape | null>;
444
+ loading: Ref<boolean>;
445
+ error: Ref<Error | null>;
446
+ load: () => Promise<void>;
447
+ /** Convenience: checks whether a FeatureKey is in the set. */
448
+ hasFeature: (key: string) => boolean;
449
+ }
450
+ declare function useEntitlement(options: UseEntitlementOptions): UseEntitlementResult;
451
+
452
+ interface TenantManifestNavItem {
453
+ id: string;
454
+ label: string;
455
+ path: string;
456
+ icon?: string;
457
+ order?: number;
458
+ }
459
+ interface TenantManifestShape {
460
+ schemaVersion: 1;
461
+ tenant: {
462
+ id: string;
463
+ };
464
+ planId: string | null;
465
+ features: string[];
466
+ quotas: Record<string, number>;
467
+ navigation: TenantManifestNavItem[];
468
+ }
469
+ interface UseTenantManifestOptions {
470
+ /** e.g. `/api/tenant/manifest` (mandatory). */
471
+ endpoint: string;
472
+ http?: HttpClient;
473
+ getAuthToken?: () => string | null;
474
+ /** Defaults to `true`. */
475
+ autoLoad?: boolean;
476
+ }
477
+ interface UseTenantManifestResult {
478
+ manifest: Ref<TenantManifestShape | null>;
479
+ loading: Ref<boolean>;
480
+ error: Ref<Error | null>;
481
+ load: () => Promise<void>;
482
+ hasFeature: (key: string) => boolean;
483
+ /** Quota limit (`null` if the Quota is not in the Plan). */
484
+ quotaLimit: (key: string) => number | null;
485
+ }
486
+ declare function useTenantManifest(options: UseTenantManifestOptions): UseTenantManifestResult;
487
+
488
+ declare const ENTITLEMENT_INJECTION_KEY: InjectionKey<UseEntitlementResult>;
489
+ /**
490
+ * Binds a `useEntitlement(...)` result for the whole app so that
491
+ * `<FeatureGate>` and router guards can inject it.
492
+ */
493
+ declare function provideEntitlement(app: App, entitlement: UseEntitlementResult): void;
494
+ /**
495
+ * Fetches the bound entitlement result. Returns `null` when the
496
+ * consumer did not call `provideEntitlement(...)` — `<FeatureGate>`
497
+ * then falls back to "everything allowed" (with a dev warning).
498
+ */
499
+ declare function useInjectedEntitlement(): UseEntitlementResult | null;
500
+
501
+ interface FeatureRouterGuardOptions {
502
+ /**
503
+ * Returns the current entitlement. Deliberately a factory (instead of
504
+ * passing a ref directly), so the app can lazy-load the entitlement and
505
+ * re-bind it — e.g. after a plan change or logout/re-login.
506
+ */
507
+ getEntitlement: () => UseEntitlementResult | null;
508
+ /**
509
+ * Where to redirect when a feature is missing. Default: no redirect, but
510
+ * `next(false)` (route blocks). Apps with an upgrade page set
511
+ * `'/upgrade'`.
512
+ */
513
+ redirectTo?: string;
514
+ /**
515
+ * `true` (default): entitlement not yet loaded → let through,
516
+ * so the first render doesn't hang. `false`: blocks until the entitlement
517
+ * is there (user sees a white screen if the endpoint is slow — only
518
+ * sensible in apps with pre-login load).
519
+ */
520
+ allowWhileLoading?: boolean;
521
+ }
522
+ declare function buildFeatureRouterGuard(options: FeatureRouterGuardOptions): NavigationGuardWithThis<undefined>;
523
+
524
+ interface CatalogPlan {
525
+ id: string;
526
+ name: string;
527
+ tagline: string;
528
+ monthlyNet: number | null;
529
+ yearlyNet: number | null;
530
+ popular: boolean;
531
+ quotas: Record<string, number>;
532
+ features: string[];
533
+ }
534
+ /**
535
+ * Bookable catalog bundle (wire form of `PublicBundleEntry` from
536
+ * `GET /billing/bundles`) — standalone catalog bundles
537
+ * (`bundle_versions`) with their own purchase flow `/billing/subscription-bundles`.
538
+ * Prices arrive as a decimal string on the wire and become `number` here.
539
+ */
540
+ interface CatalogBundle {
541
+ bundleVersionId: string;
542
+ bundleKey: string;
543
+ label: string;
544
+ description: string | null;
545
+ features: string[];
546
+ quotas: Record<string, number>;
547
+ monthlyNet: number | null;
548
+ yearlyNet: number | null;
549
+ /**
550
+ * Uncovered feature dependencies (#35): union of the `requires` of the
551
+ * contained features minus those contained in the bundle itself. The UI
552
+ * grays out the bundle if these keys are neither in the plan nor in the
553
+ * active bundles. Empty without requires data (graceful).
554
+ */
555
+ requiresFeatures: string[];
556
+ /** Marketing price label (e.g. "from 19 €/month") — null = auto-format. */
557
+ priceTag: string | null;
558
+ }
559
+ interface UseTenantBillingCatalogOptions {
560
+ /**
561
+ * URL prefix before `/plans`, `/feature-registry`, `/bundles`.
562
+ * Default `'/billing'`. **Convention**: `apiPrefix` is the sub-path
563
+ * UNDER the app API base URL that the HTTP adapter itself holds.
564
+ * Example: HTTP adapter baseURL `/api` + apiPrefix `/billing` →
565
+ * `/api/billing/...`. Do NOT set `'/api/billing'` if the HTTP adapter
566
+ * already has `/api` as baseURL (→ `/api/api/...` 404).
567
+ */
568
+ apiPrefix?: string;
569
+ http?: HttpClient;
570
+ /**
571
+ * Default `true`. Set to `false` if the consumer wants to trigger
572
+ * `load()` itself (e.g. after login).
573
+ */
574
+ autoLoad?: boolean;
575
+ }
576
+ interface UseTenantBillingCatalogResult {
577
+ plans: Ref<CatalogPlan[] | null>;
578
+ featureRegistry: Ref<FeatureUiRegistry | null>;
579
+ /**
580
+ * Bookable catalog bundles (`/billing/bundles`). `null` while not yet
581
+ * loaded; `[]` if the endpoint is missing/empty (non-fatal).
582
+ */
583
+ bundles: Ref<CatalogBundle[] | null>;
584
+ loading: Ref<boolean>;
585
+ error: Ref<Error | null>;
586
+ /** Loads plans/feature-registry in parallel + bundles (non-fatal). */
587
+ load: () => Promise<void>;
588
+ }
589
+ declare function useTenantBillingCatalog(options?: UseTenantBillingCatalogOptions): UseTenantBillingCatalogResult;
590
+
591
+ type BillingCycleStr = 'MONTHLY' | 'YEARLY';
592
+ interface UsageSnapshotShape {
593
+ plan: string;
594
+ effectivePlan: string;
595
+ billingCycle: BillingCycleStr;
596
+ status: string;
597
+ isPilot: boolean;
598
+ pilotEndsAt: string | null;
599
+ trialEndsAt: string | null;
600
+ startedAt: string | null;
601
+ currentPeriodStart: string | null;
602
+ currentPeriodEnd: string | null;
603
+ pendingPlan: string | null;
604
+ pendingBillingCycle: BillingCycleStr | null;
605
+ pendingEffectiveAt: string | null;
606
+ planVersion: {
607
+ id: string;
608
+ planId: string;
609
+ version: number;
610
+ publishedAt: string | null;
611
+ supersededAt: string | null;
612
+ changeNote: string | null;
613
+ };
614
+ pendingPlanVersion: {
615
+ id: string;
616
+ planId: string;
617
+ version: number;
618
+ nonRegressive: boolean;
619
+ changeNote: string | null;
620
+ publishedChanges: unknown;
621
+ } | null;
622
+ pendingPlanVersionEffectiveAt: string | null;
623
+ pendingPlanVersionAccepted: boolean;
624
+ pendingPlanVersionAcceptedAt: string | null;
625
+ limits: {
626
+ plan: string;
627
+ quotas: Record<string, number>;
628
+ features: string[];
629
+ };
630
+ usage: Record<string, number>;
631
+ /**
632
+ * P11.4 (METAMODELL §17a): Read-only package snapshot from the
633
+ * original CheckoutOffer. `null` for subscriptions without a
634
+ * CheckoutOffer origin. The JSON structure matches the Offer
635
+ * schema; the UI can read `bundleVersionIds`, `currency`,
636
+ * `priceTotal` and more from it.
637
+ */
638
+ packageSnapshot: PackageSnapshotShape | null;
639
+ /** P11.4: Optional reference to the original CheckoutOffer. */
640
+ checkoutOfferId: string | null;
641
+ }
642
+ /**
643
+ * Self-contained package snapshot (shape of `CheckoutOffer.snapshot`).
644
+ * All fields are optional because the snapshot schema may grow and older
645
+ * subscriptions can carry leaner snapshots. The UI must stay defensive
646
+ * against missing fields.
647
+ */
648
+ interface PackageSnapshotShape {
649
+ planId?: string;
650
+ planVersionId?: string;
651
+ billingCycle?: BillingCycleStr;
652
+ bundleVersionIds?: string[];
653
+ currency?: string;
654
+ priceMonthlyNet?: number | null;
655
+ priceYearlyNet?: number | null;
656
+ priceTotalNet?: number | null;
657
+ label?: string;
658
+ capturedAt?: string;
659
+ [key: string]: unknown;
660
+ }
661
+ interface PlanChangePreviewShape {
662
+ changeType: 'UPGRADE' | 'DOWNGRADE' | 'CYCLE_CHANGE' | 'NOOP';
663
+ current: {
664
+ plan: PlanSnapshotShape;
665
+ billingCycle: BillingCycleStr;
666
+ };
667
+ target: {
668
+ plan: PlanSnapshotShape;
669
+ billingCycle: BillingCycleStr;
670
+ };
671
+ effectiveAt: string | null;
672
+ isImmediate: boolean;
673
+ /** Projected new trial end (ISO) after the change, otherwise null. */
674
+ projectedTrialEndsAt: string | null;
675
+ proration: {
676
+ daysRemainingInPeriod: number;
677
+ daysInPeriod: number;
678
+ periodStart: string;
679
+ periodEnd: string;
680
+ currentPriceNet: number;
681
+ targetPriceNet: number;
682
+ prorataDeltaNet: number;
683
+ } | null;
684
+ limitsCheck: Record<string, {
685
+ used: number;
686
+ currentMax: number;
687
+ targetMax: number;
688
+ exceeded: boolean;
689
+ }>;
690
+ featuresLost: string[];
691
+ featuresGained: string[];
692
+ blockers: Array<{
693
+ code: string;
694
+ message: string;
695
+ }>;
696
+ warnings: Array<{
697
+ code: string;
698
+ message: string;
699
+ }>;
700
+ }
701
+ interface PlanSnapshotShape {
702
+ id: string;
703
+ name: string;
704
+ monthlyNet: number | null;
705
+ yearlyNet: number | null;
706
+ quotas: Record<string, number>;
707
+ features: string[];
708
+ }
709
+ /**
710
+ * Booked catalog bundle (wire shape of `SubscriptionBundleRecord`, dates
711
+ * as ISO strings). Source: `GET /billing/subscription-bundles`. The label/
712
+ * price is joined by the consumer via `bundleVersionId` against the bundle
713
+ * catalog (`GET /billing/bundles`) — the record itself carries only the
714
+ * version reference.
715
+ */
716
+ interface SubscriptionBundleShape {
717
+ id: string;
718
+ subscriptionId: string;
719
+ bundleVersionId: string;
720
+ /** Denormalized (server-side from the booked bundleVersion): label/
721
+ * key/price, so that booked bundles can be shown without a catalog join. */
722
+ bundleKey?: string | null;
723
+ label?: string | null;
724
+ monthlyNet?: string | null;
725
+ startedAt: string;
726
+ minimumTermEndsAt: string | null;
727
+ canceledAt: string | null;
728
+ canceledEffectiveAt: string | null;
729
+ }
730
+ interface BundlePreviewIssueShape {
731
+ code: string;
732
+ message: string;
733
+ }
734
+ interface BundlePreviewSnapshotShape {
735
+ bundleKey: string;
736
+ label: string;
737
+ bundleVersionId: string;
738
+ features: string[];
739
+ quotas: Record<string, number>;
740
+ }
741
+ /** AK-13: Feature is already paid for elsewhere — double-payment hint. */
742
+ interface RedundantFeatureHintShape {
743
+ featureKey: string;
744
+ coveredBy: 'PLAN' | 'BUNDLE';
745
+ coveredByKey: string;
746
+ }
747
+ /**
748
+ * Wire shape of `SubscriptionBundleAddPreviewDto` (#37,
749
+ * `POST /billing/subscription-bundles/preview` with `bundleVersionId`).
750
+ * `proration` is `null` during TRIAL or without a maintained list price.
751
+ */
752
+ interface BundleAddPreviewShape {
753
+ action: 'add';
754
+ bundle: BundlePreviewSnapshotShape;
755
+ billingCycle: string;
756
+ proration: {
757
+ daysRemainingInPeriod: number;
758
+ daysInPeriod: number;
759
+ periodStart: string;
760
+ periodEnd: string;
761
+ currentPriceNet: number;
762
+ targetPriceNet: number;
763
+ prorataDeltaNet: number;
764
+ } | null;
765
+ nextPeriodPriceNet: number | null;
766
+ minimumTermMonths: number;
767
+ minimumTermEndsAt: string | null;
768
+ redundantFeatures: RedundantFeatureHintShape[];
769
+ missingRequires: string[];
770
+ blockers: BundlePreviewIssueShape[];
771
+ warnings: BundlePreviewIssueShape[];
772
+ }
773
+ /**
774
+ * Wire shape of `SubscriptionBundleCancelPreviewDto` (#37, preview with
775
+ * `subscriptionBundleId`). `effectiveAt` = max(period end, minimum term).
776
+ */
777
+ interface BundleCancelPreviewShape {
778
+ action: 'cancel';
779
+ subscriptionBundleId: string;
780
+ bundle: BundlePreviewSnapshotShape;
781
+ billingCycle: string;
782
+ effectiveAt: string;
783
+ nextPeriodSavingsNet: number | null;
784
+ blockers: BundlePreviewIssueShape[];
785
+ warnings: BundlePreviewIssueShape[];
786
+ }
787
+ type BundlePreviewShape = BundleAddPreviewShape | BundleCancelPreviewShape;
788
+ interface UseTenantBillingOptions {
789
+ /**
790
+ * Default `'/billing'`. The app HTTP adapter sets the API base URL
791
+ * (e.g. `/api` or `/api/v1`); `apiPrefix` is
792
+ * the sub-path below it. A doubled `/api` prefix leads to HTTP 404.
793
+ */
794
+ apiPrefix?: string;
795
+ http?: HttpClient;
796
+ getAuthToken?: () => string | null;
797
+ /** Default `true`. */
798
+ autoLoad?: boolean;
799
+ }
800
+ interface UseTenantBillingResult {
801
+ usage: Ref<UsageSnapshotShape | null>;
802
+ loading: Ref<boolean>;
803
+ error: Ref<Error | null>;
804
+ reload: () => Promise<void>;
805
+ previewPlanChange: (plan: string, billingCycle: BillingCycleStr) => Promise<PlanChangePreviewShape>;
806
+ changePlan: (plan: string, billingCycle: BillingCycleStr, effectiveImmediately: boolean) => Promise<void>;
807
+ acceptPendingPlanVersion: () => Promise<void>;
808
+ cancelSubscription: (immediately: boolean) => Promise<void>;
809
+ /** True if `usage.value.features` contains the FeatureKey. */
810
+ hasFeature: (key: string) => boolean;
811
+ /**
812
+ * The tenant's booked catalog bundles (`/billing/subscription-bundles`).
813
+ * Loaded along with `reload()`. If the endpoint is missing (consumer
814
+ * without `SubscriptionBundleModule`), the list stays empty without
815
+ * setting the main `error` — the page degrades gracefully.
816
+ */
817
+ subscriptionBundles: Ref<SubscriptionBundleShape[]>;
818
+ /** Reloads only the booked bundles (non-fatal). */
819
+ loadBundles: () => Promise<void>;
820
+ /** Books a bundle via `bundleVersionId` + reloads the list. */
821
+ addBundle: (bundleVersionId: string, minimumTermMonths?: number) => Promise<void>;
822
+ /** Cancels a booked bundle via SubscriptionBundle PK + reloads. */
823
+ cancelBundle: (subscriptionBundleId: string) => Promise<void>;
824
+ /** Reverses a cancellation that has not yet taken effect + reloads. */
825
+ reactivateBundle: (subscriptionBundleId: string) => Promise<void>;
826
+ /**
827
+ * Add preview (#37): proration, next-period price, redundancy hint,
828
+ * requires check and blockers — show BEFORE booking.
829
+ */
830
+ previewAddBundle: (bundleVersionId: string, minimumTermMonths?: number) => Promise<BundleAddPreviewShape>;
831
+ /** Cancel preview (#37): effective date + savings from the next period on. */
832
+ previewCancelBundle: (subscriptionBundleId: string) => Promise<BundleCancelPreviewShape>;
833
+ }
834
+ declare function useTenantBilling(options?: UseTenantBillingOptions): UseTenantBillingResult;
835
+
836
+ declare const DEFAULT_YEARLY_FACTOR = 10;
837
+ type PromoStatus = 'idle' | 'checking' | 'valid' | 'invalid' | 'restricted';
838
+ interface PromoState {
839
+ status: PromoStatus;
840
+ /** Backend response (only set when status === 'valid' or 'restricted'). */
841
+ preview: PromoPreviewResponse | null;
842
+ /** Display text for the UI (confirmation or error message). */
843
+ message: string;
844
+ }
845
+ interface UseSubscriptionDraftOptions {
846
+ plans: Ref<CatalogPlan[] | null> | ComputedRef<CatalogPlan[] | null>;
847
+ subscriptionBundles?: Ref<PublicMarketingBundle[] | null> | ComputedRef<PublicMarketingBundle[] | null>;
848
+ initialPlan?: string | null;
849
+ initialCycle?: BillingCycleStr;
850
+ initialBundleVersionIds?: ReadonlyArray<string>;
851
+ /** Override for the `yearlyNet` fallback if the catalog provides no yearlyNet. */
852
+ yearlyFactor?: number;
853
+ }
854
+ interface PriceLineItem {
855
+ /** Stable key for UI `v-for`. */
856
+ key: string;
857
+ label: string;
858
+ /** Raw catalog value in the selected cycle unit. */
859
+ net: number;
860
+ /** Optional: additional text. */
861
+ sublabel?: string;
862
+ }
863
+ interface DraftPricing {
864
+ cycle: BillingCycleStr;
865
+ /** Plan base without bundles. */
866
+ planNet: number;
867
+ /** Sum of all selected catalog bundles. */
868
+ bundlesNet: number;
869
+ /** Plan + Bundles. */
870
+ subtotalNet: number;
871
+ /** Discount derived from the promo preview (on subtotalNet, not plan-only). */
872
+ discountNet: number;
873
+ /** subtotalNet - discountNet. */
874
+ totalNet: number;
875
+ /** Savings per year vs. monthly payment. */
876
+ yearSavings: number;
877
+ /** Structured breakdown for the sticky sidebar. */
878
+ breakdown: {
879
+ plan: PriceLineItem | null;
880
+ bundles: PriceLineItem[];
881
+ };
882
+ }
883
+ interface SubscriptionDraft {
884
+ plan: Ref<string | null>;
885
+ cycle: Ref<BillingCycleStr>;
886
+ selectedBundleVersionIds: Ref<Set<string>>;
887
+ promoCode: Ref<string>;
888
+ promoState: Ref<PromoState>;
889
+ selectedPlan: ComputedRef<CatalogPlan | null>;
890
+ /** Plan-included ∪ features of the selected catalog bundles. */
891
+ activeFeatures: ComputedRef<Set<string>>;
892
+ pricing: ComputedRef<DraftPricing>;
893
+ isDirty: ComputedRef<boolean>;
894
+ setPlan(planId: string): void;
895
+ setCycle(c: BillingCycleStr): void;
896
+ toggleSubscriptionBundle(bundleVersionId: string): void;
897
+ setPromoCode(code: string): void;
898
+ setPromoState(state: PromoState): void;
899
+ clearPromo(): void;
900
+ toApiPayload(): OnboardingSelectionRequest;
901
+ }
902
+ declare function useSubscriptionDraft(options: UseSubscriptionDraftOptions): SubscriptionDraft;
903
+
904
+ interface TenantPlanSectionI18n {
905
+ sectionTitle: string;
906
+ sectionSubtitle: string;
907
+ loading: string;
908
+ noSubscription: string;
909
+ activePlan: string;
910
+ cycleMonthly: string;
911
+ cycleYearly: string;
912
+ statusActive: string;
913
+ statusTrial: string;
914
+ statusPastDue: string;
915
+ statusCanceled: string;
916
+ statusPendingSales: string;
917
+ trialEndsAt: string;
918
+ pilotEndsAt: string;
919
+ nextBillingDate: string;
920
+ pendingChange: string;
921
+ changeFromTo: string;
922
+ changeEffectiveAt: string;
923
+ changePlanButton: string;
924
+ cancelSubscriptionButton: string;
925
+ usageTitle: string;
926
+ /** #18 — feature / scope-of-services matrix (included + locked). */
927
+ featuresOverviewTitle: string;
928
+ featuresActive: string;
929
+ featuresLocked: string;
930
+ /** #15 — bundle store (booked + available bundles). */
931
+ bundlesStoreTitle: string;
932
+ bundlesBookedTitle: string;
933
+ bundlesAvailableTitle: string;
934
+ bundlesAvailableEmpty: string;
935
+ bundlesPerMonth: string;
936
+ bundleBookAction: string;
937
+ bundleBookInProgress: string;
938
+ bundleCancelAction: string;
939
+ bundleReactivateAction: string;
940
+ bundleReactivateConfirmTitle: string;
941
+ bundleReactivateConfirmBody: string;
942
+ bundleCanceledAt: string;
943
+ bundleMinimumTermUntil: string;
944
+ bundleIncludesLabel: string;
945
+ bundleAlreadyBooked: string;
946
+ bundleIncompatible: string;
947
+ /** #37/#61 — requires graying-out + booking/cancellation preview. */
948
+ bundleMissingRequires: string;
949
+ bundlePreviewAddTitle: string;
950
+ bundlePreviewCancelTitle: string;
951
+ bundlePreviewLoading: string;
952
+ bundlePreviewProrationTitle: string;
953
+ bundlePreviewProratedNow: string;
954
+ bundlePreviewProrationDays: string;
955
+ bundlePreviewNextPeriod: string;
956
+ bundlePreviewTrialNote: string;
957
+ bundlePreviewNoPrice: string;
958
+ bundlePreviewMinimumTermLabel: string;
959
+ bundlePreviewMinimumTermMonths: string;
960
+ bundlePreviewMinimumTermNone: string;
961
+ bundlePreviewRedundantTitle: string;
962
+ bundlePreviewRedundantCoveredByPlan: string;
963
+ bundlePreviewRedundantCoveredByBundle: string;
964
+ bundlePreviewMissingRequiresTitle: string;
965
+ bundlePreviewBlockersTitle: string;
966
+ bundlePreviewWarningsTitle: string;
967
+ bundlePreviewEffectiveAt: string;
968
+ bundlePreviewSavings: string;
969
+ bundlePreviewConfirmAdd: string;
970
+ bundlePreviewConfirmCancel: string;
971
+ bundlePreviewInProgress: string;
972
+ bundlePreviewClose: string;
973
+ pendingVersionTitle: string;
974
+ pendingVersionChipNonRegressive: string;
975
+ pendingVersionChipRegressive: string;
976
+ pendingVersionEffectiveAt: string;
977
+ pendingVersionAcceptAction: string;
978
+ pendingVersionAcceptInProgress: string;
979
+ pendingVersionAcceptedAt: string;
980
+ wizardTitle: string;
981
+ wizardClose: string;
982
+ wizardCurrent: string;
983
+ wizardBadgeCurrent: string;
984
+ wizardBadgePopular: string;
985
+ wizardPriceUnitMonthly: string;
986
+ wizardPriceUnitYearly: string;
987
+ wizardPriceOnRequest: string;
988
+ wizardStepChoose: string;
989
+ wizardStepChooseIntro: string;
990
+ wizardStepPreview: string;
991
+ wizardStepConfirm: string;
992
+ wizardNext: string;
993
+ wizardBack: string;
994
+ wizardPreviewLoading: string;
995
+ wizardEffectiveAtLabel: string;
996
+ wizardEffectiveImmediate: string;
997
+ wizardProrationTitle: string;
998
+ wizardProrationLine: string;
999
+ wizardProrationDays: string;
1000
+ wizardLimitsTitle: string;
1001
+ wizardLimitsUsed: string;
1002
+ wizardLimitsCurrent: string;
1003
+ wizardLimitsTarget: string;
1004
+ wizardFeaturesGained: string;
1005
+ wizardFeaturesLost: string;
1006
+ wizardBlockersTitle: string;
1007
+ wizardConfirmImmediate: string;
1008
+ wizardConfirmScheduled: string;
1009
+ wizardConfirmAction: string;
1010
+ wizardConfirmInProgress: string;
1011
+ /** #17 — price overview in the confirm step. */
1012
+ wizardConfirmPriceTitle: string;
1013
+ wizardConfirmProratedNow: string;
1014
+ wizardConfirmRecurringNext: string;
1015
+ wizardConfirmRecurringFrom: string;
1016
+ wizardConfirmPerCycleMonthly: string;
1017
+ wizardConfirmPerCycleYearly: string;
1018
+ /** #17 — trial case in the confirm step (nothing due during the trial). */
1019
+ wizardConfirmTrialNote: string;
1020
+ wizardConfirmRecurringTrialEnd: string;
1021
+ wizardChangeTypeUpgrade: string;
1022
+ wizardChangeTypeDowngrade: string;
1023
+ wizardChangeTypeCycle: string;
1024
+ wizardChangeTypeNoop: string;
1025
+ /** P11.4: read-only display of the package snapshot. */
1026
+ packageSnapshotTitle: string;
1027
+ packageSnapshotSubtitle: string;
1028
+ packageSnapshotCapturedAt: string;
1029
+ packageSnapshotOfferRef: string;
1030
+ packageSnapshotPlanLabel: string;
1031
+ packageSnapshotPlanVersionLabel: string;
1032
+ packageSnapshotCycleLabel: string;
1033
+ packageSnapshotBundlesLabel: string;
1034
+ packageSnapshotBundlesEmpty: string;
1035
+ packageSnapshotPriceMonthly: string;
1036
+ packageSnapshotPriceYearly: string;
1037
+ packageSnapshotPriceTotal: string;
1038
+ packageSnapshotNone: string;
1039
+ packageSnapshotShowRaw: string;
1040
+ packageSnapshotHideRaw: string;
1041
+ }
1042
+ /** German default strings — apps can override them selectively. */
1043
+ declare const DEFAULT_I18N_DE: TenantPlanSectionI18n;
1044
+
1045
+ /**
1046
+ * Generic filter shape for versions lists.
1047
+ * `state`: 'draft' (publishedAt null), 'live' (publishedAt set, supersededAt null),
1048
+ * 'superseded' (supersededAt set), 'all' (default).
1049
+ */
1050
+ interface PlanVersionListFilter {
1051
+ state?: 'draft' | 'live' | 'superseded' | 'all';
1052
+ /** PlanId (`'BASIC'`, `'STANDARD'`, …). */
1053
+ planId?: string;
1054
+ }
1055
+ /**
1056
+ * Generic versions row shape. Depending on the endpoint, the consumer backend
1057
+ * returns slightly different fields; the platform composable takes the universal ones.
1058
+ */
1059
+ interface PlanVersionRow {
1060
+ id: string;
1061
+ version: number;
1062
+ publishedAt: string | null;
1063
+ supersededAt: string | null;
1064
+ nonRegressive: boolean;
1065
+ changeNote?: string;
1066
+ [extra: string]: unknown;
1067
+ }
1068
+ interface VersionsOptions {
1069
+ /**
1070
+ * Fully qualified versions-list endpoint including the app globalPrefix
1071
+ * (`/api/admin/plan-versions`, `/api/v1/admin/plan-versions`, …). Mandatory.
1072
+ */
1073
+ endpoint: string;
1074
+ filter?: Ref<PlanVersionListFilter>;
1075
+ http?: UseApiListOptions<Record<string, unknown>>['http'];
1076
+ getAuthToken?: () => string | null;
1077
+ autoLoad?: boolean;
1078
+ }
1079
+ interface VersionsResult extends UseApiListResult<PlanVersionRow> {
1080
+ filter: Ref<PlanVersionListFilter>;
1081
+ }
1082
+ /**
1083
+ * Read-only catalog view for PlanVersions (lift-and-shift from a
1084
+ * consumer admin).
1085
+ * Called `usePlanVersionsCatalog` since M6 Pack 2a, because `usePlanVersions`
1086
+ * from `use-plans.ts` is now the lifecycle editor (createDraft/publish).
1087
+ */
1088
+ declare const usePlanVersionsCatalog: (options: VersionsOptions) => VersionsResult;
1089
+
1090
+ type BulkItemKind = 'plan';
1091
+ type BulkItemStatus = 'pending' | 'publishing' | 'published' | 'failed';
1092
+ interface BulkPublishItem {
1093
+ /** Composite ID: `<kind>:<draftId>`. Unique within the bulk set. */
1094
+ key: string;
1095
+ kind: BulkItemKind;
1096
+ draftId: string;
1097
+ /** Display label in the modal (e.g. "STANDARD v3"). */
1098
+ label: string;
1099
+ status: BulkItemStatus;
1100
+ error?: string;
1101
+ /** Server response when status='published'. */
1102
+ result?: unknown;
1103
+ }
1104
+ interface UseBulkPublishOptions {
1105
+ /**
1106
+ * Endpoint mapping per kind. **Required** — the platform does not know
1107
+ * the app's globalPrefix (e.g. `/api/admin/...` or
1108
+ * `/api/v1/admin/...`), so consumers supply the full URL per kind.
1109
+ */
1110
+ endpoints: Record<BulkItemKind, (draftId: string) => string>;
1111
+ http?: HttpClient;
1112
+ getAuthToken?: () => string | null;
1113
+ }
1114
+ interface UseBulkPublishResult {
1115
+ items: Ref<BulkPublishItem[]>;
1116
+ /** Fraction of items already completed (0..1). */
1117
+ progress: ComputedRef<number>;
1118
+ /** Returns `true` once all items are either `published` or `failed`. */
1119
+ done: ComputedRef<boolean>;
1120
+ /** Number of successful publishes. */
1121
+ successCount: ComputedRef<number>;
1122
+ /** Number of failed publishes. */
1123
+ failureCount: ComputedRef<number>;
1124
+ /** Resets the bulk set — e.g. after modal open. */
1125
+ setItems: (items: Array<Omit<BulkPublishItem, 'status'>>) => void;
1126
+ /**
1127
+ * Triggers the bulk publish. `changeNote` is required for all
1128
+ * drafts; `mfaCode` is optional and is sent in the `X-Mfa-Code`
1129
+ * header when present.
1130
+ */
1131
+ run: (input: {
1132
+ changeNote: string;
1133
+ mfaCode?: string;
1134
+ }) => Promise<void>;
1135
+ }
1136
+ declare function useBulkPublish(options: UseBulkPublishOptions): UseBulkPublishResult;
1137
+
1138
+ declare class PlannedOnlyFeatureError extends Error {
1139
+ readonly violations: FeatureKey[];
1140
+ constructor(violations: FeatureKey[]);
1141
+ }
1142
+ interface FeatureRowMarkers {
1143
+ /** Catalog definition (label, icon, tier, marker). */
1144
+ def: FeatureDef;
1145
+ /** Currently selected in the draft. */
1146
+ isSelected: boolean;
1147
+ /** Catalog marker `plannedOnly: true` — the UI shows it as roadmap, not bookable. */
1148
+ isPlannedOnly: boolean;
1149
+ /** In the base plan (inherited) — not removable on nonRegressive drafts. */
1150
+ isInherited: boolean;
1151
+ /** Computed: is the user allowed to toggle the feature? */
1152
+ canToggle: boolean;
1153
+ }
1154
+ interface UsePlanEditorOptions {
1155
+ /** Initial selection — typically `draft.features` from PATCH preparation. */
1156
+ initialFeatures?: FeatureKey[];
1157
+ /** Features the base plan already contained; not removable under nonRegressive. */
1158
+ baseFeatures?: FeatureKey[];
1159
+ /** If `true`: base features are locked (default `true`, platform convention). */
1160
+ nonRegressive?: boolean;
1161
+ }
1162
+ interface UsePlanEditorResult {
1163
+ /** Feature keys currently selected in the draft (Set for O(1) lookup). */
1164
+ selectedFeatures: Ref<Set<FeatureKey>>;
1165
+ /** Ordered list of all catalog features with UI markers. */
1166
+ availableFeatures: ComputedRef<FeatureRowMarkers[]>;
1167
+ /** Tier grouping for drawer sections (CORE, ADVANCED, PRO, …). */
1168
+ featuresByTier: ComputedRef<Array<{
1169
+ tier: string;
1170
+ rows: FeatureRowMarkers[];
1171
+ }>>;
1172
+ /** Toggle without throwing: `plannedOnly` and locked features are ignored. */
1173
+ toggleFeature: (key: FeatureKey) => void;
1174
+ /** Pre-save validation. Throws `PlannedOnlyFeatureError` if disallowed keys are present. */
1175
+ validateDraft: () => void;
1176
+ /** Snapshot of the current selection for the PATCH body. */
1177
+ snapshot: () => FeatureKey[];
1178
+ }
1179
+ /**
1180
+ * Plan-editor discovery + validation. The manifest provides the source of
1181
+ * truth (catalog snapshot including the `plannedOnly` marker). The consumer
1182
+ * builds the Vue component and uses this composable for state + filter.
1183
+ */
1184
+ declare function usePlanEditor(manifest: AdminManifest, options?: UsePlanEditorOptions): UsePlanEditorResult;
1185
+
1186
+ interface UsePublicBootResult {
1187
+ boot: Ref<PublicBootResponse | null>;
1188
+ loading: Ref<boolean>;
1189
+ error: Ref<Error | null>;
1190
+ load: () => Promise<void>;
1191
+ }
1192
+ declare function usePublicBoot(options: BootLoaderOptions): UsePublicBootResult;
1193
+
1194
+ interface UseDiscoveryOptions {
1195
+ /**
1196
+ * Fully qualified discovery endpoint including the app globalPrefix
1197
+ * (`/api/admin/discovery`, `/api/v1/admin/discovery`, …). Mandatory.
1198
+ */
1199
+ endpoint: string;
1200
+ http?: HttpClient;
1201
+ /**
1202
+ * Auth header for `Authorization: Bearer <token>`. Sent along with every
1203
+ * request. The consumer supplies a function that pulls the current token
1204
+ * from the auth store.
1205
+ */
1206
+ getAuthToken?: () => string | null;
1207
+ /**
1208
+ * When `true`, loads automatically on composable init. Default
1209
+ * `false` — the consumer triggers `load()` itself (e.g. after page mount).
1210
+ */
1211
+ autoLoad?: boolean;
1212
+ }
1213
+ declare class DiscoveryLoadError extends Error {
1214
+ readonly status: number;
1215
+ constructor(status: number, message: string);
1216
+ }
1217
+ interface UseDiscoveryResult {
1218
+ snapshot: Ref<DiscoverySnapshot | null>;
1219
+ /** ETag header of the last 200 response, or null. */
1220
+ etag: Ref<string | null>;
1221
+ loading: Ref<boolean>;
1222
+ error: Ref<Error | null>;
1223
+ /**
1224
+ * Loads fresh. On a cache hit (HTTP 304) `snapshot` stays unchanged,
1225
+ * as does `etag`.
1226
+ */
1227
+ load: () => Promise<void>;
1228
+ /**
1229
+ * Discards the cache (etag = null) and loads without If-None-Match. Useful
1230
+ * after code deploys, when a new snapshot is expected.
1231
+ */
1232
+ reload: () => Promise<void>;
1233
+ /**
1234
+ * `POST <endpoint>/rescan` — forces a fresh code scan in the backend
1235
+ * (new `scannedAt`) and adopts the snapshot.
1236
+ */
1237
+ rescan: () => Promise<void>;
1238
+ }
1239
+ declare function useDiscovery(options: UseDiscoveryOptions): UseDiscoveryResult;
1240
+
1241
+ interface UseCatalogEntriesOptions {
1242
+ /** Admin endpoint prefix incl. globalPrefix (`/api/admin`, `/api/v1/admin`). */
1243
+ adminEndpoint: string;
1244
+ http?: HttpClient;
1245
+ getAuthToken?: () => string | null;
1246
+ /** projectKey the catalog entries are filtered against. */
1247
+ projectKey: string;
1248
+ autoLoad?: boolean;
1249
+ }
1250
+ declare class CatalogEntriesApiError extends Error {
1251
+ readonly status: number;
1252
+ readonly body: unknown;
1253
+ constructor(status: number, body: unknown, message: string);
1254
+ }
1255
+ interface UseCatalogEntriesResult {
1256
+ capabilities: Ref<CapabilityCatalogEntryRow[]>;
1257
+ features: Ref<FeatureCatalogEntryRow[]>;
1258
+ quotas: Ref<QuotaCatalogEntryRow[]>;
1259
+ loading: Ref<boolean>;
1260
+ error: Ref<Error | null>;
1261
+ load: () => Promise<void>;
1262
+ /** Approval transition of a feature (#20): PATCH …/features/:key/review. */
1263
+ reviewFeature: (featureKey: string, data: ReviewCatalogEntryData) => Promise<FeatureCatalogEntryRow>;
1264
+ /** Approval transition of a quota (#20): PATCH …/quotas/:key/review. */
1265
+ reviewQuota: (quotaKey: string, data: ReviewCatalogEntryData) => Promise<QuotaCatalogEntryRow>;
1266
+ setFeatureI18n: (featureKey: string, i18n: CatalogEntryI18n) => Promise<FeatureCatalogEntryRow>;
1267
+ setQuotaI18n: (quotaKey: string, i18n: CatalogEntryI18n) => Promise<QuotaCatalogEntryRow>;
1268
+ /** Sets the editable default-locale label/description of a feature. */
1269
+ setFeatureBase: (featureKey: string, data: UpdateCatalogEntryBaseData) => Promise<FeatureCatalogEntryRow>;
1270
+ /** Sets the editable default-locale label/description of a quota. */
1271
+ setQuotaBase: (quotaKey: string, data: UpdateCatalogEntryBaseData) => Promise<QuotaCatalogEntryRow>;
1272
+ /** Upserts the catalog entries from a discovery snapshot and reloads. */
1273
+ syncDiscovery: (snapshot: DiscoverySnapshot) => Promise<SyncDiscoveryResult>;
1274
+ }
1275
+ declare function useCatalogEntries(options: UseCatalogEntriesOptions): UseCatalogEntriesResult;
1276
+
1277
+ interface UseBundlesOptions {
1278
+ /**
1279
+ * Fully-qualified admin endpoint prefix including the app globalPrefix
1280
+ * (`/api/admin`, `/api/v1/admin`, …). Required. The composable appends
1281
+ * the catalog paths itself (`/catalog/bundles`).
1282
+ */
1283
+ adminEndpoint: string;
1284
+ http?: HttpClient;
1285
+ getAuthToken?: () => string | null;
1286
+ /** Required: projectKey the list is filtered against. */
1287
+ projectKey: string;
1288
+ /** When `true`, loads on composable init. Default `false`. */
1289
+ autoLoad?: boolean;
1290
+ }
1291
+ declare class BundlesApiError extends Error {
1292
+ readonly status: number;
1293
+ readonly body: unknown;
1294
+ constructor(status: number, body: unknown, message: string);
1295
+ }
1296
+ interface UseBundlesResult {
1297
+ bundles: Ref<BundleRow[]>;
1298
+ loading: Ref<boolean>;
1299
+ error: Ref<Error | null>;
1300
+ load: () => Promise<void>;
1301
+ create: (data: CreateBundleData) => Promise<BundleRow>;
1302
+ update: (bundleId: string, data: UpdateBundleData) => Promise<BundleRow>;
1303
+ softDelete: (bundleId: string) => Promise<void>;
1304
+ }
1305
+ declare function useBundles(options: UseBundlesOptions): UseBundlesResult;
1306
+ interface UseBundleVersionsOptions {
1307
+ adminEndpoint: string;
1308
+ bundleId: string;
1309
+ http?: HttpClient;
1310
+ getAuthToken?: () => string | null;
1311
+ autoLoad?: boolean;
1312
+ }
1313
+ interface UseBundleVersionsResult {
1314
+ versions: Ref<BundleVersionRow[]>;
1315
+ loading: Ref<boolean>;
1316
+ error: Ref<Error | null>;
1317
+ load: () => Promise<void>;
1318
+ /** Returns {row, warnings} — warnings should be shown to the user as a banner. */
1319
+ createDraft: (data: Omit<CreateBundleVersionDraftData, 'bundleId'>) => Promise<BundleVersionMutationResult>;
1320
+ updateDraft: (versionId: string, data: UpdateBundleVersionDraftData) => Promise<BundleVersionMutationResult>;
1321
+ publish: (versionId: string, opts?: {
1322
+ forceRegressive?: boolean;
1323
+ allowZeroPrice?: boolean;
1324
+ validFrom?: string | null;
1325
+ validUntil?: string | null;
1326
+ }) => Promise<BundleVersionMutationResult>;
1327
+ /**
1328
+ * Discards a draft (`DELETE /admin/catalog/bundle-versions/:id`).
1329
+ * Published versions cannot be discarded — the API responds
1330
+ * with 422 `BUNDLE_VERSION_ALREADY_PUBLISHED`.
1331
+ */
1332
+ discardDraft: (versionId: string) => Promise<void>;
1333
+ }
1334
+ declare function useBundleVersions(options: UseBundleVersionsOptions): UseBundleVersionsResult;
1335
+
1336
+ interface UseBundleVersionsMapOptions {
1337
+ adminEndpoint: string;
1338
+ /** Reactive list of bundle roots; the watcher reloads when the IDs change. */
1339
+ bundles: Ref<BundleRow[]>;
1340
+ http?: HttpClient;
1341
+ getAuthToken?: () => string | null;
1342
+ }
1343
+ interface UseBundleVersionsMapResult {
1344
+ /** `bundleId → BundleVersionRow[]`. Empty list for bundles without versions. */
1345
+ versionsByBundle: Ref<Record<string, BundleVersionRow[]>>;
1346
+ loading: Ref<boolean>;
1347
+ error: Ref<Error | null>;
1348
+ /** Forces a reload of all bundle versions. */
1349
+ refresh: () => Promise<void>;
1350
+ /** Refresh only a single bundle (e.g. after an inline-editor save). */
1351
+ refreshOne: (bundleId: string) => Promise<void>;
1352
+ }
1353
+ declare function useBundleVersionsMap(options: UseBundleVersionsMapOptions): UseBundleVersionsMapResult;
1354
+
1355
+ interface UseTenantSubscriptionBundlesOptions {
1356
+ /** App-global API prefix incl. `/billing` (e.g. `/api/v1`). */
1357
+ billingEndpoint: string;
1358
+ http?: HttpClient;
1359
+ getAuthToken?: () => string | null;
1360
+ /** With `true`, loads on mount. Default `false`. */
1361
+ autoLoad?: boolean;
1362
+ }
1363
+ interface UseTenantSubscriptionBundlesResult {
1364
+ bundles: Ref<SubscriptionBundleRecord[]>;
1365
+ loading: Ref<boolean>;
1366
+ error: Ref<Error | null>;
1367
+ load: () => Promise<void>;
1368
+ add: (data: {
1369
+ bundleVersionId: string;
1370
+ minimumTermMonths?: number;
1371
+ }) => Promise<SubscriptionBundleRecord>;
1372
+ cancel: (subscriptionBundleId: string, opts?: {
1373
+ canceledAt?: string;
1374
+ }) => Promise<SubscriptionBundleRecord>;
1375
+ }
1376
+ declare class TenantSubscriptionBundlesApiError extends Error {
1377
+ readonly status: number;
1378
+ readonly body: unknown;
1379
+ constructor(status: number, body: unknown, message: string);
1380
+ }
1381
+ declare function useTenantSubscriptionBundles(options: UseTenantSubscriptionBundlesOptions): UseTenantSubscriptionBundlesResult;
1382
+
1383
+ interface UseBusinessTypesOptions {
1384
+ adminEndpoint: string;
1385
+ http?: HttpClient;
1386
+ getAuthToken?: () => string | null;
1387
+ projectKey: string;
1388
+ autoLoad?: boolean;
1389
+ }
1390
+ declare class BusinessTypesApiError extends Error {
1391
+ readonly status: number;
1392
+ readonly body: unknown;
1393
+ constructor(status: number, body: unknown, message: string);
1394
+ }
1395
+ interface UseBusinessTypesResult {
1396
+ businessTypes: Ref<BusinessTypeRow[]>;
1397
+ loading: Ref<boolean>;
1398
+ error: Ref<Error | null>;
1399
+ load: () => Promise<void>;
1400
+ create: (data: CreateBusinessTypeData) => Promise<BusinessTypeRow>;
1401
+ update: (businessTypeId: string, data: UpdateBusinessTypeData) => Promise<BusinessTypeRow>;
1402
+ softDelete: (businessTypeId: string) => Promise<void>;
1403
+ }
1404
+ declare function useBusinessTypes(options: UseBusinessTypesOptions): UseBusinessTypesResult;
1405
+ interface UseBusinessTypeVersionsOptions {
1406
+ adminEndpoint: string;
1407
+ businessTypeId: string;
1408
+ http?: HttpClient;
1409
+ getAuthToken?: () => string | null;
1410
+ autoLoad?: boolean;
1411
+ }
1412
+ interface UseBusinessTypeVersionsResult {
1413
+ versions: Ref<BusinessTypeVersionRow[]>;
1414
+ loading: Ref<boolean>;
1415
+ error: Ref<Error | null>;
1416
+ load: () => Promise<void>;
1417
+ createDraft: (data: Omit<CreateBusinessTypeVersionDraftData, 'businessTypeId'>) => Promise<BusinessTypeVersionMutationResult>;
1418
+ updateDraft: (versionId: string, data: UpdateBusinessTypeVersionDraftData) => Promise<BusinessTypeVersionMutationResult>;
1419
+ publish: (versionId: string, opts?: {
1420
+ forceRegressive?: boolean;
1421
+ }) => Promise<BusinessTypeVersionMutationResult>;
1422
+ }
1423
+ declare function useBusinessTypeVersions(options: UseBusinessTypeVersionsOptions): UseBusinessTypeVersionsResult;
1424
+
1425
+ interface UseMarketingProjectionsOptions {
1426
+ adminEndpoint: string;
1427
+ http?: HttpClient;
1428
+ getAuthToken?: () => string | null;
1429
+ /** Filter that is active on `load()`. Can be changed via `setFilter()`. */
1430
+ filter: MarketingProjectionFilter;
1431
+ autoLoad?: boolean;
1432
+ }
1433
+ declare class MarketingProjectionsApiError extends Error {
1434
+ readonly status: number;
1435
+ readonly body: unknown;
1436
+ constructor(status: number, body: unknown, message: string);
1437
+ }
1438
+ interface UseMarketingProjectionsResult {
1439
+ projections: Ref<MarketingProjectionRow[]>;
1440
+ filter: Ref<MarketingProjectionFilter>;
1441
+ loading: Ref<boolean>;
1442
+ error: Ref<Error | null>;
1443
+ /** Changes the filter and reloads fresh. */
1444
+ setFilter: (next: MarketingProjectionFilter) => Promise<void>;
1445
+ load: () => Promise<void>;
1446
+ create: (data: CreateMarketingProjectionData) => Promise<MarketingProjectionRow>;
1447
+ update: (id: string, data: UpdateMarketingProjectionData) => Promise<MarketingProjectionRow>;
1448
+ remove: (id: string) => Promise<void>;
1449
+ }
1450
+ declare function useMarketingProjections(options: UseMarketingProjectionsOptions): UseMarketingProjectionsResult;
1451
+
1452
+ interface UsePromotionsOptions {
1453
+ adminEndpoint: string;
1454
+ http?: HttpClient;
1455
+ getAuthToken?: () => string | null;
1456
+ projectKey: string;
1457
+ autoLoad?: boolean;
1458
+ }
1459
+ declare class PromotionsApiError extends Error {
1460
+ readonly status: number;
1461
+ readonly body: unknown;
1462
+ constructor(status: number, body: unknown, message: string);
1463
+ }
1464
+ interface UsePromotionsResult {
1465
+ promotions: Ref<PromotionRow[]>;
1466
+ loading: Ref<boolean>;
1467
+ error: Ref<Error | null>;
1468
+ load: () => Promise<void>;
1469
+ create: (data: CreatePromotionData) => Promise<PromotionRow>;
1470
+ update: (id: string, data: UpdatePromotionData) => Promise<PromotionRow>;
1471
+ remove: (id: string) => Promise<void>;
1472
+ }
1473
+ declare function usePromotions(options: UsePromotionsOptions): UsePromotionsResult;
1474
+
1475
+ interface UsePlansOptions {
1476
+ /**
1477
+ * Fully-qualified admin endpoint prefix incl. the app globalPrefix
1478
+ * (`/api/admin`, `/api/v1/admin`, …). Required.
1479
+ */
1480
+ adminEndpoint: string;
1481
+ http?: HttpClient;
1482
+ getAuthToken?: () => string | null;
1483
+ /** Required: projectKey the list is filtered against. */
1484
+ projectKey: string;
1485
+ /** With `true`, loads on composable init. Default `false`. */
1486
+ autoLoad?: boolean;
1487
+ }
1488
+ declare class PlansApiError extends Error {
1489
+ readonly status: number;
1490
+ readonly body: unknown;
1491
+ constructor(status: number, body: unknown, message: string);
1492
+ }
1493
+ interface UsePlansResult {
1494
+ plans: Ref<PlanRow[]>;
1495
+ loading: Ref<boolean>;
1496
+ error: Ref<Error | null>;
1497
+ /**
1498
+ * planKey → number of active (ACTIVE/TRIAL) subscriptions, across
1499
+ * versions and tenants. Plans without a subscription are absent from the
1500
+ * map (default 0 on read). Populated by `loadTenantCounts()`.
1501
+ */
1502
+ tenantCountsByPlanKey: Ref<Record<string, number>>;
1503
+ load: () => Promise<void>;
1504
+ /**
1505
+ * Loads the platform-wide tenant counters
1506
+ * (`GET /admin/catalog/plans/tenant-counts?projectKey=…`) and writes them
1507
+ * into `tenantCountsByPlanKey`. Best-effort: errors are swallowed
1508
+ * (empty map), since the counters are only decorative in the plan overview.
1509
+ */
1510
+ loadTenantCounts: () => Promise<void>;
1511
+ create: (data: CreatePlanData) => Promise<PlanRow>;
1512
+ update: (planId: string, data: UpdatePlanData) => Promise<PlanRow>;
1513
+ softDelete: (planId: string) => Promise<void>;
1514
+ /**
1515
+ * Hard delete (`DELETE /admin/catalog/plans/:id/purge`). Only allowed
1516
+ * for plans without PlanVersions — otherwise the backend responds with
1517
+ * 422 `PLAN_HAS_VERSIONS`. Also removes the plan from `plans`.
1518
+ */
1519
+ hardDelete: (planId: string) => Promise<void>;
1520
+ }
1521
+ declare function usePlans(options: UsePlansOptions): UsePlansResult;
1522
+ interface UsePlanVersionsOptions {
1523
+ adminEndpoint: string;
1524
+ /** UUID of the plan root (Plan.id), not the planKey. */
1525
+ planId: string;
1526
+ http?: HttpClient;
1527
+ getAuthToken?: () => string | null;
1528
+ autoLoad?: boolean;
1529
+ }
1530
+ interface UsePlanVersionsResult {
1531
+ versions: Ref<PlanVersionRow$1[]>;
1532
+ loading: Ref<boolean>;
1533
+ error: Ref<Error | null>;
1534
+ load: () => Promise<void>;
1535
+ /**
1536
+ * The caller does not need to set `data.planId` — it comes from the
1537
+ * composable options (`adminEndpoint/catalog/plans/:id/versions`).
1538
+ */
1539
+ createDraft: (data: Omit<CreatePlanVersionDraftData, 'planId'>) => Promise<PlanVersionMutationResult>;
1540
+ updateDraft: (versionId: string, data: UpdatePlanVersionDraftData) => Promise<PlanVersionMutationResult>;
1541
+ publish: (versionId: string, opts?: {
1542
+ forceRegressive?: boolean;
1543
+ allowZeroPrice?: boolean;
1544
+ validFrom?: string | null;
1545
+ validUntil?: string | null;
1546
+ }) => Promise<PlanVersionMutationResult>;
1547
+ /**
1548
+ * Discards a draft (`DELETE /admin/catalog/plan-versions/:id`).
1549
+ * Published versions cannot be discarded — the API responds with
1550
+ * 422 and code `PLAN_VERSION_ALREADY_PUBLISHED`.
1551
+ */
1552
+ discardDraft: (versionId: string) => Promise<void>;
1553
+ /**
1554
+ * Terminates a live PlanVersion with `endsAt` (without a successor
1555
+ * version). Idempotent — a second call with a different date overwrites.
1556
+ */
1557
+ terminateVersion: (versionId: string, endsAt: string) => Promise<PlanVersionRow$1>;
1558
+ }
1559
+ declare function usePlanVersions(options: UsePlanVersionsOptions): UsePlanVersionsResult;
1560
+
1561
+ interface UseLivePlanVersionsOptions {
1562
+ adminEndpoint: string;
1563
+ /** Reactive list of plan stems — the watcher reloads when the list changes. */
1564
+ plans: Ref<PlanRow[]>;
1565
+ http?: HttpClient;
1566
+ getAuthToken?: () => string | null;
1567
+ }
1568
+ interface UseLivePlanVersionsResult {
1569
+ /** `planKey → live PlanVersion` (or null when the plan has no live version). */
1570
+ livePlanVersions: Ref<Record<string, PlanVersionRow$1 | null>>;
1571
+ loading: Ref<boolean>;
1572
+ error: Ref<Error | null>;
1573
+ /** Forces a reload of all live versions. */
1574
+ refresh: () => Promise<void>;
1575
+ }
1576
+ declare function useLivePlanVersions(options: UseLivePlanVersionsOptions): UseLivePlanVersionsResult;
1577
+
1578
+ interface UseManifestResult {
1579
+ manifest: Ref<AdminManifest | null>;
1580
+ loading: Ref<boolean>;
1581
+ error: Ref<Error | null>;
1582
+ /** Loads once (or returns the cache); does not abort on re-call. */
1583
+ load: () => Promise<void>;
1584
+ /** Discards the cache and loads fresh (e.g. after manifest/reload). */
1585
+ reload: () => Promise<void>;
1586
+ /** Clears the cache — e.g. on logout. */
1587
+ clearCache: () => void;
1588
+ }
1589
+ declare function useManifest(options: ManifestLoaderOptions): UseManifestResult;
1590
+
1591
+ interface UseNavResult {
1592
+ /** List of all routes available via the manifest — filtered by capabilities. */
1593
+ routes: ComputedRef<BuildRouteEntry[]>;
1594
+ /** Drawer items, grouped by navSection. */
1595
+ sidebar: ComputedRef<SidebarSection[]>;
1596
+ }
1597
+ declare function useNav(manifest: Ref<AdminManifest | null>, options?: NavBuilderOptions): UseNavResult;
1598
+
1599
+ interface UseActionsResult {
1600
+ registry: ComputedRef<ActionRegistry | null>;
1601
+ }
1602
+ declare function useActions(manifest: Ref<AdminManifest | null>, actions: Record<string, ActionHandler>): UseActionsResult;
1603
+
1604
+ /**
1605
+ * Input the app-side handler receives. `mfaCode` is populated when
1606
+ * `def.requiresMfa === true` AND the provider supplies a code.
1607
+ * `reason` comes from the confirm provider (typed-slug / typed-production
1608
+ * typically confirm a slug entry as a safety check).
1609
+ */
1610
+ interface TenantActionInput<TRow extends TenantDto = TenantDto> {
1611
+ row: TRow;
1612
+ mfaCode: string | null;
1613
+ reason: string | null;
1614
+ /**
1615
+ * App-specific extra inputs that the confirm provider captures and
1616
+ * passes through to the handler (e.g. `until` for `pilots.extend` with
1617
+ * `confirmType: 'date'`). If the confirm provider supplies no `extras`,
1618
+ * this is an empty object.
1619
+ */
1620
+ extras: Record<string, unknown>;
1621
+ }
1622
+ /** Provider contract for UI dialogs — apps supply the Quasar/headless implementation. */
1623
+ interface TenantActionFlowProviders<TRow extends TenantDto = TenantDto> {
1624
+ /**
1625
+ * App-specific confirm UI. Invoked when `def.confirmType !== 'none'`.
1626
+ * Returns `{ ok: false }` when the user cancels. Optionally `extras`
1627
+ * may be returned — that ends up in the handler's
1628
+ * `TenantActionInput.extras` (e.g. `{ until: ISO }` for
1629
+ * `confirmType: 'date'`).
1630
+ */
1631
+ confirm?: (def: TenantActionDef, ctx: {
1632
+ row: TRow;
1633
+ }) => Promise<{
1634
+ ok: boolean;
1635
+ reason?: string | null;
1636
+ extras?: Record<string, unknown>;
1637
+ }>;
1638
+ /**
1639
+ * App-specific MFA UI. Invoked when `def.requiresMfa`. Returns `null`
1640
+ * when the user cancels. Otherwise the TOTP code that must be attached
1641
+ * to the backend via the `X-Mfa-Code` header.
1642
+ */
1643
+ mfa?: (def: TenantActionDef, ctx: {
1644
+ row: TRow;
1645
+ }) => Promise<string | null>;
1646
+ /** Optional: success/error notify (toast/snackbar). */
1647
+ notify?: (kind: 'positive' | 'negative', message: string) => void;
1648
+ /** Optional: hook after a successful dispatch (e.g. reload the list). */
1649
+ onSuccess?: (def: TenantActionDef, ctx: {
1650
+ row: TRow;
1651
+ }) => void;
1652
+ /**
1653
+ * Optional: row-specific visibility. Evaluated after the capability and
1654
+ * handler checks — `false` hides the action for this row (e.g.
1655
+ * `tenants.suspend` only for active tenants).
1656
+ * Default: `true` (do not hide).
1657
+ */
1658
+ visibleForRow?: (def: TenantActionDef, row: TRow) => boolean;
1659
+ }
1660
+ interface ResolvedTenantAction<TRow extends TenantDto = TenantDto> {
1661
+ def: TenantActionDef;
1662
+ /**
1663
+ * Starts the flow: confirm → MFA → handler. Returns the handler result
1664
+ * or `undefined` when the user cancelled the flow.
1665
+ */
1666
+ invoke: (row: TRow) => Promise<unknown>;
1667
+ }
1668
+ interface UseTenantActionFlowResult<TRow extends TenantDto = TenantDto> {
1669
+ registry: ComputedRef<ActionRegistry | null>;
1670
+ /**
1671
+ * Row-independent action list, filtered by **static** criteria:
1672
+ * 1. Capability gate: `requiredCapability` must not be explicitly
1673
+ * `false` in the manifest.
1674
+ * 2. Handler gate: for every `actionKey` a handler must be registered
1675
+ * in the `actions:` map (otherwise a ghost button).
1676
+ *
1677
+ * `visibleForRow` is NOT evaluated here — the list is the basis from
1678
+ * which pages derive their action descriptors. Row-specific visibility
1679
+ * only comes in via `actionsForRow(row)` or via the `condition()` of the
1680
+ * platform page buttons.
1681
+ *
1682
+ * This makes sample-row-based UI constructions work again: a page can
1683
+ * walk `availableActions.value` and prepare buttons without
1684
+ * `tenants.reactivate` disappearing for `isActive: true` sample rows.
1685
+ */
1686
+ availableActions: ComputedRef<TenantActionDef[]>;
1687
+ /**
1688
+ * Returns the actions visible for `row`. In addition to the
1689
+ * capability/handler checks (see `availableActions`), it runs the
1690
+ * `visibleForRow` provider — i.e. everything that depends on the row
1691
+ * (e.g. `tenants.suspend` only for active tenants).
1692
+ */
1693
+ actionsForRow: (row: TRow) => ResolvedTenantAction<TRow>[];
1694
+ /** Drift diagnostics: declared actions without a registered handler. */
1695
+ orphanedDefs: ComputedRef<string[]>;
1696
+ }
1697
+ /**
1698
+ * Vue composable. Uses:
1699
+ * 1. `useSuperAdminActions()` as the source of the handler map (the app
1700
+ * supplies it via `createSuperAdminApp({ actions: { [actionKey]: handler } })`).
1701
+ * 2. The provided manifest ref, to react to manifest reloads.
1702
+ *
1703
+ * Returns an `actionsForRow(row)` function that is rendered directly in the
1704
+ * page inside `<button v-for="...">`. The `invoke` call orchestrates
1705
+ * confirm + MFA + dispatch automatically.
1706
+ */
1707
+ declare function useTenantActionFlow<TRow extends TenantDto = TenantDto>(manifest: Ref<AdminManifest | null>, providers?: TenantActionFlowProviders<TRow>): UseTenantActionFlowResult<TRow>;
1708
+
1709
+ type TenantRowLike = TenantDto & Record<string, unknown>;
1710
+ interface PlatformTenantActionTone {
1711
+ tone: 'positive' | 'negative' | 'primary' | 'warning' | 'accent';
1712
+ }
1713
+ interface PlatformTenantActionRow<TRow extends TenantRowLike> {
1714
+ id: string;
1715
+ label: string;
1716
+ icon: string;
1717
+ tone: PlatformTenantActionTone['tone'];
1718
+ actionKey: string;
1719
+ condition: (row: TRow) => boolean;
1720
+ handler: (row: TRow) => void;
1721
+ }
1722
+ interface PlatformTenantActionsOptions<TRow extends TenantRowLike> {
1723
+ /** Reactive manifest source — usually `storeToRefs(useManifestStore()).manifest`. */
1724
+ manifest: Ref<AdminManifest | null>;
1725
+ /** Notify provider (toast/snackbar). */
1726
+ notify: (kind: 'positive' | 'negative', message: string) => void;
1727
+ /** Success hook after a successful dispatch (e.g. reload the list). */
1728
+ onSuccess: () => Promise<void> | void;
1729
+ /** Row-specific visibility. Default: suspend only for active, reactivate only for inactive tenants. */
1730
+ visibleForRow?: (def: TenantActionDef, row: TRow) => boolean;
1731
+ /** MFA dialog description. Default: `"{def.label} — Tenant „{row.name}". TOTP-Code aus Authenticator eingeben."`. */
1732
+ mfaDescription?: (def: TenantActionDef, row: TRow) => string;
1733
+ /** Manifest action → icon. Default: suspend→block, reactivate→play_arrow, grant→star, revoke→star_outline, impersonate→switch_account, export→download, otherwise→bolt. */
1734
+ iconForActionKey?: (actionKey: string) => string;
1735
+ /** Manifest action → tone. Default: suspend/revoke→negative, reactivate/grant→positive, otherwise→primary. */
1736
+ toneForActionKey?: (actionKey: string) => PlatformTenantActionTone['tone'];
1737
+ }
1738
+ interface MfaDialogState {
1739
+ show: boolean;
1740
+ description: string;
1741
+ error: string;
1742
+ }
1743
+ interface ConfirmDialogState<TRow extends TenantRowLike> {
1744
+ show: boolean;
1745
+ def: TenantActionDef | null;
1746
+ row: TRow | null;
1747
+ }
1748
+ interface PlatformTenantActionsResult<TRow extends TenantRowLike> {
1749
+ /** Reactive list of fully configured row actions — pass straight to the page action renderer. */
1750
+ manifestActions: ComputedRef<PlatformTenantActionRow<TRow>[]>;
1751
+ /** Manifest drift: declared actions without a handler (Capability=false filtered out). */
1752
+ realOrphans: ComputedRef<string[]>;
1753
+ mfa: Ref<MfaDialogState>;
1754
+ onMfaConfirm: (code: string) => void;
1755
+ onMfaDialogVisibility: (open: boolean) => void;
1756
+ confirmDialog: Ref<ConfirmDialogState<TRow>>;
1757
+ onConfirmSubmit: (payload: {
1758
+ reason: string | null;
1759
+ extras?: Record<string, unknown>;
1760
+ }) => void;
1761
+ onConfirmCancel: () => void;
1762
+ onConfirmDialogVisibility: (open: boolean) => void;
1763
+ }
1764
+ /**
1765
+ * Default helper for apps that want to override `iconForActionKey`
1766
+ * themselves but use the platform defaults as a fallback.
1767
+ */
1768
+ declare function defaultIconForActionKey(actionKey: string): string;
1769
+ /**
1770
+ * Default helper for apps that want to override `toneForActionKey`
1771
+ * themselves but use the platform defaults as a fallback.
1772
+ */
1773
+ declare function defaultToneForActionKey(actionKey: string): PlatformTenantActionTone['tone'];
1774
+ declare function usePlatformTenantActions<TRow extends TenantRowLike>(options: PlatformTenantActionsOptions<TRow>): PlatformTenantActionsResult<TRow>;
1775
+
1776
+ interface UseBatchColumnsResult {
1777
+ /** Data per column key (`columnKey → tenantId → value`). */
1778
+ data: Ref<BatchColumnData>;
1779
+ loading: Ref<boolean>;
1780
+ error: Ref<Error | null>;
1781
+ /** Manual re-fetch (e.g. after a mutation). */
1782
+ reload: () => Promise<void>;
1783
+ }
1784
+ declare function useBatchColumns(manifest: Ref<AdminManifest | null>, tenantIds: Ref<string[]>, options?: BatchColumnFetcherOptions): UseBatchColumnsResult;
1785
+
1786
+ type SnapshotKind = 'drafts' | 'active' | 'historical';
1787
+ interface CatalogSnapshot<P extends PlanVersionRow$1 = PlanVersionRow$1> {
1788
+ id: string;
1789
+ kind: SnapshotKind;
1790
+ status: 'DRAFT' | 'ACTIVE' | 'ARCHIVED';
1791
+ label: string;
1792
+ title: string;
1793
+ description: string;
1794
+ asOf: string | null;
1795
+ createdAt: string | null;
1796
+ publishedAt: string | null;
1797
+ authorEmail: string | null;
1798
+ plans: ResolvedPlan<P>[];
1799
+ /** Number of open drafts for the `drafts` snapshot, otherwise 0. */
1800
+ draftCount: number;
1801
+ /** Number of entities in this snapshot whose publication flagged at least
1802
+ * one regression (`nonRegressive === false`). */
1803
+ regressionCount: number;
1804
+ }
1805
+ interface ResolvedPlan<P extends PlanVersionRow$1 = PlanVersionRow$1> {
1806
+ /** Which PlanVersionRow was selected to represent this slot. */
1807
+ source: P;
1808
+ /** Live predecessor (only set when `source` is a DRAFT; otherwise null). */
1809
+ liveBase: P | null;
1810
+ isDraft: boolean;
1811
+ planId: string;
1812
+ features: string[];
1813
+ /** Quota map (from `source.quotas` or legacy fields; empty if none). */
1814
+ quotas: Record<string, number>;
1815
+ monthlyNet: number;
1816
+ yearlyNet: number;
1817
+ marketed: boolean;
1818
+ version: number;
1819
+ /** @deprecated Read from `quotas['users']`. */
1820
+ maxUsers?: number;
1821
+ /** @deprecated Legacy field; read from `quotas['vehicles']`. */
1822
+ maxVehicles?: number;
1823
+ /** @deprecated Read from `quotas['storageGb']`. */
1824
+ maxStorageGb?: number;
1825
+ }
1826
+ interface RawCatalogData<P extends PlanVersionRow$1 = PlanVersionRow$1> {
1827
+ planVersions: P[];
1828
+ }
1829
+ interface BuildSnapshotsOptions {
1830
+ /**
1831
+ * App-specific plan order, e.g. `['BASIC', 'STANDARD',
1832
+ * 'PROFESSIONAL', 'BUSINESS', 'ENTERPRISE']`. Plan IDs outside the list
1833
+ * end up sorted alphabetically at the back.
1834
+ */
1835
+ planSortOrder?: readonly string[];
1836
+ }
1837
+ declare function buildSnapshots<P extends PlanVersionRow$1>(data: RawCatalogData<P>, options?: BuildSnapshotsOptions): CatalogSnapshot<P>[];
1838
+ declare function listOpenDrafts<P extends PlanVersionRow$1>(data: RawCatalogData<P>): {
1839
+ plans: P[];
1840
+ };
1841
+
1842
+ interface PilotCreatePayload {
1843
+ tenant: {
1844
+ name: string;
1845
+ slug?: string;
1846
+ legalForm?: string;
1847
+ vatId?: string;
1848
+ };
1849
+ admin: {
1850
+ email: string;
1851
+ firstName: string;
1852
+ lastName: string;
1853
+ initialPassword?: string;
1854
+ };
1855
+ pilot: {
1856
+ plan: string;
1857
+ note?: string;
1858
+ endsAt?: string;
1859
+ };
1860
+ }
1861
+ interface PilotCreateResult {
1862
+ slug: string;
1863
+ /** If the server generated an initial password, include it here. */
1864
+ initialPassword?: string;
1865
+ }
1866
+ /** Edit an existing pilot subscription (plan, end date, note). */
1867
+ interface PilotEditPayload {
1868
+ /** Optional — only the fields that are set get changed. */
1869
+ plan?: string;
1870
+ /** `null` clears the end date (open-ended pilot phase). */
1871
+ endsAt?: string | null;
1872
+ /** `null` or empty clears the note. */
1873
+ note?: string | null;
1874
+ }
1875
+ interface PilotEditResult {
1876
+ slug: string;
1877
+ changed?: string[];
1878
+ }
1879
+ /**
1880
+ * Tenant-specific vocabulary for the pilot dialogs. The platform
1881
+ * provides neutral defaults ("Mandant"); consumers override with
1882
+ * their domain language (e.g. "Verein" or "Händler").
1883
+ */
1884
+ interface PilotCopy {
1885
+ /** Subtitle of the tenant section in the create dialog. */
1886
+ tenantSubtitle?: string;
1887
+ /** Label of the name field, e.g. "Vereinsname" / "Firmenname". */
1888
+ tenantNameLabel?: string;
1889
+ /** Placeholder for the tenant name. */
1890
+ tenantNamePlaceholder?: string;
1891
+ /** Placeholder for the slug. */
1892
+ slugPlaceholder?: string;
1893
+ /** Placeholder for the initial admin email. */
1894
+ adminEmailPlaceholder?: string;
1895
+ /** Placeholder for the internal note (create + edit). */
1896
+ notePlaceholder?: string;
1897
+ }
1898
+ /** Neutral, tenant-agnostic defaults for {@link PilotCopy}. */
1899
+ declare const DEFAULT_PILOT_COPY: Required<PilotCopy>;
1900
+ type PromoCodeValueType = 'PERCENT' | 'ABSOLUTE';
1901
+ type PromoCodeDurationType = 'ONCE' | 'MONTHS' | 'BILLING_CYCLES';
1902
+ interface PromoCodeCreatePayload {
1903
+ code: string;
1904
+ valueType: PromoCodeValueType;
1905
+ value: number;
1906
+ durationType: PromoCodeDurationType;
1907
+ durationValue: number | null;
1908
+ maxRedemptions: number | null;
1909
+ validFrom: string | null;
1910
+ validUntil: string | null;
1911
+ /** Plan keys the code applies to. Empty = all plans. */
1912
+ appliesToPlans?: string[];
1913
+ /** Optional: filter on MONTHLY/YEARLY subscriptions. */
1914
+ appliesToBilling?: 'MONTHLY' | 'YEARLY';
1915
+ /** Only redeemable by new customers. */
1916
+ firstTimeCustomersOnly?: boolean;
1917
+ /** Minimum gross plan amount from which the code takes effect. */
1918
+ minimumPlanAmountGross?: number;
1919
+ /** Allows €0 invoices (otherwise the discount is capped at 100% of the amount). */
1920
+ allowZeroInvoice?: boolean;
1921
+ /** Ledger account for discount revenue reduction (app-specific). */
1922
+ revenueDeductionAccount?: string;
1923
+ campaignTag?: string;
1924
+ description?: string;
1925
+ }
1926
+ /** Plan option for the plan picker in PromoCodeCreateDialog. */
1927
+ interface PromoCodePlanOption {
1928
+ /** Plan key as sent to the backend (e.g. 'BASIC'). */
1929
+ key: string;
1930
+ /** Display label (e.g. 'Basic'). */
1931
+ label: string;
1932
+ /** Optional accent for the plan chip; fallback neutral gray. */
1933
+ color?: string;
1934
+ }
1935
+ /**
1936
+ * PATCH payload — all fields optional, only the explicitly set ones
1937
+ * are sent to the backend (whitelist on the server side). `code` is
1938
+ * not in the list because it stays stable after creation.
1939
+ */
1940
+ interface PromoCodeUpdatePayload {
1941
+ status?: 'ACTIVE' | 'PAUSED';
1942
+ valueType?: PromoCodeValueType;
1943
+ value?: number;
1944
+ durationType?: PromoCodeDurationType;
1945
+ durationValue?: number | null;
1946
+ maxRedemptions?: number | null;
1947
+ validFrom?: string | null;
1948
+ validUntil?: string | null;
1949
+ appliesToPlans?: string[];
1950
+ appliesToBilling?: 'MONTHLY' | 'YEARLY' | null;
1951
+ firstTimeCustomersOnly?: boolean;
1952
+ minimumPlanAmountGross?: number | null;
1953
+ allowZeroInvoice?: boolean;
1954
+ description?: string | null;
1955
+ campaignTag?: string | null;
1956
+ revenueDeductionAccount?: string | null;
1957
+ }
1958
+
1959
+ declare const CATALOG_DEFAULT_LOCALE = "de";
1960
+ /** Display mapping per feature key (compatible with `FeatureMeta`). */
1961
+ interface FeatureRegistryEntry {
1962
+ label?: string;
1963
+ group?: string;
1964
+ core?: boolean;
1965
+ }
1966
+ /** Display mapping per quota key. */
1967
+ interface QuotaMeta {
1968
+ label?: string;
1969
+ unit?: string;
1970
+ }
1971
+ /**
1972
+ * Builds the `featureRegistry` for the bundle editors from the feature catalog
1973
+ * entries. Keys without a resolvable label are omitted — the editor then falls
1974
+ * back to the feature key.
1975
+ */
1976
+ declare function buildFeatureRegistry(featureCatalog: FeatureCatalogEntryRow[], locale: string): Record<string, FeatureRegistryEntry>;
1977
+ /**
1978
+ * Builds the `quotaRegistry` for the bundle editors from the quota catalog
1979
+ * entries. Label and unit are resolved independently; if a translation is
1980
+ * missing, the editor falls back to the Discovery value or the quota key.
1981
+ */
1982
+ declare function buildQuotaRegistry(quotaCatalog: QuotaCatalogEntryRow[], locale: string): Record<string, QuotaMeta>;
1983
+
1984
+ interface PlatformEmailProvider {
1985
+ id: string;
1986
+ name: string;
1987
+ smtpHost: string;
1988
+ smtpPort: number;
1989
+ smtpUser: string;
1990
+ encryption: string;
1991
+ autoTls?: boolean;
1992
+ fromEmail: string;
1993
+ fromName?: string | null;
1994
+ isDefault: boolean;
1995
+ active: boolean;
1996
+ [extra: string]: unknown;
1997
+ }
1998
+ interface PlatformEmailWriteInput {
1999
+ name: string;
2000
+ smtpHost: string;
2001
+ smtpPort: number;
2002
+ smtpUser: string;
2003
+ smtpPassword?: string;
2004
+ encryption: string;
2005
+ fromEmail: string;
2006
+ fromName?: string;
2007
+ active?: boolean;
2008
+ }
2009
+ interface PlatformEmailTestInput {
2010
+ toEmail: string;
2011
+ subject?: string;
2012
+ }
2013
+ interface PlatformEmailTestResult {
2014
+ success: boolean;
2015
+ message: string;
2016
+ }
2017
+
2018
+ type EmailHistoryStatus = 'PENDING' | 'SENT' | 'FAILED' | 'BOUNCED';
2019
+ /** List projection — deliberately without body (loaded only in the detail view). */
2020
+ interface EmailHistoryRow {
2021
+ id: string;
2022
+ fromEmail: string;
2023
+ toEmail: string;
2024
+ subject: string;
2025
+ status: EmailHistoryStatus;
2026
+ sentAt?: string | null;
2027
+ createdAt: string;
2028
+ }
2029
+ /** Complete entry including content, headers, SMTP response and errors. */
2030
+ interface EmailHistoryDetail extends EmailHistoryRow {
2031
+ ccEmail?: string | null;
2032
+ bccEmail?: string | null;
2033
+ bodyHtml?: string | null;
2034
+ bodyText?: string | null;
2035
+ errorMessage?: string | null;
2036
+ smtpResponse?: string | null;
2037
+ }
2038
+ /** Search/filter/pagination input — field names as in the backend (QueryEmailLogDto). */
2039
+ interface EmailHistoryFilter {
2040
+ search?: string;
2041
+ status?: EmailHistoryStatus;
2042
+ from?: string;
2043
+ to?: string;
2044
+ page?: number;
2045
+ limit?: number;
2046
+ }
2047
+ interface EmailHistoryListResult {
2048
+ rows: EmailHistoryRow[];
2049
+ total: number;
2050
+ }
2051
+ interface EmailHistoryResendResult {
2052
+ success: boolean;
2053
+ message?: string;
2054
+ }
2055
+
2056
+ /**
2057
+ * App-specific branding data that the platform `AdminLayout` and other
2058
+ * consumers read via `useSuperAdminBrand()`.
2059
+ */
2060
+ interface SuperAdminBrand {
2061
+ /** 2-letter abbreviation in the logo badge (`'ma'`, `'da'`, …). */
2062
+ logoText: string;
2063
+ /** Full display name (`'DemoApp'`, `'ClubApp'`, …). */
2064
+ name: string;
2065
+ /** Optional: tag to the right of the name, default `'SuperAdmin'`. */
2066
+ tag?: string;
2067
+ }
2068
+ /**
2069
+ * Endpoint configuration. `apiBase` is the shared prefix under which
2070
+ * `/manifest`, `/boot` and extra routes live.
2071
+ */
2072
+ interface SuperAdminEndpoints {
2073
+ /** Shared prefix, e.g. `'/api/admin'` or `'/api/v1/admin'`. */
2074
+ apiBase: string;
2075
+ /** Pre-login branding endpoint, default `${apiBase}/boot`. */
2076
+ publicBootEndpoint?: string;
2077
+ /** Post-login full-manifest endpoint, default `${apiBase}/manifest`. */
2078
+ manifestEndpoint?: string;
2079
+ }
2080
+ type ExtensionLoader = () => Promise<Component | {
2081
+ default: Component;
2082
+ }>;
2083
+ type ExtensionsMap = Record<ComponentKey, ExtensionLoader>;
2084
+ type ActionsMap = Record<ActionKey, ActionHandler>;
2085
+ interface SuperAdminAuthGuardOptions {
2086
+ /** App provides: is the user currently logged in? */
2087
+ isAuthenticated: () => boolean;
2088
+ /** App provides: does the user have the SuperAdmin role? Default: only check `isAuthenticated`. */
2089
+ isSuperAdmin?: () => boolean;
2090
+ /** App provides: redirect path for unauthenticated calls (e.g. `'/login'`). */
2091
+ onUnauthenticated: () => string;
2092
+ }
2093
+ /**
2094
+ * Result of a login attempt. Apps pass this back from their auth store to the
2095
+ * platform LoginPage; the page renders an appropriate error message.
2096
+ *
2097
+ * `ok: true` → login succeeded, page redirects to `redirectAfterLogin`.
2098
+ * `ok: false` → page shows `message` or a translation derived from `code`.
2099
+ *
2100
+ * Known codes:
2101
+ * - `BAD_CREDENTIALS` — wrong email/password combination.
2102
+ * - `NOT_SUPER_ADMIN` — account does not have the SuperAdmin role.
2103
+ * - otherwise — app-specific; `message` is displayed directly.
2104
+ */
2105
+ type SuperAdminLoginResult = {
2106
+ ok: true;
2107
+ } | {
2108
+ ok: false;
2109
+ code?: 'BAD_CREDENTIALS' | 'NOT_SUPER_ADMIN' | string;
2110
+ message?: string;
2111
+ };
2112
+ /**
2113
+ * Login adapter. The app passes its auth-store call through here. The platform
2114
+ * LoginPage consumes it via `useSuperAdminLoginAdapter()`, without knowledge
2115
+ * of app-specific stores (Pinia, auth API routes, MFA hooks).
2116
+ */
2117
+ interface SuperAdminLoginAdapter {
2118
+ /**
2119
+ * Performs the login. The app store encapsulates the API call, token
2120
+ * storage, MFA hops etc.
2121
+ */
2122
+ login(email: string, password: string): Promise<SuperAdminLoginResult>;
2123
+ /**
2124
+ * Target route after a successful login. Default: `/admin/dashboard`
2125
+ * (platform convention for the standard pages — apps with a different
2126
+ * default mount override this here).
2127
+ */
2128
+ redirectAfterLogin?: string;
2129
+ /**
2130
+ * Optional: dev hint (test account), shown below the form. Deliberately
2131
+ * rendered only when `environment !== 'production'`.
2132
+ */
2133
+ devHint?: {
2134
+ email: string;
2135
+ password: string;
2136
+ };
2137
+ }
2138
+ interface SuperAdminManifestGuardOptions {
2139
+ /**
2140
+ * App provides: loads the manifest into the app store. The router guard
2141
+ * `await`s the promise before the route is resolved.
2142
+ *
2143
+ * **On loader error:** the promise REJECTS. The router guard catches the
2144
+ * rejection and decides depending on `errorRoute`:
2145
+ * - `errorRoute` set → redirect to this route (fail-closed).
2146
+ * - `errorRoute` not set → `console.error` + render allowed
2147
+ * (defensive default behavior; the app must render the manifest gap
2148
+ * itself).
2149
+ */
2150
+ ensureLoaded: () => Promise<void>;
2151
+ /**
2152
+ * Optional: read accessor on the loaded manifest. When set, it is exposed
2153
+ * via `provide(SUPER_ADMIN_MANIFEST_KEY)` — the `<ProjectPageHost>`
2154
+ * resolves manifest `projectPages` through it against the
2155
+ * `extensions:` map.
2156
+ */
2157
+ getManifest?: () => AdminManifest | null;
2158
+ /**
2159
+ * Optional: path that the router guard redirects to on a manifest load
2160
+ * error (fail-closed mode). The app must register the route in `appRoutes`
2161
+ * and mark it as `meta.public = true`, otherwise it runs through the
2162
+ * manifest guard again and produces a redirect loop.
2163
+ */
2164
+ errorRoute?: string;
2165
+ }
2166
+ interface CreateSuperAdminAppOptions {
2167
+ /** App root component (`App.vue`). */
2168
+ rootComponent: Component;
2169
+ /** App branding (logo, name). */
2170
+ brand: SuperAdminBrand;
2171
+ /** Endpoint configuration. */
2172
+ endpoints: SuperAdminEndpoints;
2173
+ /** App's own routes (login, standard pages, bundle pages, …). */
2174
+ appRoutes: RouteRecordRaw[];
2175
+ /**
2176
+ * Static `extensions:` map. Manifest `projectPages[].componentKey` is
2177
+ * looked up in it (see Spec §4.4).
2178
+ */
2179
+ extensions?: ExtensionsMap;
2180
+ /**
2181
+ * Static `actions:` map. Manifest `tenants.actions[].actionKey` is looked
2182
+ * up in it.
2183
+ */
2184
+ actions?: ActionsMap;
2185
+ /**
2186
+ * Optional: auth guard. When set, `router.beforeEach` is wired up
2187
+ * automatically — `to.meta.public === true` bypasses the guard.
2188
+ */
2189
+ authGuard?: SuperAdminAuthGuardOptions;
2190
+ /**
2191
+ * Optional: manifest guard. Runs after a successful auth guard, blocks the
2192
+ * render until the manifest is loaded (prevents sidebar flicker).
2193
+ */
2194
+ manifestGuard?: SuperAdminManifestGuardOptions;
2195
+ /**
2196
+ * Optional: login adapter. When set, it is exposed via `provide()` for the
2197
+ * shared `<SuperAdminLoginPage>` (from `pages-standard/`) — the app no
2198
+ * longer needs to hold its own LoginPage Vue component.
2199
+ */
2200
+ loginAdapter?: SuperAdminLoginAdapter;
2201
+ /**
2202
+ * Optional: router history variant, default `createWebHistory()`.
2203
+ * Apps with a subpath mount override this.
2204
+ */
2205
+ routerHistory?: RouterHistory;
2206
+ /**
2207
+ * Optional: Quasar configuration. Default loads `Notify`/`Dialog`/`Loading`
2208
+ * with the established consumer convention (`top-right`, 3 s).
2209
+ */
2210
+ quasarOptions?: Partial<QuasarPluginOptions>;
2211
+ /**
2212
+ * Optional: additional Vue plugins (e.g. an app's own NotificationCenter)
2213
+ * that are installed after the platform setup, before the mount.
2214
+ */
2215
+ installPlugins?: Array<(app: App) => void>;
2216
+ /**
2217
+ * Optional: `HttpClient` for all pre-login calls (boot, first-run setup).
2218
+ * Default `defaultHttpClient()` (= `fetch`). Consumers pass their own
2219
+ * variant through (auth header, baseURL, retry) — it then applies uniformly,
2220
+ * including for the setup wizard. Consumed via `useSuperAdminHttp()`.
2221
+ */
2222
+ http?: HttpClient;
2223
+ }
2224
+ interface SuperAdminAppHandle {
2225
+ app: App;
2226
+ router: Router;
2227
+ pinia: Pinia;
2228
+ /** Mounts the app on a selector. Returns the root component instance. */
2229
+ mount: (selector: string | Element) => ReturnType<App['mount']>;
2230
+ }
2231
+ /** Vue inject key for `useSuperAdminBrand()`. */
2232
+ declare const SUPER_ADMIN_BRAND_KEY: InjectionKey<SuperAdminBrand>;
2233
+ /** Vue inject key for `useSuperAdminEndpoints()`. */
2234
+ declare const SUPER_ADMIN_ENDPOINTS_KEY: InjectionKey<Required<SuperAdminEndpoints>>;
2235
+ /** Vue inject key for `useSuperAdminExtensions()`. */
2236
+ declare const SUPER_ADMIN_EXTENSIONS_KEY: InjectionKey<ExtensionsMap>;
2237
+ /** Vue inject key for `useSuperAdminActions()`. */
2238
+ declare const SUPER_ADMIN_ACTIONS_KEY: InjectionKey<ActionsMap>;
2239
+ /**
2240
+ * Vue inject key for the manifest accessor. Only provided when
2241
+ * `manifestGuard.getManifest` was passed to `createSuperAdminApp()` — the
2242
+ * `<ProjectPageHost>` needs it to resolve project pages.
2243
+ */
2244
+ declare const SUPER_ADMIN_MANIFEST_KEY: InjectionKey<() => AdminManifest | null>;
2245
+ /** Vue inject key for `useSuperAdminLoginAdapter()`. */
2246
+ declare const SUPER_ADMIN_LOGIN_ADAPTER_KEY: InjectionKey<SuperAdminLoginAdapter>;
2247
+ /** Vue inject key for `useSuperAdminHttp()` (pre-login HttpClient). */
2248
+ declare const SUPER_ADMIN_HTTP_KEY: InjectionKey<HttpClient>;
2249
+ /**
2250
+ * Universal bootstrap function for SuperAdmin apps. Replaces the `main.ts`
2251
+ * boilerplate duplicated per app today (Quasar + Pinia + Router + manifest
2252
+ * guard) and exposes the platform maps via `provide()` for downstream
2253
+ * components.
2254
+ */
2255
+ declare function createSuperAdminApp(options: CreateSuperAdminAppOptions): SuperAdminAppHandle;
2256
+ /**
2257
+ * Internal helper, exported for isolated unit tests of the navigation
2258
+ * behavior (auth redirect, manifest fail-closed path). Consumers should call
2259
+ * `createSuperAdminApp()`, not this helper directly.
2260
+ */
2261
+ declare function buildNavigationGuard(options: Pick<CreateSuperAdminAppOptions, 'authGuard' | 'manifestGuard'>): NavigationGuardWithThis<undefined> | null;
2262
+
2263
+ /** Returns the `extensions:` map registered in `createSuperAdminApp()`. */
2264
+ declare function useSuperAdminExtensions(): ExtensionsMap;
2265
+ /** Returns the `actions:` map registered in `createSuperAdminApp()`. */
2266
+ declare function useSuperAdminActions(): ActionsMap;
2267
+ /**
2268
+ * Returns the app branding. Throws when the component is rendered outside a
2269
+ * `createSuperAdminApp()` shell — that is a setup bug, not a runtime
2270
+ * problem.
2271
+ */
2272
+ declare function useSuperAdminBrand(): SuperAdminBrand;
2273
+ /** Returns the app endpoints (apiBase, publicBootEndpoint, manifestEndpoint). */
2274
+ declare function useSuperAdminEndpoints(): Required<SuperAdminEndpoints>;
2275
+ /**
2276
+ * Returns the manifest accessor that was provided via
2277
+ * `createSuperAdminApp({ manifestGuard: { getManifest } })`.
2278
+ * Returns `null` if the app did not pass an accessor — components
2279
+ * (e.g. `<ProjectPageHost>`) must handle the null case cleanly, because
2280
+ * the accessor is optional.
2281
+ */
2282
+ declare function useSuperAdminManifest(): AdminManifest | null;
2283
+ /**
2284
+ * Returns the login adapter the app registered via
2285
+ * `createSuperAdminApp({ loginAdapter })`. Throws when none was
2286
+ * set — the shared `<SuperAdminLoginPage>` needs it.
2287
+ */
2288
+ declare function useSuperAdminLoginAdapter(): SuperAdminLoginAdapter;
2289
+ /**
2290
+ * Returns the pre-login `HttpClient` (boot, first-run setup) the app registered
2291
+ * via `createSuperAdminApp({ http })`. Falls back to `defaultHttpClient()`
2292
+ * if none was provided (e.g. in isolated tests).
2293
+ */
2294
+ declare function useSuperAdminHttp(): HttpClient;
2295
+
2296
+ interface CreatePlatformLoadersOptions {
2297
+ /**
2298
+ * Same endpoint configuration as for `createSuperAdminApp()`.
2299
+ * `publicBootEndpoint` / `manifestEndpoint` are derived from `apiBase`
2300
+ * when not set explicitly.
2301
+ */
2302
+ endpoints: SuperAdminEndpoints;
2303
+ /** HTTP adapter. App-specific, because auth-header / base-URL conventions vary. */
2304
+ http: HttpClient;
2305
+ /** Storage for the `ManifestLoader` ETag cache. Defaults to `defaultKvStore()`. */
2306
+ storage?: KvStore;
2307
+ /**
2308
+ * Storage key prefix — consumers with multiple apps under one domain
2309
+ * set this to e.g. `'ma:'` or `'da:'` so the caches stay separate.
2310
+ * Only forwarded to the `ManifestLoader`.
2311
+ */
2312
+ storageKeyPrefix?: string;
2313
+ /** Auth-token provider for `Authorization: Bearer …` (ManifestLoader only). */
2314
+ getAuthToken?: () => string | null;
2315
+ }
2316
+ interface PlatformLoaders {
2317
+ bootLoader: BootLoader;
2318
+ manifestLoader: ManifestLoader;
2319
+ }
2320
+ /**
2321
+ * Builds `BootLoader` + `ManifestLoader` from a single endpoint constant.
2322
+ * Apps use the same constant for `createSuperAdminApp({ endpoints })`.
2323
+ */
2324
+ declare function createPlatformLoaders(options: CreatePlatformLoadersOptions): PlatformLoaders;
2325
+
2326
+ declare const ProjectPageHost: vue.DefineComponent<{}, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
2327
+ [key: string]: any;
2328
+ }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
2329
+ /**
2330
+ * Returns a Vue Router child route that is registered as a catch-all under
2331
+ * the app's `/admin` layout. Children defined statically in the app router
2332
+ * (e.g. `/admin/dashboard`) win, because
2333
+ * Vue Router 4 matches specific routes before wildcard children.
2334
+ *
2335
+ * Example (simplified):
2336
+ *
2337
+ * {
2338
+ * path: '/admin',
2339
+ * component: AdminLayout,
2340
+ * children: [
2341
+ * { path: '', redirect: '/admin/dashboard' },
2342
+ * { path: 'dashboard', component: DashboardPage },
2343
+ * // ...more static children
2344
+ * createProjectPageHostRoute(),
2345
+ * ],
2346
+ * }
2347
+ */
2348
+ declare function createProjectPageHostRoute(options?: {
2349
+ /**
2350
+ * Path pattern of the catch-all route. Default `:projectPagePath(.+)`, so
2351
+ * that `/admin` and `/admin/` are not swallowed by the ProjectPageHost,
2352
+ * but the dashboard redirect child can take effect.
2353
+ */
2354
+ path?: string;
2355
+ }): RouteRecordRaw;
2356
+
2357
+ interface ManifestStoreState {
2358
+ manifest: Ref<AdminManifest | null>;
2359
+ loading: Ref<boolean>;
2360
+ error: Ref<Error | null>;
2361
+ loaded: Ref<boolean>;
2362
+ }
2363
+ interface ManifestStoreActions {
2364
+ /**
2365
+ * Loads once per session. Concurrent calls share the promise.
2366
+ *
2367
+ * **Behavior on loader error:** the store caches the error in
2368
+ * `error.value` and resets `loaded = false`/`manifest = null`, and the
2369
+ * promise REJECTS with the original error. Callers must either
2370
+ * call `.catch(...)` (defensive behavior) or let the rejection
2371
+ * propagate. The platform router guard
2372
+ * (`createSuperAdminApp({ manifestGuard.errorRoute })`) provides a
2373
+ * fail-closed path with a redirect to a dedicated error route.
2374
+ */
2375
+ ensureLoaded: () => Promise<void>;
2376
+ /** Discards cache + state (logout path). */
2377
+ clearCache: () => void;
2378
+ /** Forces a server refresh (e.g. after `manifest reload`). */
2379
+ reload: () => Promise<void>;
2380
+ }
2381
+ interface CreateManifestStoreOptions {
2382
+ /** Platform `ManifestLoader` instance (typically via `createPlatformLoaders()`). */
2383
+ loader: ManifestLoader;
2384
+ /** Pinia store ID. Default `admin-manifest`. */
2385
+ id?: string;
2386
+ }
2387
+ type ManifestStoreDefinition = StoreDefinition<string, ManifestStoreState, Record<string, never>, ManifestStoreActions>;
2388
+ /**
2389
+ * Returns a `useStore` function for the manifest store. Apps call the
2390
+ * factory once at module top level and export the result as
2391
+ * `useManifestStore`.
2392
+ */
2393
+ declare function createManifestStore(options: CreateManifestStoreOptions): ManifestStoreDefinition;
2394
+
2395
+ export { ADMIN_UI_VERSION, ActionDefNotInManifestError, type ActionHandler, ActionRegistry, type ActionsMap, type ApiListResponse, type BatchColumnData, BatchColumnDriftError, BatchColumnFetcher, type BatchColumnFetcherOptions, type BatchColumnRow, type BatchColumnValue, type BillingCycleStr, BootLoadError, BootLoader, type BootLoaderOptions, type BuildRouteEntry, type BuildSnapshotsOptions, type BulkItemKind, type BulkItemStatus, type BulkPublishItem, type BundleAddPreviewShape, type BundleCancelPreviewShape, type BundlePreviewIssueShape, type BundlePreviewShape, type BundlePreviewSnapshotShape, BundlesApiError, BusinessTypesApiError, CATALOG_DEFAULT_LOCALE, type CachedManifestEntry, type CatalogBundle, CatalogEntriesApiError, type CatalogPlan, type CatalogSnapshot, type ConfirmDialogState, type CreateAdminRoutesOptions, type CreateManifestStoreOptions, type CreatePlatformLoadersOptions, type CreateSuperAdminAppOptions, DEFAULT_I18N_DE, DEFAULT_PILOT_COPY, DEFAULT_SECTION_ORDER, DEFAULT_STANDARD_PAGE_ROUTES, DEFAULT_YEARLY_FACTOR, DiscoveryLoadError, type DraftPricing, ENTITLEMENT_INJECTION_KEY, type EmailHistoryDetail, type EmailHistoryFilter, type EmailHistoryListResult, type EmailHistoryResendResult, type EmailHistoryRow, type EmailHistoryStatus, type EntitlementSnapshotShape, type ExtensionLoader, type ExtensionsMap, type FeatureRegistryEntry, type FeatureRouterGuardOptions, type FeatureRowMarkers, type HttpClient, HttpJsonError, type HttpResponse, type KvStore, ManifestLoadError, ManifestLoader, type ManifestLoaderOptions, type ManifestStoreActions, type ManifestStoreDefinition, type ManifestStoreState, MarketingProjectionsApiError, type MfaDialogState, MissingHandlerError, type NavBuilderOptions, type PackageSnapshotShape, type ParamStyle, type PilotCopy, type PilotCreatePayload, type PilotCreateResult, type PilotEditPayload, type PilotEditResult, type PlanChangePreviewShape, type PlanSnapshotShape, type PlanVersionListFilter, type PlanVersionRow, PlannedOnlyFeatureError, PlansApiError, type PlatformEmailProvider, type PlatformEmailTestInput, type PlatformEmailTestResult, type PlatformEmailWriteInput, type PlatformLoaders, type PlatformTenantActionRow, type PlatformTenantActionTone, type PlatformTenantActionsOptions, type PlatformTenantActionsResult, type PriceLineItem, ProjectPageHost, type PromoCodeCreatePayload, type PromoCodeDurationType, type PromoCodePlanOption, type PromoCodeUpdatePayload, type PromoCodeValueType, type PromoState, type PromoStatus, PromotionsApiError, type QuotaMeta, type RawCatalogData, type RedundantFeatureHintShape, type ResolvedAction, type ResolvedPlan, type ResolvedTenantAction, SUPER_ADMIN_ACTIONS_KEY, SUPER_ADMIN_BRAND_KEY, SUPER_ADMIN_ENDPOINTS_KEY, SUPER_ADMIN_EXTENSIONS_KEY, SUPER_ADMIN_HTTP_KEY, SUPER_ADMIN_LOGIN_ADAPTER_KEY, SUPER_ADMIN_MANIFEST_KEY, type SidebarItem, type SidebarSection, type SnapshotKind, type SubscriptionBundleShape, type SubscriptionDraft, type SuperAdminAppHandle, type SuperAdminAuthGuardOptions, type SuperAdminBrand, type SuperAdminEndpoints, type SuperAdminLoginAdapter, type SuperAdminLoginResult, type SuperAdminManifestGuardOptions, type TenantActionFlowProviders, type TenantActionInput, type TenantManifestNavItem, type TenantManifestShape, type TenantPlanSectionI18n, type TenantRowLike, TenantSubscriptionBundlesApiError, type UsageSnapshotShape, type UseActionsResult, type UseApiListOptions, type UseApiListResult, type UseAuditEntriesOptions, type UseAuditEntriesResult, type UseBatchColumnsResult, type UseBulkPublishOptions, type UseBulkPublishResult, type UseBundleVersionsMapOptions, type UseBundleVersionsMapResult, type UseBundleVersionsOptions, type UseBundleVersionsResult, type UseBundlesOptions, type UseBundlesResult, type UseBusinessTypeVersionsOptions, type UseBusinessTypeVersionsResult, type UseBusinessTypesOptions, type UseBusinessTypesResult, type UseCatalogEntriesOptions, type UseCatalogEntriesResult, type UseDiscoveryOptions, type UseDiscoveryResult, type UseEntitlementOptions, type UseEntitlementResult, type UseLivePlanVersionsOptions, type UseLivePlanVersionsResult, type UseManifestResult, type UseMarketingProjectionsOptions, type UseMarketingProjectionsResult, type UseNavResult, type UsePlanEditorOptions, type UsePlanEditorResult, type UsePlanVersionsOptions, type UsePlanVersionsResult, type UsePlansOptions, type UsePlansResult, type UsePromotionsOptions, type UsePromotionsResult, type UsePublicBootResult, type UseSubscriptionDraftOptions, type UseTenantActionFlowResult, type UseTenantBillingCatalogOptions, type UseTenantBillingCatalogResult, type UseTenantBillingOptions, type UseTenantBillingResult, type UseTenantManifestOptions, type UseTenantManifestResult, type UseTenantSubscriptionBundlesOptions, type UseTenantSubscriptionBundlesResult, type UseTenantsOptions, type UseTenantsResult, type VersionsOptions, type VersionsResult, buildFeatureRegistry, buildFeatureRouterGuard, buildNavigationGuard, buildQuotaRegistry, buildRoutes, buildSidebar, buildSnapshots, createAdminRoutes, createManifestStore, createPlatformLoaders, createProjectPageHostRoute, createSuperAdminApp, defaultHttpClient, defaultIconForActionKey, defaultKvStore, defaultToneForActionKey, getJson, listOpenDrafts, postJson, provideEntitlement, resolveExtension, trimTrailingSlashes, useActions, useApiList, useAuditEntries, useBatchColumns, useBulkPublish, useBundleVersions, useBundleVersionsMap, useBundles, useBusinessTypeVersions, useBusinessTypes, useCatalogEntries, useDiscovery, useEntitlement, useInjectedEntitlement, useLivePlanVersions, useManifest, useMarketingProjections, useNav, usePlanEditor, usePlanVersions, usePlanVersionsCatalog, usePlans, usePlatformTenantActions, usePromotions, usePublicBoot, useSubscriptionDraft, useSuperAdminActions, useSuperAdminBrand, useSuperAdminEndpoints, useSuperAdminExtensions, useSuperAdminHttp, useSuperAdminLoginAdapter, useSuperAdminManifest, useTenantActionFlow, useTenantBilling, useTenantBillingCatalog, useTenantManifest, useTenantSubscriptionBundles, useTenants };