@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.
- package/LICENSE +202 -0
- package/README.md +47 -0
- package/dist/index.cjs +3776 -0
- package/dist/index.d.cts +2395 -0
- package/dist/index.d.ts +2395 -0
- package/dist/index.js +3659 -0
- package/dist/testing-e2e/admin-pages-suite.cjs +134 -0
- package/dist/testing-e2e/admin-pages-suite.d.cts +37 -0
- package/dist/testing-e2e/admin-pages-suite.d.ts +37 -0
- package/dist/testing-e2e/admin-pages-suite.js +109 -0
- package/package.json +87 -0
- package/src/action-registry.ts +110 -0
- package/src/batch-column-fetcher.ts +152 -0
- package/src/boot-loader.ts +60 -0
- package/src/components/BundleVersionPublishDialog.vue +359 -0
- package/src/components/BusinessTypeVersionEditorDialog.vue +417 -0
- package/src/components/BusinessTypeVersionPublishDialog.vue +258 -0
- package/src/components/FeatureGate.vue +56 -0
- package/src/components/KpiCard.vue +53 -0
- package/src/components/KvBlock.vue +36 -0
- package/src/components/MarketingPromotionsTab.vue +861 -0
- package/src/components/MfaPromptDialog.vue +104 -0
- package/src/components/TenantActionConfirmDialog.vue +252 -0
- package/src/components/VersionDiffPreview.vue +150 -0
- package/src/components/bundle-editor/BundleCreatePanel.vue +681 -0
- package/src/components/bundle-editor/BundleFeaturesEditor.vue +215 -0
- package/src/components/bundle-editor/BundlePlanCompatPicker.vue +316 -0
- package/src/components/bundle-editor/BundleQuotasEditor.vue +219 -0
- package/src/components/bundle-editor/BundleStatusBanner.vue +189 -0
- package/src/components/bundle-editor/BundleVersionInlineEditor.vue +659 -0
- package/src/components/bundle-editor/BundleVersionStrip.vue +207 -0
- package/src/components/bundle-editor/bundle-version-status.ts +164 -0
- package/src/components/bundle-editor/catalog-i18n.ts +85 -0
- package/src/components/dialogs/PilotCreateDialog.vue +780 -0
- package/src/components/dialogs/PilotEditDialog.vue +548 -0
- package/src/components/dialogs/PromoCodeCreateDialog.vue +737 -0
- package/src/components/dialogs/PromoCodeEditDialog.vue +859 -0
- package/src/components/dialogs/types.ts +126 -0
- package/src/components/plan/PlanCycleToggle.vue +99 -0
- package/src/components/plan/PlanGrid.vue +211 -0
- package/src/components/plan/PriceSummary.vue +293 -0
- package/src/components/plan/PromoCodeInput.vue +192 -0
- package/src/components/plan/PublicBundleGrid.vue +287 -0
- package/src/components/plan-cockpit/PlanCockpit.vue +993 -0
- package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +65 -0
- package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +91 -0
- package/src/components/plan-cockpit/PlanCockpitHeader.vue +98 -0
- package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +99 -0
- package/src/components/plan-cockpit/PlanCockpitKpis.vue +57 -0
- package/src/components/plan-cockpit/PlanCockpitVersions.vue +226 -0
- package/src/components/plan-cockpit/types.ts +72 -0
- package/src/components/plan-create-dialog/PlanCreateDialog.vue +508 -0
- package/src/components/plan-detail/PlanAuditLog.vue +59 -0
- package/src/components/plan-detail/PlanDetail.vue +1195 -0
- package/src/components/plan-detail/PlanDetailHeader.vue +185 -0
- package/src/components/plan-detail/PlanDetailKpis.vue +57 -0
- package/src/components/plan-detail/PlanTerminateDialog.vue +76 -0
- package/src/components/plan-detail/PlanVersionDiffPanel.vue +149 -0
- package/src/components/plan-detail/PlanVersionsPanel.vue +255 -0
- package/src/components/plan-detail/types.ts +59 -0
- package/src/components/plan-list/PlanList.vue +1067 -0
- package/src/components/plan-matrix/PlanMatrix.vue +1113 -0
- package/src/components/plan-review/PlanReview.vue +742 -0
- package/src/components/plan-version-editor/PlanCatalogPreview.vue +184 -0
- package/src/components/plan-version-editor/PlanComponentPool.vue +272 -0
- package/src/components/plan-version-editor/PlanVersionBasket.vue +281 -0
- package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +84 -0
- package/src/components/plan-version-editor/PlanVersionEditor.vue +1728 -0
- package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +94 -0
- package/src/components/plan-version-editor/types.ts +96 -0
- package/src/components/wizard-stepper/WizardStepper.vue +107 -0
- package/src/create-admin-routes.ts +68 -0
- package/src/create-super-admin-app.ts +345 -0
- package/src/entitlement-provider.ts +36 -0
- package/src/feature-router-guard.ts +77 -0
- package/src/http-json.ts +62 -0
- package/src/index.ts +98 -0
- package/src/manifest-loader.ts +139 -0
- package/src/manifest-store-factory.ts +114 -0
- package/src/nav-builder.ts +274 -0
- package/src/pages-standard/AdminLayout.vue +411 -0
- package/src/pages-standard/AdminManifestErrorPage.vue +79 -0
- package/src/pages-standard/AuditPage.vue +223 -0
- package/src/pages-standard/BundlesPage.vue +910 -0
- package/src/pages-standard/BusinessTypesPage.vue +662 -0
- package/src/pages-standard/DashboardPage.vue +595 -0
- package/src/pages-standard/DiscoveryPage.vue +663 -0
- package/src/pages-standard/EmailHistoryPage.vue +576 -0
- package/src/pages-standard/MarketingCatalogPage.vue +1771 -0
- package/src/pages-standard/PilotsPage.vue +577 -0
- package/src/pages-standard/PlanVersionsPage.vue +216 -0
- package/src/pages-standard/PlansPage.vue +1222 -0
- package/src/pages-standard/PlatformEmailPage.vue +434 -0
- package/src/pages-standard/PromoCodeDetailPage.vue +331 -0
- package/src/pages-standard/PromoCodesPage.vue +589 -0
- package/src/pages-standard/SubscriptionsPage.vue +159 -0
- package/src/pages-standard/SuperAdminLoginPage.vue +299 -0
- package/src/pages-standard/SuperAdminSetupWizard.vue +405 -0
- package/src/pages-standard/TenantDetailPage.vue +432 -0
- package/src/pages-standard/TenantsPage.vue +732 -0
- package/src/pages-standard/UsersPage.vue +504 -0
- package/src/pages-standard/bundles-page/BundleAccordionList.vue +116 -0
- package/src/pages-standard/bundles-page/BundleDetailPanel.vue +193 -0
- package/src/pages-standard/bundles-page/BundlesFilterBar.vue +48 -0
- package/src/pages-standard/bundles-page/BundlesHeader.vue +67 -0
- package/src/pages-standard/bundles-page/BundlesKpis.vue +39 -0
- package/src/pages-standard/bundles-page/types.ts +15 -0
- package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +246 -0
- package/src/pages-standard/discovery-page/DiscoveryCapList.vue +171 -0
- package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +410 -0
- package/src/pages-standard/discovery-page/DiscoveryHeader.vue +31 -0
- package/src/pages-standard/discovery-page/DiscoveryKpis.vue +47 -0
- package/src/pages-standard/discovery-page/DiscoveryMetaBanner.vue +26 -0
- package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +247 -0
- package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +73 -0
- package/src/pages-standard/discovery-page/discovery-ui.ts +166 -0
- package/src/pages-standard/email-history.types.ts +48 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +448 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +93 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +138 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +58 -0
- package/src/pages-standard/marketing-catalog/types.ts +43 -0
- package/src/pages-standard/plan-versions/PlanDiffCard.vue +132 -0
- package/src/pages-standard/plan-versions/PlanVersionHeader.vue +293 -0
- package/src/pages-standard/plan-versions/PlanVersionStatusBadge.vue +48 -0
- package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +307 -0
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +269 -0
- package/src/pages-standard/plan-versions/PlanVersionsKpi.vue +65 -0
- package/src/pages-standard/plan-versions/PlanVersionsList.vue +292 -0
- package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +261 -0
- package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +300 -0
- package/src/pages-standard/plan-versions/VersionDiffPreview.vue +167 -0
- package/src/pages-standard/plan-versions/format.ts +53 -0
- package/src/pages-standard/plan-versions/types.ts +33 -0
- package/src/pages-standard/plans-page/PlanArchiveDialog.vue +50 -0
- package/src/pages-standard/plans-page/PlanBundleOverview.vue +147 -0
- package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +46 -0
- package/src/pages-standard/plans-page/PlanPublishDialog.vue +106 -0
- package/src/pages-standard/plans-page/PlansPageToast.vue +69 -0
- package/src/pages-standard/plans-page/types.ts +18 -0
- package/src/pages-standard/platform-email.types.ts +41 -0
- package/src/pages-standard/sa-theme.css +223 -0
- package/src/pages-standard/tenants/StatusPill.vue +62 -0
- package/src/pages-standard/tenants/format.ts +38 -0
- package/src/pages-tenant/LimitsRow.vue +67 -0
- package/src/pages-tenant/MySubscriptionBundlesPage.vue +604 -0
- package/src/pages-tenant/OnboardingConfigurator.vue +417 -0
- package/src/pages-tenant/PackageSnapshotPanel.vue +241 -0
- package/src/pages-tenant/PendingVersionBanner.vue +126 -0
- package/src/pages-tenant/PlanChangeWizard.vue +630 -0
- package/src/pages-tenant/TenantPlanSection.vue +729 -0
- package/src/pages-tenant/UsageBar.vue +107 -0
- package/src/pages-tenant/default-i18n.ts +280 -0
- package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +304 -0
- package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +323 -0
- package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +122 -0
- package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +67 -0
- package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +31 -0
- package/src/plan-versions-catalog.ts +368 -0
- package/src/platform-loaders.ts +68 -0
- package/src/project-page-host.ts +108 -0
- package/src/testing-e2e/admin-pages-suite.ts +187 -0
- package/src/types.ts +58 -0
- package/src/use-actions.ts +25 -0
- package/src/use-api-list.ts +151 -0
- package/src/use-audit-entries.ts +48 -0
- package/src/use-batch-columns.ts +59 -0
- package/src/use-bulk-publish.ts +150 -0
- package/src/use-bundle-versions-map.ts +112 -0
- package/src/use-bundles.ts +297 -0
- package/src/use-business-types.ts +269 -0
- package/src/use-catalog-entries.ts +243 -0
- package/src/use-discovery.ts +158 -0
- package/src/use-entitlement.ts +87 -0
- package/src/use-live-plan-versions.ts +126 -0
- package/src/use-manifest.ts +57 -0
- package/src/use-marketing-projections.ts +160 -0
- package/src/use-nav.ts +33 -0
- package/src/use-plan-editor.ts +162 -0
- package/src/use-plan-versions.ts +83 -0
- package/src/use-plans.ts +375 -0
- package/src/use-platform-tenant-actions.ts +286 -0
- package/src/use-promotions.ts +126 -0
- package/src/use-public-boot.ts +40 -0
- package/src/use-subscription-draft.ts +355 -0
- package/src/use-super-admin-context.ts +93 -0
- package/src/use-tenant-action-flow.ts +238 -0
- package/src/use-tenant-billing-catalog.ts +160 -0
- package/src/use-tenant-billing.ts +453 -0
- package/src/use-tenant-manifest.ts +94 -0
- package/src/use-tenant-subscription-bundles.ts +148 -0
- package/src/use-tenants.ts +61 -0
- package/src/version.ts +10 -0
package/dist/index.d.cts
ADDED
|
@@ -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 };
|