@saasicat/ui-vue 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +47 -4
  2. package/dist/action-registry-CQ2wL3R-.d.cts +85 -0
  3. package/dist/action-registry-CQ2wL3R-.d.ts +85 -0
  4. package/dist/chunk-3YP2ZAOD.js +63 -0
  5. package/dist/chunk-EH7TOD4H.js +718 -0
  6. package/dist/chunk-LVGUSD3T.js +26 -0
  7. package/dist/client/index.cjs +785 -0
  8. package/dist/client/index.d.cts +295 -0
  9. package/dist/client/index.d.ts +295 -0
  10. package/dist/client/index.js +52 -0
  11. package/dist/index.cjs +787 -834
  12. package/dist/index.d.cts +267 -841
  13. package/dist/index.d.ts +267 -841
  14. package/dist/index.js +284 -1059
  15. package/dist/quasar/index.cjs +159 -0
  16. package/dist/quasar/index.d.cts +86 -0
  17. package/dist/quasar/index.d.ts +86 -0
  18. package/dist/quasar/index.js +91 -0
  19. package/dist/ui-notify-COUzXEQ4.d.ts +178 -0
  20. package/dist/ui-notify-D_eAAwRh.d.cts +178 -0
  21. package/package.json +23 -3
  22. package/src/client/index.ts +25 -0
  23. package/src/components/FeatureGate.vue +1 -1
  24. package/src/components/plan/PlanCycleToggle.vue +1 -1
  25. package/src/components/plan/PlanGrid.vue +2 -2
  26. package/src/components/plan/PriceSummary.vue +1 -1
  27. package/src/components/plan/PromoCodeInput.vue +1 -1
  28. package/src/components/plan/PublicBundleGrid.vue +1 -1
  29. package/src/index.ts +82 -79
  30. package/src/pages-standard/AdminLayout.vue +2 -2
  31. package/src/pages-standard/DashboardPage.vue +2 -2
  32. package/src/pages-standard/EmailHistoryPage.vue +9 -13
  33. package/src/pages-standard/MarketingCatalogPage.vue +5 -5
  34. package/src/pages-standard/PilotsPage.vue +9 -13
  35. package/src/pages-standard/PlanVersionsPage.vue +2 -2
  36. package/src/pages-standard/PlansPage.vue +2 -2
  37. package/src/pages-standard/PlatformEmailPage.vue +7 -5
  38. package/src/pages-standard/PromoCodesPage.vue +8 -22
  39. package/src/pages-standard/SuperAdminLoginPage.vue +3 -3
  40. package/src/pages-standard/SuperAdminSetupWizard.vue +2 -2
  41. package/src/pages-standard/TenantDetailPage.vue +4 -4
  42. package/src/pages-standard/TenantsPage.vue +3 -3
  43. package/src/pages-standard/UsersPage.vue +6 -12
  44. package/src/pages-standard/plan-versions/PlanVersionHeader.vue +1 -1
  45. package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +1 -1
  46. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +1 -1
  47. package/src/pages-standard/plan-versions/PlanVersionsList.vue +1 -1
  48. package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +1 -1
  49. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +1 -1
  50. package/src/pages-tenant/MySubscriptionBundlesPage.vue +1 -1
  51. package/src/pages-tenant/OnboardingConfigurator.vue +3 -3
  52. package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
  53. package/src/pages-tenant/PlanChangeWizard.vue +2 -2
  54. package/src/pages-tenant/TenantPlanSection.vue +3 -3
  55. package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +1 -1
  56. package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +2 -2
  57. package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +1 -1
  58. package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +1 -1
  59. package/src/quasar/create-super-admin-app.ts +173 -0
  60. package/src/quasar/index.ts +11 -0
  61. package/src/quasar/notify.ts +30 -0
  62. package/src/{manifest-store-factory.ts → vue/manifest-store-factory.ts} +1 -1
  63. package/src/{platform-loaders.ts → vue/platform-loaders.ts} +4 -4
  64. package/src/{project-page-host.ts → vue/project-page-host.ts} +1 -1
  65. package/src/{create-super-admin-app.ts → vue/super-admin-context.ts} +20 -139
  66. package/src/vue/ui-notify.ts +30 -0
  67. package/src/{use-actions.ts → vue/use-actions.ts} +1 -1
  68. package/src/{use-api-list.ts → vue/use-api-list.ts} +1 -1
  69. package/src/{use-batch-columns.ts → vue/use-batch-columns.ts} +1 -1
  70. package/src/{use-bulk-publish.ts → vue/use-bulk-publish.ts} +1 -1
  71. package/src/{use-bundle-versions-map.ts → vue/use-bundle-versions-map.ts} +1 -1
  72. package/src/{use-bundles.ts → vue/use-bundles.ts} +1 -1
  73. package/src/{use-business-types.ts → vue/use-business-types.ts} +1 -1
  74. package/src/{use-catalog-entries.ts → vue/use-catalog-entries.ts} +1 -1
  75. package/src/{use-discovery.ts → vue/use-discovery.ts} +1 -1
  76. package/src/{use-entitlement.ts → vue/use-entitlement.ts} +1 -1
  77. package/src/{use-live-plan-versions.ts → vue/use-live-plan-versions.ts} +1 -1
  78. package/src/{use-manifest.ts → vue/use-manifest.ts} +1 -1
  79. package/src/{use-marketing-projections.ts → vue/use-marketing-projections.ts} +1 -1
  80. package/src/{use-nav.ts → vue/use-nav.ts} +1 -1
  81. package/src/{use-plans.ts → vue/use-plans.ts} +1 -1
  82. package/src/{use-promotions.ts → vue/use-promotions.ts} +1 -1
  83. package/src/{use-public-boot.ts → vue/use-public-boot.ts} +1 -1
  84. package/src/{use-super-admin-context.ts → vue/use-super-admin-context.ts} +2 -2
  85. package/src/{use-tenant-action-flow.ts → vue/use-tenant-action-flow.ts} +1 -1
  86. package/src/{use-tenant-billing-catalog.ts → vue/use-tenant-billing-catalog.ts} +2 -2
  87. package/src/{use-tenant-billing.ts → vue/use-tenant-billing.ts} +2 -2
  88. package/src/{use-tenant-manifest.ts → vue/use-tenant-manifest.ts} +1 -1
  89. package/src/{use-tenant-subscription-bundles.ts → vue/use-tenant-subscription-bundles.ts} +1 -1
  90. package/dist/testing-e2e/admin-pages-suite.d.cts +0 -37
  91. package/dist/testing-e2e/admin-pages-suite.d.ts +0 -37
  92. /package/src/{action-registry.ts → client/action-registry.ts} +0 -0
  93. /package/src/{batch-column-fetcher.ts → client/batch-column-fetcher.ts} +0 -0
  94. /package/src/{boot-loader.ts → client/boot-loader.ts} +0 -0
  95. /package/src/{http-json.ts → client/http-json.ts} +0 -0
  96. /package/src/{manifest-loader.ts → client/manifest-loader.ts} +0 -0
  97. /package/src/{nav-builder.ts → client/nav-builder.ts} +0 -0
  98. /package/src/{plan-versions-catalog.ts → client/plan-versions-catalog.ts} +0 -0
  99. /package/src/{types.ts → client/types.ts} +0 -0
  100. /package/src/{version.ts → client/version.ts} +0 -0
  101. /package/src/{create-admin-routes.ts → vue/create-admin-routes.ts} +0 -0
  102. /package/src/{entitlement-provider.ts → vue/entitlement-provider.ts} +0 -0
  103. /package/src/{feature-router-guard.ts → vue/feature-router-guard.ts} +0 -0
  104. /package/src/{use-audit-entries.ts → vue/use-audit-entries.ts} +0 -0
  105. /package/src/{use-plan-editor.ts → vue/use-plan-editor.ts} +0 -0
  106. /package/src/{use-plan-versions.ts → vue/use-plan-versions.ts} +0 -0
  107. /package/src/{use-platform-tenant-actions.ts → vue/use-platform-tenant-actions.ts} +0 -0
  108. /package/src/{use-subscription-draft.ts → vue/use-subscription-draft.ts} +0 -0
  109. /package/src/{use-tenants.ts → vue/use-tenants.ts} +0 -0
package/dist/index.cjs CHANGED
@@ -18,8 +18,8 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
20
  // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
23
  ADMIN_UI_VERSION: () => ADMIN_UI_VERSION,
24
24
  ActionDefNotInManifestError: () => ActionDefNotInManifestError,
25
25
  ActionRegistry: () => ActionRegistry,
@@ -54,6 +54,7 @@ __export(index_exports, {
54
54
  SUPER_ADMIN_HTTP_KEY: () => SUPER_ADMIN_HTTP_KEY,
55
55
  SUPER_ADMIN_LOGIN_ADAPTER_KEY: () => SUPER_ADMIN_LOGIN_ADAPTER_KEY,
56
56
  SUPER_ADMIN_MANIFEST_KEY: () => SUPER_ADMIN_MANIFEST_KEY,
57
+ SUPER_ADMIN_NOTIFY_KEY: () => SUPER_ADMIN_NOTIFY_KEY,
57
58
  TenantSubscriptionBundlesApiError: () => TenantSubscriptionBundlesApiError,
58
59
  buildFeatureRegistry: () => buildFeatureRegistry,
59
60
  buildFeatureRouterGuard: () => buildFeatureRouterGuard,
@@ -66,7 +67,6 @@ __export(index_exports, {
66
67
  createManifestStore: () => createManifestStore,
67
68
  createPlatformLoaders: () => createPlatformLoaders,
68
69
  createProjectPageHostRoute: () => createProjectPageHostRoute,
69
- createSuperAdminApp: () => createSuperAdminApp,
70
70
  defaultHttpClient: () => defaultHttpClient,
71
71
  defaultIconForActionKey: () => defaultIconForActionKey,
72
72
  defaultKvStore: () => defaultKvStore,
@@ -117,12 +117,12 @@ __export(index_exports, {
117
117
  useTenantSubscriptionBundles: () => useTenantSubscriptionBundles,
118
118
  useTenants: () => useTenants
119
119
  });
120
- module.exports = __toCommonJS(index_exports);
120
+ module.exports = __toCommonJS(src_exports);
121
121
 
122
- // src/version.ts
122
+ // src/client/version.ts
123
123
  var ADMIN_UI_VERSION = "1.2.0";
124
124
 
125
- // src/types.ts
125
+ // src/client/types.ts
126
126
  function defaultKvStore() {
127
127
  if (typeof globalThis.localStorage === "undefined") {
128
128
  return {
@@ -144,7 +144,7 @@ function defaultHttpClient() {
144
144
  return (url, init) => fetch(url, init);
145
145
  }
146
146
 
147
- // src/http-json.ts
147
+ // src/client/http-json.ts
148
148
  var HttpJsonError = class extends Error {
149
149
  constructor(status, code) {
150
150
  super(code ?? `HTTP ${status}`);
@@ -187,195 +187,7 @@ async function postJson(http, url, body) {
187
187
  return await res.json();
188
188
  }
189
189
 
190
- // src/project-page-host.ts
191
- var import_vue2 = require("vue");
192
- var import_vue_router2 = require("vue-router");
193
-
194
- // src/create-super-admin-app.ts
195
- var import_vue = require("vue");
196
- var import_quasar = require("quasar");
197
- var import_pinia = require("pinia");
198
- var import_vue_router = require("vue-router");
199
- var SUPER_ADMIN_BRAND_KEY = /* @__PURE__ */ Symbol.for(
200
- "@saasicat/ui-vue/SUPER_ADMIN_BRAND"
201
- );
202
- var SUPER_ADMIN_ENDPOINTS_KEY = /* @__PURE__ */ Symbol.for(
203
- "@saasicat/ui-vue/SUPER_ADMIN_ENDPOINTS"
204
- );
205
- var SUPER_ADMIN_EXTENSIONS_KEY = /* @__PURE__ */ Symbol.for(
206
- "@saasicat/ui-vue/SUPER_ADMIN_EXTENSIONS"
207
- );
208
- var SUPER_ADMIN_ACTIONS_KEY = /* @__PURE__ */ Symbol.for(
209
- "@saasicat/ui-vue/SUPER_ADMIN_ACTIONS"
210
- );
211
- var SUPER_ADMIN_MANIFEST_KEY = /* @__PURE__ */ Symbol.for(
212
- "@saasicat/ui-vue/SUPER_ADMIN_MANIFEST"
213
- );
214
- var SUPER_ADMIN_LOGIN_ADAPTER_KEY = /* @__PURE__ */ Symbol.for(
215
- "@saasicat/ui-vue/SUPER_ADMIN_LOGIN_ADAPTER"
216
- );
217
- var SUPER_ADMIN_HTTP_KEY = /* @__PURE__ */ Symbol.for(
218
- "@saasicat/ui-vue/SUPER_ADMIN_HTTP"
219
- );
220
- var DEFAULT_QUASAR_OPTIONS = {
221
- plugins: { Notify: import_quasar.Notify, Dialog: import_quasar.Dialog, Loading: import_quasar.Loading },
222
- config: { notify: { position: "top-right", timeout: 3e3 } }
223
- };
224
- function createSuperAdminApp(options) {
225
- const app = (0, import_vue.createApp)(options.rootComponent);
226
- app.use(import_quasar.Quasar, options.quasarOptions ?? DEFAULT_QUASAR_OPTIONS);
227
- const pinia = (0, import_pinia.createPinia)();
228
- app.use(pinia);
229
- const router = (0, import_vue_router.createRouter)({
230
- history: options.routerHistory ?? (0, import_vue_router.createWebHistory)(),
231
- routes: options.appRoutes
232
- });
233
- const navGuard = buildNavigationGuard(options);
234
- if (navGuard) {
235
- router.beforeEach(navGuard);
236
- }
237
- app.use(router);
238
- const endpoints = {
239
- apiBase: options.endpoints.apiBase,
240
- publicBootEndpoint: options.endpoints.publicBootEndpoint ?? `${options.endpoints.apiBase}/boot`,
241
- manifestEndpoint: options.endpoints.manifestEndpoint ?? `${options.endpoints.apiBase}/manifest`
242
- };
243
- app.provide(SUPER_ADMIN_BRAND_KEY, { tag: "SuperAdmin", ...options.brand });
244
- app.provide(SUPER_ADMIN_ENDPOINTS_KEY, endpoints);
245
- app.provide(SUPER_ADMIN_EXTENSIONS_KEY, options.extensions ?? {});
246
- app.provide(SUPER_ADMIN_ACTIONS_KEY, options.actions ?? {});
247
- if (options.manifestGuard?.getManifest) {
248
- app.provide(SUPER_ADMIN_MANIFEST_KEY, options.manifestGuard.getManifest);
249
- }
250
- if (options.loginAdapter) {
251
- app.provide(SUPER_ADMIN_LOGIN_ADAPTER_KEY, options.loginAdapter);
252
- }
253
- app.provide(SUPER_ADMIN_HTTP_KEY, options.http ?? defaultHttpClient());
254
- for (const plugin of options.installPlugins ?? []) {
255
- plugin(app);
256
- }
257
- return {
258
- app,
259
- router,
260
- pinia,
261
- mount: (selector) => app.mount(selector)
262
- };
263
- }
264
- function buildNavigationGuard(options) {
265
- const { authGuard, manifestGuard } = options;
266
- if (!authGuard && !manifestGuard) return null;
267
- return async (to) => {
268
- if (to.meta?.public === true) return true;
269
- if (authGuard) {
270
- if (!authGuard.isAuthenticated()) return authGuard.onUnauthenticated();
271
- if (authGuard.isSuperAdmin && !authGuard.isSuperAdmin()) {
272
- return authGuard.onUnauthenticated();
273
- }
274
- }
275
- if (manifestGuard) {
276
- try {
277
- await manifestGuard.ensureLoaded();
278
- } catch (err) {
279
- if (manifestGuard.errorRoute && to.path !== manifestGuard.errorRoute) {
280
- return manifestGuard.errorRoute;
281
- }
282
- console.error("[SuperAdmin] manifest load failed", err);
283
- }
284
- }
285
- return true;
286
- };
287
- }
288
-
289
- // src/project-page-host.ts
290
- var asyncComponentCache = /* @__PURE__ */ new WeakMap();
291
- function resolveAsync(extensions, key) {
292
- let bucket = asyncComponentCache.get(extensions);
293
- if (!bucket) {
294
- bucket = /* @__PURE__ */ new Map();
295
- asyncComponentCache.set(extensions, bucket);
296
- }
297
- const cached = bucket.get(key);
298
- if (cached) return cached;
299
- const loader = extensions[key];
300
- if (!loader) return null;
301
- const cmp = (0, import_vue2.defineAsyncComponent)(loader);
302
- bucket.set(key, cmp);
303
- return cmp;
304
- }
305
- var ProjectPageHost = (0, import_vue2.defineComponent)({
306
- name: "ProjectPageHost",
307
- setup() {
308
- const route = (0, import_vue_router2.useRoute)();
309
- const extensions = (0, import_vue2.inject)(SUPER_ADMIN_EXTENSIONS_KEY, {});
310
- const manifestAccessor = (0, import_vue2.inject)(SUPER_ADMIN_MANIFEST_KEY, null);
311
- const matchingPage = (0, import_vue2.computed)(() => {
312
- const manifest = manifestAccessor ? manifestAccessor() : null;
313
- if (!manifest) return null;
314
- const pages = manifest.navigation?.projectPages ?? [];
315
- return pages.find((p) => p.route === route.path) ?? null;
316
- });
317
- const resolvedComponent = (0, import_vue2.computed)(() => {
318
- const page = matchingPage.value;
319
- if (!page) return null;
320
- return resolveAsync(extensions, page.componentKey);
321
- });
322
- return () => {
323
- const page = matchingPage.value;
324
- const cmp = resolvedComponent.value;
325
- if (page && cmp) return (0, import_vue2.h)(cmp);
326
- return (0, import_vue2.h)("div", { class: "sa-project-page-host__missing", role: "alert" }, [
327
- (0, import_vue2.h)("h2", "Seite nicht verf\xFCgbar"),
328
- (0, import_vue2.h)(
329
- "p",
330
- page ? `componentKey "${page.componentKey}" ist nicht in der extensions:-Map registriert. Apps m\xFCssen die Komponente in createSuperAdminApp({ extensions }) eintragen.` : `Keine Manifest-Definition f\xFCr ${route.path}. Entweder fehlt die ProjectPage im Manifest, oder die Capability ist nicht aktiv.`
331
- )
332
- ]);
333
- };
334
- }
335
- });
336
- function createProjectPageHostRoute(options) {
337
- return {
338
- path: options?.path ?? ":projectPagePath(.+)",
339
- component: ProjectPageHost,
340
- meta: { isProjectPageHost: true }
341
- };
342
- }
343
-
344
- // src/create-admin-routes.ts
345
- function createAdminRoutes(options) {
346
- return [
347
- {
348
- path: "/login",
349
- name: "login",
350
- component: options.loginPage,
351
- meta: { public: true }
352
- },
353
- {
354
- // `meta.public: true` MUST be set, otherwise the route runs through
355
- // the manifest guard again → redirect loop.
356
- path: "/admin-error",
357
- name: "admin-error",
358
- component: options.adminErrorPage,
359
- meta: { public: true }
360
- },
361
- {
362
- path: "/admin",
363
- component: options.adminLayout,
364
- children: [
365
- { path: "", redirect: options.dashboardPath ?? "/admin/dashboard" },
366
- ...options.children,
367
- // Catch-all for manifest ProjectPages — Vue Router 4 matches
368
- // more specific children first; the host only steps in when
369
- // no static page matches. MUST come last.
370
- createProjectPageHostRoute()
371
- ]
372
- },
373
- { path: "/", redirect: "/admin" },
374
- { path: "/:pathMatch(.*)*", redirect: "/admin" }
375
- ];
376
- }
377
-
378
- // src/boot-loader.ts
190
+ // src/client/boot-loader.ts
379
191
  var BootLoadError = class extends Error {
380
192
  constructor(status, message) {
381
193
  super(message);
@@ -405,7 +217,7 @@ var BootLoader = class {
405
217
  }
406
218
  };
407
219
 
408
- // src/manifest-loader.ts
220
+ // src/client/manifest-loader.ts
409
221
  var ManifestLoadError = class extends Error {
410
222
  constructor(status, message) {
411
223
  super(message);
@@ -488,7 +300,7 @@ var ManifestLoader = class {
488
300
  }
489
301
  };
490
302
 
491
- // src/nav-builder.ts
303
+ // src/client/nav-builder.ts
492
304
  var DEFAULT_STANDARD_PAGE_ROUTES = {
493
305
  dashboard: "/admin/dashboard",
494
306
  tenants: "/admin/tenants",
@@ -636,7 +448,7 @@ function resolveExtension(componentKey, extensions) {
636
448
  return extensions[componentKey] ?? null;
637
449
  }
638
450
 
639
- // src/action-registry.ts
451
+ // src/client/action-registry.ts
640
452
  var MissingHandlerError = class extends Error {
641
453
  constructor(actionKey) {
642
454
  super(
@@ -712,7 +524,7 @@ var ActionRegistry = class {
712
524
  }
713
525
  };
714
526
 
715
- // src/batch-column-fetcher.ts
527
+ // src/client/batch-column-fetcher.ts
716
528
  var BatchColumnDriftError = class extends Error {
717
529
  constructor(column, reason) {
718
530
  super(`Spalte "${column.key}": ${reason}`);
@@ -812,116 +624,509 @@ var BatchColumnFetcher = class {
812
624
  }
813
625
  };
814
626
 
815
- // src/use-api-list.ts
816
- var import_vue3 = require("vue");
817
- function useApiList(options) {
818
- const http = options.http ?? defaultHttpClient();
819
- const items = (0, import_vue3.ref)([]);
820
- const page = (0, import_vue3.ref)(1);
821
- const pageSize = (0, import_vue3.ref)(50);
822
- const total = (0, import_vue3.ref)(0);
823
- const loading = (0, import_vue3.ref)(false);
824
- const error = (0, import_vue3.ref)(null);
825
- function buildUrl() {
826
- const params = new URLSearchParams();
827
- params.set("page", String(page.value));
828
- params.set("pageSize", String(pageSize.value));
829
- const f = options.filter?.value ?? {};
830
- for (const [k, v] of Object.entries(f)) {
831
- if (v === void 0 || v === null || v === "") continue;
832
- params.set(k, String(v));
833
- }
834
- const sep = options.endpoint.includes("?") ? "&" : "?";
835
- return `${options.endpoint}${sep}${params.toString()}`;
836
- }
837
- async function load() {
838
- loading.value = true;
839
- error.value = null;
840
- try {
841
- const headers = {};
842
- const token = options.getAuthToken?.();
843
- if (token) headers.Authorization = `Bearer ${token}`;
844
- const res = await http(buildUrl(), { method: "GET", headers });
845
- if (res.status !== 200) {
846
- throw new Error(`Endpoint ${options.endpoint} \u2192 HTTP ${res.status}`);
847
- }
848
- const raw = await res.json();
849
- if (Array.isArray(raw)) {
850
- items.value = raw;
851
- total.value = raw.length;
852
- } else if (raw !== null && typeof raw === "object") {
853
- const body = raw;
854
- items.value = body.items ?? [];
855
- if (typeof body.page === "number") page.value = body.page;
856
- if (typeof body.pageSize === "number") pageSize.value = body.pageSize;
857
- if (typeof body.total === "number") total.value = body.total;
858
- } else {
859
- items.value = [];
860
- total.value = 0;
861
- }
862
- } catch (err) {
863
- error.value = err instanceof Error ? err : new Error(String(err));
864
- items.value = [];
865
- total.value = 0;
866
- } finally {
867
- loading.value = false;
868
- }
869
- }
870
- async function goToPage(p) {
871
- page.value = Math.max(1, Math.floor(p));
872
- await load();
873
- }
874
- async function setPageSize(size) {
875
- pageSize.value = Math.max(1, Math.floor(size));
876
- page.value = 1;
877
- await load();
878
- }
879
- if (options.filter) {
880
- (0, import_vue3.watch)(
881
- options.filter,
882
- () => {
883
- page.value = 1;
884
- void load();
885
- },
886
- { deep: true }
887
- );
888
- }
889
- if (options.autoLoad !== false) {
890
- Promise.resolve().then(() => void load());
627
+ // src/client/plan-versions-catalog.ts
628
+ var dec = (s) => Number(s);
629
+ function extractQuotas(p) {
630
+ if (p.quotas && Object.keys(p.quotas).length > 0) {
631
+ return { ...p.quotas };
891
632
  }
892
- return {
893
- items,
894
- page,
895
- pageSize,
896
- total,
897
- loading,
898
- error,
899
- reload: load,
900
- goToPage,
901
- setPageSize
902
- };
633
+ const out = {};
634
+ if (typeof p.maxUsers === "number") out.users = p.maxUsers;
635
+ if (typeof p.maxVehicles === "number") out.vehicles = p.maxVehicles;
636
+ if (typeof p.maxStorageGb === "number") out.storageGb = p.maxStorageGb;
637
+ return out;
903
638
  }
904
-
905
- // src/use-tenants.ts
906
- var import_vue4 = require("vue");
907
- function useTenants(options) {
908
- if (!options?.endpoint) {
909
- throw new Error(
910
- 'useTenants: `endpoint` ist Pflicht (z. B. "/api/v1/admin/tenants" oder "/api/admin/tenants"). Plattform hat keinen Default, weil Apps unterschiedliche globalPrefix-Konventionen haben.'
911
- );
912
- }
913
- const filter = options.filter ?? (0, import_vue4.ref)({});
914
- const list = useApiList({
915
- endpoint: options.endpoint,
916
- filter,
917
- http: options.http,
918
- getAuthToken: options.getAuthToken,
919
- autoLoad: options.autoLoad
639
+ var sortByPublishedDesc = (rows) => [...rows].sort((a, b) => {
640
+ const at = a.publishedAt ? Date.parse(a.publishedAt) : Number.POSITIVE_INFINITY;
641
+ const bt = b.publishedAt ? Date.parse(b.publishedAt) : Number.POSITIVE_INFINITY;
642
+ if (at !== bt) return bt - at;
643
+ return b.version - a.version;
644
+ });
645
+ function findLiveAt(rows, asOfMs) {
646
+ const candidates = rows.filter((r) => {
647
+ if (r.publishedAt === null) return false;
648
+ if (Date.parse(r.publishedAt) > asOfMs) return false;
649
+ if (r.supersededAt !== null && Date.parse(r.supersededAt) <= asOfMs) return false;
650
+ return true;
651
+ });
652
+ if (candidates.length === 0) return null;
653
+ return [...candidates].sort((a, b) => b.version - a.version)[0] ?? null;
654
+ }
655
+ function findCurrentLive(rows) {
656
+ return rows.find((r) => r.publishedAt !== null && r.supersededAt === null) ?? null;
657
+ }
658
+ function findDraft(rows) {
659
+ return rows.find((r) => r.publishedAt === null) ?? null;
660
+ }
661
+ function groupBy(rows, keyOf) {
662
+ const out = /* @__PURE__ */ new Map();
663
+ for (const r of rows) {
664
+ const k = keyOf(r);
665
+ const list = out.get(k) ?? [];
666
+ list.push(r);
667
+ out.set(k, list);
668
+ }
669
+ return out;
670
+ }
671
+ function resolvePlan(source, liveBase) {
672
+ const quotas = extractQuotas(source);
673
+ return {
674
+ source,
675
+ liveBase,
676
+ isDraft: source.publishedAt === null,
677
+ planId: source.planId,
678
+ features: source.features,
679
+ quotas,
680
+ monthlyNet: dec(source.monthlyNet),
681
+ yearlyNet: dec(source.yearlyNet),
682
+ marketed: source.marketed,
683
+ version: source.version,
684
+ maxUsers: quotas.users,
685
+ maxVehicles: quotas.vehicles,
686
+ maxStorageGb: quotas.storageGb
687
+ };
688
+ }
689
+ function dateLabelDe(iso) {
690
+ return new Date(iso).toLocaleDateString("de-DE", {
691
+ day: "2-digit",
692
+ month: "short",
693
+ year: "numeric"
694
+ });
695
+ }
696
+ function buildDraftsSnapshot(data, sortPlansFn) {
697
+ const planByKey = groupBy(data.planVersions, (r) => r.planId);
698
+ const plans = [];
699
+ for (const [, rows] of planByKey) {
700
+ const draft = findDraft(rows);
701
+ const live = findCurrentLive(rows);
702
+ if (draft) plans.push(resolvePlan(draft, live));
703
+ else if (live) plans.push(resolvePlan(live, null));
704
+ }
705
+ const draftCount = plans.filter((p) => p.isDraft).length;
706
+ return {
707
+ id: "drafts",
708
+ kind: "drafts",
709
+ status: "DRAFT",
710
+ label: "Arbeitsstand",
711
+ title: draftCount === 0 ? "Keine offenen Drafts" : `${draftCount} offene${draftCount === 1 ? "r" : ""} Draft${draftCount === 1 ? "" : "s"} bereit f\xFCr Publish`,
712
+ description: draftCount === 0 ? "Lege einen Draft an, um Limits, Preise oder Features zu \xE4ndern. Drafts sind ohne MFA editierbar und werden erst beim Publish wirksam." : "Diese Tabelle zeigt, wie der Catalog nach Publish aller Drafts aussehen w\xFCrde. Bestehende Mandanten bleiben auf der vorherigen Version, bis sie aktiv migriert werden.",
713
+ asOf: null,
714
+ createdAt: null,
715
+ publishedAt: null,
716
+ authorEmail: null,
717
+ plans: sortPlansFn(plans),
718
+ draftCount,
719
+ regressionCount: 0
720
+ };
721
+ }
722
+ function buildActiveSnapshot(data, sortPlansFn) {
723
+ const planByKey = groupBy(data.planVersions, (r) => r.planId);
724
+ const plans = [];
725
+ for (const [, rows] of planByKey) {
726
+ const live = findCurrentLive(rows);
727
+ if (live) plans.push(resolvePlan(live, null));
728
+ }
729
+ if (plans.length === 0) return null;
730
+ const publishedDates = plans.map((p) => p.source.publishedAt).filter((t) => t !== null).sort();
731
+ const latestPublished = publishedDates[publishedDates.length - 1];
732
+ return {
733
+ id: "active",
734
+ kind: "active",
735
+ status: "ACTIVE",
736
+ label: "Aktuell",
737
+ title: "Live-Catalog",
738
+ description: "Diese Plan-Versionen sind aktuell f\xFCr neue Onboardings und f\xFCr Renewals nach Stichtag aktiv.",
739
+ asOf: latestPublished ?? null,
740
+ createdAt: null,
741
+ publishedAt: latestPublished ?? null,
742
+ authorEmail: null,
743
+ plans: sortPlansFn(plans),
744
+ draftCount: 0,
745
+ regressionCount: 0
746
+ };
747
+ }
748
+ function buildHistoricalSnapshots(data, sortPlansFn) {
749
+ const allPublishedAt = /* @__PURE__ */ new Set();
750
+ for (const r of data.planVersions) if (r.publishedAt) allPublishedAt.add(r.publishedAt);
751
+ if (allPublishedAt.size === 0) return [];
752
+ const planByKey = groupBy(data.planVersions, (r) => r.planId);
753
+ const liveTimestamps = /* @__PURE__ */ new Set();
754
+ for (const rows of planByKey.values()) {
755
+ const live = findCurrentLive(rows);
756
+ if (live?.publishedAt) liveTimestamps.add(live.publishedAt);
757
+ }
758
+ const liveSorted = [...liveTimestamps].sort();
759
+ const latestLive = liveSorted[liveSorted.length - 1];
760
+ const sortedDesc = [...allPublishedAt].sort((a, b) => Date.parse(b) - Date.parse(a));
761
+ const out = [];
762
+ for (const ts of sortedDesc) {
763
+ if (latestLive && Date.parse(ts) >= Date.parse(latestLive)) continue;
764
+ const tMs = Date.parse(ts) + 1;
765
+ const plans = [];
766
+ for (const [, rows] of planByKey) {
767
+ const live = findLiveAt(rows, tMs);
768
+ if (live) plans.push(resolvePlan(live, null));
769
+ }
770
+ if (plans.length === 0) continue;
771
+ const publishedAtThisEvent = data.planVersions.filter((r) => r.publishedAt === ts);
772
+ const regressionCount = publishedAtThisEvent.filter((r) => !r.nonRegressive).length;
773
+ const author = publishedAtThisEvent[0]?.publishedByUserId ?? null;
774
+ out.push({
775
+ id: `hist-${ts}`,
776
+ kind: "historical",
777
+ status: "ARCHIVED",
778
+ label: dateLabelDe(ts),
779
+ title: summarizeEvent(publishedAtThisEvent),
780
+ description: detailEvent(publishedAtThisEvent),
781
+ asOf: ts,
782
+ createdAt: ts,
783
+ publishedAt: ts,
784
+ authorEmail: author,
785
+ plans: sortPlansFn(plans),
786
+ draftCount: 0,
787
+ regressionCount
788
+ });
789
+ }
790
+ return out;
791
+ }
792
+ function summarizeEvent(rows) {
793
+ if (rows.length === 0) return "Publish-Event";
794
+ if (rows.length === 1) {
795
+ const r = rows[0];
796
+ return `${r.planId} v${r.version} publiziert`;
797
+ }
798
+ return `${rows.length} Versionen publiziert`;
799
+ }
800
+ function detailEvent(rows) {
801
+ if (rows.length === 0) return "";
802
+ const note = rows[0]?.changeNote ?? "";
803
+ return note.length > 0 ? note : "Snapshot rekonstruiert aus per-Entity-Versionen.";
804
+ }
805
+ function sortPlansBy(order) {
806
+ if (!order || order.length === 0) {
807
+ return (rows) => [...rows].sort((a, b) => a.planId.localeCompare(b.planId));
808
+ }
809
+ const idx = {};
810
+ for (let i = 0; i < order.length; i++) idx[order[i]] = i;
811
+ return (rows) => [...rows].sort((a, b) => {
812
+ const ai = idx[a.planId] ?? Number.POSITIVE_INFINITY;
813
+ const bi = idx[b.planId] ?? Number.POSITIVE_INFINITY;
814
+ if (ai !== bi) return ai - bi;
815
+ return a.planId.localeCompare(b.planId);
816
+ });
817
+ }
818
+ function buildSnapshots(data, options = {}) {
819
+ const sortPlansFn = sortPlansBy(options.planSortOrder);
820
+ const drafts = buildDraftsSnapshot(data, sortPlansFn);
821
+ const active = buildActiveSnapshot(data, sortPlansFn);
822
+ const historical = buildHistoricalSnapshots(data, sortPlansFn);
823
+ const out = [drafts];
824
+ if (active) out.push(active);
825
+ out.push(...historical);
826
+ return out;
827
+ }
828
+ function listOpenDrafts(data) {
829
+ return {
830
+ plans: sortByPublishedDesc(data.planVersions).filter((r) => r.publishedAt === null)
831
+ };
832
+ }
833
+
834
+ // src/vue/super-admin-context.ts
835
+ var SUPER_ADMIN_BRAND_KEY = /* @__PURE__ */ Symbol.for(
836
+ "@saasicat/ui-vue/SUPER_ADMIN_BRAND"
837
+ );
838
+ var SUPER_ADMIN_ENDPOINTS_KEY = /* @__PURE__ */ Symbol.for(
839
+ "@saasicat/ui-vue/SUPER_ADMIN_ENDPOINTS"
840
+ );
841
+ var SUPER_ADMIN_EXTENSIONS_KEY = /* @__PURE__ */ Symbol.for(
842
+ "@saasicat/ui-vue/SUPER_ADMIN_EXTENSIONS"
843
+ );
844
+ var SUPER_ADMIN_ACTIONS_KEY = /* @__PURE__ */ Symbol.for(
845
+ "@saasicat/ui-vue/SUPER_ADMIN_ACTIONS"
846
+ );
847
+ var SUPER_ADMIN_MANIFEST_KEY = /* @__PURE__ */ Symbol.for(
848
+ "@saasicat/ui-vue/SUPER_ADMIN_MANIFEST"
849
+ );
850
+ var SUPER_ADMIN_LOGIN_ADAPTER_KEY = /* @__PURE__ */ Symbol.for(
851
+ "@saasicat/ui-vue/SUPER_ADMIN_LOGIN_ADAPTER"
852
+ );
853
+ var SUPER_ADMIN_HTTP_KEY = /* @__PURE__ */ Symbol.for(
854
+ "@saasicat/ui-vue/SUPER_ADMIN_HTTP"
855
+ );
856
+ function buildNavigationGuard(options) {
857
+ const { authGuard, manifestGuard } = options;
858
+ if (!authGuard && !manifestGuard) return null;
859
+ return async (to) => {
860
+ if (to.meta?.public === true) return true;
861
+ if (authGuard) {
862
+ if (!authGuard.isAuthenticated()) return authGuard.onUnauthenticated();
863
+ if (authGuard.isSuperAdmin && !authGuard.isSuperAdmin()) {
864
+ return authGuard.onUnauthenticated();
865
+ }
866
+ }
867
+ if (manifestGuard) {
868
+ try {
869
+ await manifestGuard.ensureLoaded();
870
+ } catch (err) {
871
+ if (manifestGuard.errorRoute && to.path !== manifestGuard.errorRoute) {
872
+ return manifestGuard.errorRoute;
873
+ }
874
+ console.error("[SuperAdmin] manifest load failed", err);
875
+ }
876
+ }
877
+ return true;
878
+ };
879
+ }
880
+
881
+ // src/vue/ui-notify.ts
882
+ var SUPER_ADMIN_NOTIFY_KEY = /* @__PURE__ */ Symbol.for(
883
+ "@saasicat/ui-vue/SUPER_ADMIN_NOTIFY"
884
+ );
885
+
886
+ // src/vue/use-super-admin-context.ts
887
+ var import_vue = require("vue");
888
+ function useSuperAdminExtensions() {
889
+ return (0, import_vue.inject)(SUPER_ADMIN_EXTENSIONS_KEY, {});
890
+ }
891
+ function useSuperAdminActions() {
892
+ return (0, import_vue.inject)(SUPER_ADMIN_ACTIONS_KEY, {});
893
+ }
894
+ function useSuperAdminBrand() {
895
+ const brand = (0, import_vue.inject)(SUPER_ADMIN_BRAND_KEY);
896
+ if (!brand) {
897
+ throw new Error(
898
+ "useSuperAdminBrand(): kein SuperAdmin-Branding im Inject-Scope. Wurde die Komponente innerhalb createSuperAdminApp() gemounted?"
899
+ );
900
+ }
901
+ return brand;
902
+ }
903
+ function useSuperAdminEndpoints() {
904
+ const endpoints = (0, import_vue.inject)(SUPER_ADMIN_ENDPOINTS_KEY);
905
+ if (!endpoints) {
906
+ throw new Error(
907
+ "useSuperAdminEndpoints(): keine Endpoints im Inject-Scope. Wurde die Komponente innerhalb createSuperAdminApp() gemounted?"
908
+ );
909
+ }
910
+ return endpoints;
911
+ }
912
+ function useSuperAdminManifest() {
913
+ const accessor = (0, import_vue.inject)(SUPER_ADMIN_MANIFEST_KEY, null);
914
+ return accessor ? accessor() : null;
915
+ }
916
+ function useSuperAdminLoginAdapter() {
917
+ const adapter = (0, import_vue.inject)(SUPER_ADMIN_LOGIN_ADAPTER_KEY);
918
+ if (!adapter) {
919
+ throw new Error(
920
+ "useSuperAdminLoginAdapter(): kein Login-Adapter registriert. Reiche ihn via createSuperAdminApp({ loginAdapter }) ein."
921
+ );
922
+ }
923
+ return adapter;
924
+ }
925
+ function useSuperAdminHttp() {
926
+ return (0, import_vue.inject)(SUPER_ADMIN_HTTP_KEY, defaultHttpClient());
927
+ }
928
+
929
+ // src/vue/project-page-host.ts
930
+ var import_vue2 = require("vue");
931
+ var import_vue_router = require("vue-router");
932
+ var asyncComponentCache = /* @__PURE__ */ new WeakMap();
933
+ function resolveAsync(extensions, key) {
934
+ let bucket = asyncComponentCache.get(extensions);
935
+ if (!bucket) {
936
+ bucket = /* @__PURE__ */ new Map();
937
+ asyncComponentCache.set(extensions, bucket);
938
+ }
939
+ const cached = bucket.get(key);
940
+ if (cached) return cached;
941
+ const loader = extensions[key];
942
+ if (!loader) return null;
943
+ const cmp = (0, import_vue2.defineAsyncComponent)(loader);
944
+ bucket.set(key, cmp);
945
+ return cmp;
946
+ }
947
+ var ProjectPageHost = (0, import_vue2.defineComponent)({
948
+ name: "ProjectPageHost",
949
+ setup() {
950
+ const route = (0, import_vue_router.useRoute)();
951
+ const extensions = (0, import_vue2.inject)(SUPER_ADMIN_EXTENSIONS_KEY, {});
952
+ const manifestAccessor = (0, import_vue2.inject)(SUPER_ADMIN_MANIFEST_KEY, null);
953
+ const matchingPage = (0, import_vue2.computed)(() => {
954
+ const manifest = manifestAccessor ? manifestAccessor() : null;
955
+ if (!manifest) return null;
956
+ const pages = manifest.navigation?.projectPages ?? [];
957
+ return pages.find((p) => p.route === route.path) ?? null;
958
+ });
959
+ const resolvedComponent = (0, import_vue2.computed)(() => {
960
+ const page = matchingPage.value;
961
+ if (!page) return null;
962
+ return resolveAsync(extensions, page.componentKey);
963
+ });
964
+ return () => {
965
+ const page = matchingPage.value;
966
+ const cmp = resolvedComponent.value;
967
+ if (page && cmp) return (0, import_vue2.h)(cmp);
968
+ return (0, import_vue2.h)("div", { class: "sa-project-page-host__missing", role: "alert" }, [
969
+ (0, import_vue2.h)("h2", "Seite nicht verf\xFCgbar"),
970
+ (0, import_vue2.h)(
971
+ "p",
972
+ page ? `componentKey "${page.componentKey}" ist nicht in der extensions:-Map registriert. Apps m\xFCssen die Komponente in createSuperAdminApp({ extensions }) eintragen.` : `Keine Manifest-Definition f\xFCr ${route.path}. Entweder fehlt die ProjectPage im Manifest, oder die Capability ist nicht aktiv.`
973
+ )
974
+ ]);
975
+ };
976
+ }
977
+ });
978
+ function createProjectPageHostRoute(options) {
979
+ return {
980
+ path: options?.path ?? ":projectPagePath(.+)",
981
+ component: ProjectPageHost,
982
+ meta: { isProjectPageHost: true }
983
+ };
984
+ }
985
+
986
+ // src/vue/create-admin-routes.ts
987
+ function createAdminRoutes(options) {
988
+ return [
989
+ {
990
+ path: "/login",
991
+ name: "login",
992
+ component: options.loginPage,
993
+ meta: { public: true }
994
+ },
995
+ {
996
+ // `meta.public: true` MUST be set, otherwise the route runs through
997
+ // the manifest guard again → redirect loop.
998
+ path: "/admin-error",
999
+ name: "admin-error",
1000
+ component: options.adminErrorPage,
1001
+ meta: { public: true }
1002
+ },
1003
+ {
1004
+ path: "/admin",
1005
+ component: options.adminLayout,
1006
+ children: [
1007
+ { path: "", redirect: options.dashboardPath ?? "/admin/dashboard" },
1008
+ ...options.children,
1009
+ // Catch-all for manifest ProjectPages — Vue Router 4 matches
1010
+ // more specific children first; the host only steps in when
1011
+ // no static page matches. MUST come last.
1012
+ createProjectPageHostRoute()
1013
+ ]
1014
+ },
1015
+ { path: "/", redirect: "/admin" },
1016
+ { path: "/:pathMatch(.*)*", redirect: "/admin" }
1017
+ ];
1018
+ }
1019
+
1020
+ // src/vue/use-api-list.ts
1021
+ var import_vue3 = require("vue");
1022
+ function useApiList(options) {
1023
+ const http = options.http ?? defaultHttpClient();
1024
+ const items = (0, import_vue3.ref)([]);
1025
+ const page = (0, import_vue3.ref)(1);
1026
+ const pageSize = (0, import_vue3.ref)(50);
1027
+ const total = (0, import_vue3.ref)(0);
1028
+ const loading = (0, import_vue3.ref)(false);
1029
+ const error = (0, import_vue3.ref)(null);
1030
+ function buildUrl() {
1031
+ const params = new URLSearchParams();
1032
+ params.set("page", String(page.value));
1033
+ params.set("pageSize", String(pageSize.value));
1034
+ const f = options.filter?.value ?? {};
1035
+ for (const [k, v] of Object.entries(f)) {
1036
+ if (v === void 0 || v === null || v === "") continue;
1037
+ params.set(k, String(v));
1038
+ }
1039
+ const sep = options.endpoint.includes("?") ? "&" : "?";
1040
+ return `${options.endpoint}${sep}${params.toString()}`;
1041
+ }
1042
+ async function load() {
1043
+ loading.value = true;
1044
+ error.value = null;
1045
+ try {
1046
+ const headers = {};
1047
+ const token = options.getAuthToken?.();
1048
+ if (token) headers.Authorization = `Bearer ${token}`;
1049
+ const res = await http(buildUrl(), { method: "GET", headers });
1050
+ if (res.status !== 200) {
1051
+ throw new Error(`Endpoint ${options.endpoint} \u2192 HTTP ${res.status}`);
1052
+ }
1053
+ const raw = await res.json();
1054
+ if (Array.isArray(raw)) {
1055
+ items.value = raw;
1056
+ total.value = raw.length;
1057
+ } else if (raw !== null && typeof raw === "object") {
1058
+ const body = raw;
1059
+ items.value = body.items ?? [];
1060
+ if (typeof body.page === "number") page.value = body.page;
1061
+ if (typeof body.pageSize === "number") pageSize.value = body.pageSize;
1062
+ if (typeof body.total === "number") total.value = body.total;
1063
+ } else {
1064
+ items.value = [];
1065
+ total.value = 0;
1066
+ }
1067
+ } catch (err) {
1068
+ error.value = err instanceof Error ? err : new Error(String(err));
1069
+ items.value = [];
1070
+ total.value = 0;
1071
+ } finally {
1072
+ loading.value = false;
1073
+ }
1074
+ }
1075
+ async function goToPage(p) {
1076
+ page.value = Math.max(1, Math.floor(p));
1077
+ await load();
1078
+ }
1079
+ async function setPageSize(size) {
1080
+ pageSize.value = Math.max(1, Math.floor(size));
1081
+ page.value = 1;
1082
+ await load();
1083
+ }
1084
+ if (options.filter) {
1085
+ (0, import_vue3.watch)(
1086
+ options.filter,
1087
+ () => {
1088
+ page.value = 1;
1089
+ void load();
1090
+ },
1091
+ { deep: true }
1092
+ );
1093
+ }
1094
+ if (options.autoLoad !== false) {
1095
+ Promise.resolve().then(() => void load());
1096
+ }
1097
+ return {
1098
+ items,
1099
+ page,
1100
+ pageSize,
1101
+ total,
1102
+ loading,
1103
+ error,
1104
+ reload: load,
1105
+ goToPage,
1106
+ setPageSize
1107
+ };
1108
+ }
1109
+
1110
+ // src/vue/use-tenants.ts
1111
+ var import_vue4 = require("vue");
1112
+ function useTenants(options) {
1113
+ if (!options?.endpoint) {
1114
+ throw new Error(
1115
+ 'useTenants: `endpoint` ist Pflicht (z. B. "/api/v1/admin/tenants" oder "/api/admin/tenants"). Plattform hat keinen Default, weil Apps unterschiedliche globalPrefix-Konventionen haben.'
1116
+ );
1117
+ }
1118
+ const filter = options.filter ?? (0, import_vue4.ref)({});
1119
+ const list = useApiList({
1120
+ endpoint: options.endpoint,
1121
+ filter,
1122
+ http: options.http,
1123
+ getAuthToken: options.getAuthToken,
1124
+ autoLoad: options.autoLoad
920
1125
  });
921
1126
  return { ...list, filter };
922
1127
  }
923
1128
 
924
- // src/use-audit-entries.ts
1129
+ // src/vue/use-audit-entries.ts
925
1130
  var import_vue5 = require("vue");
926
1131
  function useAuditEntries(options) {
927
1132
  if (!options?.endpoint) {
@@ -940,7 +1145,7 @@ function useAuditEntries(options) {
940
1145
  return { ...list, filter };
941
1146
  }
942
1147
 
943
- // src/use-entitlement.ts
1148
+ // src/vue/use-entitlement.ts
944
1149
  var import_vue6 = require("vue");
945
1150
  function useEntitlement(options) {
946
1151
  if (!options?.endpoint) {
@@ -981,7 +1186,7 @@ function useEntitlement(options) {
981
1186
  return { entitlement, loading, error, load, hasFeature };
982
1187
  }
983
1188
 
984
- // src/use-tenant-manifest.ts
1189
+ // src/vue/use-tenant-manifest.ts
985
1190
  var import_vue7 = require("vue");
986
1191
  function useTenantManifest(options) {
987
1192
  if (!options?.endpoint) {
@@ -1025,7 +1230,7 @@ function useTenantManifest(options) {
1025
1230
  return { manifest, loading, error, load, hasFeature, quotaLimit };
1026
1231
  }
1027
1232
 
1028
- // src/entitlement-provider.ts
1233
+ // src/vue/entitlement-provider.ts
1029
1234
  var import_vue8 = require("vue");
1030
1235
  var ENTITLEMENT_INJECTION_KEY = /* @__PURE__ */ Symbol.for(
1031
1236
  "@saasicat/ui-vue/ENTITLEMENT"
@@ -1037,7 +1242,7 @@ function useInjectedEntitlement() {
1037
1242
  return (0, import_vue8.inject)(ENTITLEMENT_INJECTION_KEY, null);
1038
1243
  }
1039
1244
 
1040
- // src/feature-router-guard.ts
1245
+ // src/vue/feature-router-guard.ts
1041
1246
  function extractRequired(to) {
1042
1247
  const raw = to.meta?.requiresFeature;
1043
1248
  if (!raw) return [];
@@ -1062,7 +1267,7 @@ function buildFeatureRouterGuard(options) {
1062
1267
  };
1063
1268
  }
1064
1269
 
1065
- // src/use-tenant-billing-catalog.ts
1270
+ // src/vue/use-tenant-billing-catalog.ts
1066
1271
  var import_vue9 = require("vue");
1067
1272
  function useTenantBillingCatalog(options = {}) {
1068
1273
  const apiPrefix = trimTrailingSlashes(options.apiPrefix ?? "/billing");
@@ -1120,7 +1325,7 @@ function useTenantBillingCatalog(options = {}) {
1120
1325
  return { plans, featureRegistry, bundles, loading, error, load };
1121
1326
  }
1122
1327
 
1123
- // src/use-tenant-billing.ts
1328
+ // src/vue/use-tenant-billing.ts
1124
1329
  var import_vue10 = require("vue");
1125
1330
  function useTenantBilling(options = {}) {
1126
1331
  const apiPrefix = trimTrailingSlashes(options.apiPrefix ?? "/billing");
@@ -1261,7 +1466,7 @@ function useTenantBilling(options = {}) {
1261
1466
  };
1262
1467
  }
1263
1468
 
1264
- // src/use-subscription-draft.ts
1469
+ // src/vue/use-subscription-draft.ts
1265
1470
  var import_vue11 = require("vue");
1266
1471
  var import_types10 = require("@saasicat/types");
1267
1472
  var DEFAULT_YEARLY_FACTOR = 10;
@@ -1453,141 +1658,7 @@ function useSubscriptionDraft(options) {
1453
1658
  };
1454
1659
  }
1455
1660
 
1456
- // src/pages-tenant/default-i18n.ts
1457
- var DEFAULT_I18N_DE = {
1458
- sectionTitle: "Paket & Verbrauch",
1459
- sectionSubtitle: "Aktuelles Paket, Verbrauch und Bundles.",
1460
- loading: "Lade \u2026",
1461
- noSubscription: "Keine Subscription f\xFCr diesen Mandanten gefunden.",
1462
- activePlan: "Aktuelles Paket",
1463
- cycleMonthly: "Monatlich",
1464
- cycleYearly: "J\xE4hrlich",
1465
- statusActive: "Aktiv",
1466
- statusTrial: "Testphase",
1467
- statusPastDue: "Zahlung \xFCberf\xE4llig",
1468
- statusCanceled: "Gek\xFCndigt",
1469
- statusPendingSales: "Vertrieb-Kl\xE4rung",
1470
- trialEndsAt: "Testphase endet am",
1471
- pilotEndsAt: "Pilot endet am",
1472
- nextBillingDate: "N\xE4chste Abrechnung",
1473
- pendingChange: "Ausstehender Plan-Wechsel",
1474
- changeFromTo: "Wechsel zu",
1475
- changeEffectiveAt: "wirksam ab",
1476
- changePlanButton: "Paket wechseln",
1477
- cancelSubscriptionButton: "Abonnement k\xFCndigen",
1478
- usageTitle: "Verbrauch",
1479
- featuresOverviewTitle: "Leistungsumfang",
1480
- featuresActive: "Enthalten",
1481
- featuresLocked: "Nicht enthalten",
1482
- bundlesStoreTitle: "Bundles",
1483
- bundlesBookedTitle: "Gebuchte Bundles",
1484
- bundlesAvailableTitle: "Verf\xFCgbare Bundles",
1485
- bundlesAvailableEmpty: "Aktuell sind keine zus\xE4tzlichen Bundles verf\xFCgbar.",
1486
- bundlesPerMonth: "netto/Monat",
1487
- bundleBookAction: "Bundle buchen",
1488
- bundleBookInProgress: "Buchung l\xE4uft \u2026",
1489
- bundleCancelAction: "K\xFCndigen",
1490
- bundleReactivateAction: "Reaktivieren",
1491
- bundleReactivateConfirmTitle: "Bundle reaktivieren",
1492
- bundleReactivateConfirmBody: "Die K\xFCndigung wird r\xFCckg\xE4ngig gemacht. Das Bundle bleibt gebucht, l\xE4uft regul\xE4r weiter und wird wieder abgerechnet.",
1493
- bundleCanceledAt: "Gek\xFCndigt zum",
1494
- bundleMinimumTermUntil: "Mindestlaufzeit bis",
1495
- bundleIncludesLabel: "Enth\xE4lt",
1496
- bundleAlreadyBooked: "Bereits gebucht",
1497
- bundleIncompatible: "Nicht kompatibel mit aktuellem Paket",
1498
- bundleMissingRequires: "Ben\xF6tigt",
1499
- bundlePreviewAddTitle: "Bundle buchen",
1500
- bundlePreviewCancelTitle: "Bundle k\xFCndigen",
1501
- bundlePreviewLoading: "Vorschau wird berechnet \u2026",
1502
- bundlePreviewProrationTitle: "Anteilige Abrechnung",
1503
- bundlePreviewProratedNow: "Heute anteilig f\xE4llig",
1504
- bundlePreviewProrationDays: "Tage",
1505
- bundlePreviewNextPeriod: "Regul\xE4r ab n\xE4chster Periode",
1506
- bundlePreviewTrialNote: "W\xE4hrend der Testphase f\xE4llt nichts an \u2014 die Abrechnung beginnt mit der ersten bezahlten Periode.",
1507
- bundlePreviewNoPrice: "F\xFCr den aktuellen Abrechnungszyklus ist kein Listenpreis gepflegt.",
1508
- bundlePreviewMinimumTermLabel: "Mindestlaufzeit",
1509
- bundlePreviewMinimumTermMonths: "Monate, bis",
1510
- bundlePreviewMinimumTermNone: "Keine Mindestlaufzeit",
1511
- bundlePreviewRedundantTitle: "Bereits enthaltene Features (w\xFCrden doppelt bezahlt)",
1512
- bundlePreviewRedundantCoveredByPlan: "bereits im Plan",
1513
- bundlePreviewRedundantCoveredByBundle: "bereits im Bundle",
1514
- bundlePreviewMissingRequiresTitle: "Fehlende Voraussetzungen",
1515
- bundlePreviewBlockersTitle: "Buchung nicht m\xF6glich",
1516
- bundlePreviewWarningsTitle: "Hinweise",
1517
- bundlePreviewEffectiveAt: "K\xFCndigung wirksam zum",
1518
- bundlePreviewSavings: "Ersparnis pro Periode ab Wirksamkeit",
1519
- bundlePreviewConfirmAdd: "Kostenpflichtig buchen",
1520
- bundlePreviewConfirmCancel: "K\xFCndigung best\xE4tigen",
1521
- bundlePreviewInProgress: "Wird ausgef\xFChrt \u2026",
1522
- bundlePreviewClose: "Abbrechen",
1523
- pendingVersionTitle: "Anstehende Plan-\xC4nderung",
1524
- pendingVersionChipNonRegressive: "Verbessernd",
1525
- pendingVersionChipRegressive: "Best\xE4tigung erforderlich",
1526
- pendingVersionEffectiveAt: "Wirksam ab",
1527
- pendingVersionAcceptAction: "\xC4nderungen akzeptieren",
1528
- pendingVersionAcceptInProgress: "Akzeptiere \u2026",
1529
- pendingVersionAcceptedAt: "Akzeptiert am",
1530
- wizardTitle: "Paket wechseln",
1531
- wizardClose: "Schlie\xDFen",
1532
- wizardCurrent: "Aktuell",
1533
- wizardBadgeCurrent: "aktiv",
1534
- wizardBadgePopular: "beliebt",
1535
- wizardPriceUnitMonthly: "netto/Monat",
1536
- wizardPriceUnitYearly: "netto/Jahr",
1537
- wizardPriceOnRequest: "auf Anfrage",
1538
- wizardStepChoose: "Paket w\xE4hlen",
1539
- wizardStepChooseIntro: "W\xE4hle dein Ziel-Paket und den Abrechnungszyklus. Im n\xE4chsten Schritt zeigen wir Verbrauchs-Check und Feature-Diff.",
1540
- wizardStepPreview: "Vorschau",
1541
- wizardStepConfirm: "Best\xE4tigen",
1542
- wizardNext: "Weiter",
1543
- wizardBack: "Zur\xFCck",
1544
- wizardPreviewLoading: "Vorschau wird berechnet \u2026",
1545
- wizardEffectiveAtLabel: "Wirksam ab",
1546
- wizardEffectiveImmediate: "Wirksam sofort",
1547
- wizardProrationTitle: "Anteilige Abrechnung",
1548
- wizardProrationLine: "Mehrbetrag bis Periodenende:",
1549
- wizardProrationDays: "Tage",
1550
- wizardLimitsTitle: "Limit-Vergleich",
1551
- wizardLimitsUsed: "Verbrauch",
1552
- wizardLimitsCurrent: "Aktuell",
1553
- wizardLimitsTarget: "Ziel",
1554
- wizardFeaturesGained: "Neu freigeschaltet",
1555
- wizardFeaturesLost: "Wegfallende Features",
1556
- wizardBlockersTitle: "Verhinderungs-Gr\xFCnde",
1557
- wizardConfirmImmediate: "Der Wechsel wird sofort wirksam.",
1558
- wizardConfirmScheduled: "Der Wechsel wird wirksam zum",
1559
- wizardConfirmAction: "Wechsel best\xE4tigen",
1560
- wizardConfirmInProgress: "Wechsel l\xE4uft \u2026",
1561
- wizardConfirmPriceTitle: "Preis\xFCbersicht",
1562
- wizardConfirmProratedNow: "Heute anteilig f\xE4llig",
1563
- wizardConfirmRecurringNext: "Regul\xE4r ab n\xE4chster Periode",
1564
- wizardConfirmRecurringFrom: "Regul\xE4r f\xE4llig ab",
1565
- wizardConfirmPerCycleMonthly: "pro Monat",
1566
- wizardConfirmPerCycleYearly: "pro Jahr",
1567
- wizardConfirmTrialNote: "W\xE4hrend der Testphase f\xE4llt nichts an.",
1568
- wizardConfirmRecurringTrialEnd: "Regul\xE4r ab Ende der Testphase",
1569
- wizardChangeTypeUpgrade: "Upgrade",
1570
- wizardChangeTypeDowngrade: "Downgrade",
1571
- wizardChangeTypeCycle: "Zyklus-Wechsel",
1572
- wizardChangeTypeNoop: "Keine \xC4nderung",
1573
- packageSnapshotTitle: "Gebuchtes Paket (Snapshot)",
1574
- packageSnapshotSubtitle: "Schreibgesch\xFCtzte Kopie des Pakets, wie es beim Abschluss vermarktet wurde.",
1575
- packageSnapshotCapturedAt: "Erfasst am",
1576
- packageSnapshotOfferRef: "Angebots-Referenz",
1577
- packageSnapshotPlanLabel: "Plan",
1578
- packageSnapshotPlanVersionLabel: "Plan-Version",
1579
- packageSnapshotCycleLabel: "Abrechnungszyklus",
1580
- packageSnapshotBundlesLabel: "Enthaltene Bundles",
1581
- packageSnapshotBundlesEmpty: "Keine Bundles im Paket.",
1582
- packageSnapshotPriceMonthly: "Preis monatlich",
1583
- packageSnapshotPriceYearly: "Preis j\xE4hrlich",
1584
- packageSnapshotPriceTotal: "Gesamtpreis",
1585
- packageSnapshotNone: "Diese Subscription wurde nicht \xFCber ein Webseiten-Angebot abgeschlossen.",
1586
- packageSnapshotShowRaw: "Rohdaten anzeigen",
1587
- packageSnapshotHideRaw: "Rohdaten ausblenden"
1588
- };
1589
-
1590
- // src/use-plan-versions.ts
1661
+ // src/vue/use-plan-versions.ts
1591
1662
  var import_vue12 = require("vue");
1592
1663
  function buildVersionsComposable(label) {
1593
1664
  return function(options) {
@@ -1609,7 +1680,7 @@ function buildVersionsComposable(label) {
1609
1680
  }
1610
1681
  var usePlanVersionsCatalog = buildVersionsComposable("plan-versions");
1611
1682
 
1612
- // src/use-bulk-publish.ts
1683
+ // src/vue/use-bulk-publish.ts
1613
1684
  var import_vue13 = require("vue");
1614
1685
  function useBulkPublish(options) {
1615
1686
  if (!options?.endpoints) {
@@ -1684,7 +1755,7 @@ function useBulkPublish(options) {
1684
1755
  };
1685
1756
  }
1686
1757
 
1687
- // src/use-plan-editor.ts
1758
+ // src/vue/use-plan-editor.ts
1688
1759
  var import_vue14 = require("vue");
1689
1760
  var PlannedOnlyFeatureError = class extends Error {
1690
1761
  constructor(violations) {
@@ -1761,7 +1832,7 @@ function usePlanEditor(manifest, options = {}) {
1761
1832
  };
1762
1833
  }
1763
1834
 
1764
- // src/use-public-boot.ts
1835
+ // src/vue/use-public-boot.ts
1765
1836
  var import_vue15 = require("vue");
1766
1837
  function usePublicBoot(options) {
1767
1838
  const loader = new BootLoader(options);
@@ -1783,7 +1854,7 @@ function usePublicBoot(options) {
1783
1854
  return { boot, loading, error, load };
1784
1855
  }
1785
1856
 
1786
- // src/use-discovery.ts
1857
+ // src/vue/use-discovery.ts
1787
1858
  var import_vue16 = require("vue");
1788
1859
  var DiscoveryLoadError = class extends Error {
1789
1860
  constructor(status, message) {
@@ -1869,7 +1940,7 @@ function useDiscovery(options) {
1869
1940
  return { snapshot, etag, loading, error, load, reload, rescan };
1870
1941
  }
1871
1942
 
1872
- // src/use-catalog-entries.ts
1943
+ // src/vue/use-catalog-entries.ts
1873
1944
  var import_vue17 = require("vue");
1874
1945
  var CatalogEntriesApiError = class extends Error {
1875
1946
  constructor(status, body, message) {
@@ -2018,7 +2089,7 @@ function useCatalogEntries(options) {
2018
2089
  };
2019
2090
  }
2020
2091
 
2021
- // src/use-bundles.ts
2092
+ // src/vue/use-bundles.ts
2022
2093
  var import_vue18 = require("vue");
2023
2094
  var BundlesApiError = class extends Error {
2024
2095
  constructor(status, body, message) {
@@ -2189,7 +2260,7 @@ function useBundleVersions(options) {
2189
2260
  return { versions, loading, error, load, createDraft, updateDraft, publish, discardDraft };
2190
2261
  }
2191
2262
 
2192
- // src/use-bundle-versions-map.ts
2263
+ // src/vue/use-bundle-versions-map.ts
2193
2264
  var import_vue19 = require("vue");
2194
2265
  function useBundleVersionsMap(options) {
2195
2266
  if (!options?.adminEndpoint) {
@@ -2260,7 +2331,7 @@ function useBundleVersionsMap(options) {
2260
2331
  return { versionsByBundle, loading, error, refresh, refreshOne };
2261
2332
  }
2262
2333
 
2263
- // src/use-tenant-subscription-bundles.ts
2334
+ // src/vue/use-tenant-subscription-bundles.ts
2264
2335
  var import_vue20 = require("vue");
2265
2336
  var TenantSubscriptionBundlesApiError = class extends Error {
2266
2337
  constructor(status, body, message) {
@@ -2355,7 +2426,7 @@ function rehydrateDates(raw) {
2355
2426
  };
2356
2427
  }
2357
2428
 
2358
- // src/use-business-types.ts
2429
+ // src/vue/use-business-types.ts
2359
2430
  var import_vue21 = require("vue");
2360
2431
  var BusinessTypesApiError = class extends Error {
2361
2432
  constructor(status, body, message) {
@@ -2524,7 +2595,7 @@ function useBusinessTypeVersions(options) {
2524
2595
  return { versions, loading, error, load, createDraft, updateDraft, publish };
2525
2596
  }
2526
2597
 
2527
- // src/use-marketing-projections.ts
2598
+ // src/vue/use-marketing-projections.ts
2528
2599
  var import_vue22 = require("vue");
2529
2600
  var MarketingProjectionsApiError = class extends Error {
2530
2601
  constructor(status, body, message) {
@@ -2627,7 +2698,7 @@ function useMarketingProjections(options) {
2627
2698
  return { projections, filter, loading, error, setFilter, load, create, update, remove };
2628
2699
  }
2629
2700
 
2630
- // src/use-promotions.ts
2701
+ // src/vue/use-promotions.ts
2631
2702
  var import_vue23 = require("vue");
2632
2703
  var PromotionsApiError = class extends Error {
2633
2704
  constructor(status, body, message) {
@@ -2713,7 +2784,7 @@ function usePromotions(options) {
2713
2784
  return { promotions, loading, error, load, create, update, remove };
2714
2785
  }
2715
2786
 
2716
- // src/use-plans.ts
2787
+ // src/vue/use-plans.ts
2717
2788
  var import_vue24 = require("vue");
2718
2789
  var PlansApiError = class extends Error {
2719
2790
  constructor(status, body, message) {
@@ -2937,7 +3008,7 @@ function usePlanVersions(options) {
2937
3008
  };
2938
3009
  }
2939
3010
 
2940
- // src/use-live-plan-versions.ts
3011
+ // src/vue/use-live-plan-versions.ts
2941
3012
  var import_vue25 = require("vue");
2942
3013
  function useLivePlanVersions(options) {
2943
3014
  if (!options?.adminEndpoint) {
@@ -3012,7 +3083,7 @@ function findLatestLive(versions) {
3012
3083
  })[0];
3013
3084
  }
3014
3085
 
3015
- // src/use-manifest.ts
3086
+ // src/vue/use-manifest.ts
3016
3087
  var import_vue26 = require("vue");
3017
3088
  function useManifest(options) {
3018
3089
  const loader = new ManifestLoader(options);
@@ -3036,91 +3107,46 @@ function useManifest(options) {
3036
3107
  manifest.value = null;
3037
3108
  await load();
3038
3109
  }
3039
- function clearCache() {
3040
- loader.clearCache();
3041
- manifest.value = null;
3042
- }
3043
- return { manifest, loading, error, load, reload, clearCache };
3044
- }
3045
-
3046
- // src/use-nav.ts
3047
- var import_vue27 = require("vue");
3048
- function useNav(manifest, options = {}) {
3049
- const routes = (0, import_vue27.computed)(() => {
3050
- if (!manifest.value) return [];
3051
- return buildRoutes(manifest.value, options);
3052
- });
3053
- const sidebar = (0, import_vue27.computed)(() => buildSidebar(routes.value));
3054
- return { routes, sidebar };
3055
- }
3056
-
3057
- // src/use-actions.ts
3058
- var import_vue28 = require("vue");
3059
- function useActions(manifest, actions) {
3060
- const registry = (0, import_vue28.computed)(() => {
3061
- if (!manifest.value) return null;
3062
- return new ActionRegistry(manifest.value, actions);
3063
- });
3064
- return { registry };
3065
- }
3066
-
3067
- // src/use-tenant-action-flow.ts
3068
- var import_vue30 = require("vue");
3069
-
3070
- // src/use-super-admin-context.ts
3071
- var import_vue29 = require("vue");
3072
- function useSuperAdminExtensions() {
3073
- return (0, import_vue29.inject)(SUPER_ADMIN_EXTENSIONS_KEY, {});
3074
- }
3075
- function useSuperAdminActions() {
3076
- return (0, import_vue29.inject)(SUPER_ADMIN_ACTIONS_KEY, {});
3077
- }
3078
- function useSuperAdminBrand() {
3079
- const brand = (0, import_vue29.inject)(SUPER_ADMIN_BRAND_KEY);
3080
- if (!brand) {
3081
- throw new Error(
3082
- "useSuperAdminBrand(): kein SuperAdmin-Branding im Inject-Scope. Wurde die Komponente innerhalb createSuperAdminApp() gemounted?"
3083
- );
3084
- }
3085
- return brand;
3086
- }
3087
- function useSuperAdminEndpoints() {
3088
- const endpoints = (0, import_vue29.inject)(SUPER_ADMIN_ENDPOINTS_KEY);
3089
- if (!endpoints) {
3090
- throw new Error(
3091
- "useSuperAdminEndpoints(): keine Endpoints im Inject-Scope. Wurde die Komponente innerhalb createSuperAdminApp() gemounted?"
3092
- );
3093
- }
3094
- return endpoints;
3095
- }
3096
- function useSuperAdminManifest() {
3097
- const accessor = (0, import_vue29.inject)(SUPER_ADMIN_MANIFEST_KEY, null);
3098
- return accessor ? accessor() : null;
3099
- }
3100
- function useSuperAdminLoginAdapter() {
3101
- const adapter = (0, import_vue29.inject)(SUPER_ADMIN_LOGIN_ADAPTER_KEY);
3102
- if (!adapter) {
3103
- throw new Error(
3104
- "useSuperAdminLoginAdapter(): kein Login-Adapter registriert. Reiche ihn via createSuperAdminApp({ loginAdapter }) ein."
3105
- );
3106
- }
3107
- return adapter;
3110
+ function clearCache() {
3111
+ loader.clearCache();
3112
+ manifest.value = null;
3113
+ }
3114
+ return { manifest, loading, error, load, reload, clearCache };
3108
3115
  }
3109
- function useSuperAdminHttp() {
3110
- return (0, import_vue29.inject)(SUPER_ADMIN_HTTP_KEY, defaultHttpClient());
3116
+
3117
+ // src/vue/use-nav.ts
3118
+ var import_vue27 = require("vue");
3119
+ function useNav(manifest, options = {}) {
3120
+ const routes = (0, import_vue27.computed)(() => {
3121
+ if (!manifest.value) return [];
3122
+ return buildRoutes(manifest.value, options);
3123
+ });
3124
+ const sidebar = (0, import_vue27.computed)(() => buildSidebar(routes.value));
3125
+ return { routes, sidebar };
3111
3126
  }
3112
3127
 
3113
- // src/use-tenant-action-flow.ts
3128
+ // src/vue/use-actions.ts
3129
+ var import_vue28 = require("vue");
3130
+ function useActions(manifest, actions) {
3131
+ const registry = (0, import_vue28.computed)(() => {
3132
+ if (!manifest.value) return null;
3133
+ return new ActionRegistry(manifest.value, actions);
3134
+ });
3135
+ return { registry };
3136
+ }
3137
+
3138
+ // src/vue/use-tenant-action-flow.ts
3139
+ var import_vue29 = require("vue");
3114
3140
  function useTenantActionFlow(manifest, providers = {}) {
3115
3141
  const handlers = useSuperAdminActions();
3116
- const registry = (0, import_vue30.computed)(() => {
3142
+ const registry = (0, import_vue29.computed)(() => {
3117
3143
  if (!manifest.value) return null;
3118
3144
  return new ActionRegistry(manifest.value, handlers);
3119
3145
  });
3120
- const orphanedDefs = (0, import_vue30.computed)(
3146
+ const orphanedDefs = (0, import_vue29.computed)(
3121
3147
  () => registry.value ? registry.value.listOrphanedDefs() : []
3122
3148
  );
3123
- const availableActions = (0, import_vue30.computed)(() => {
3149
+ const availableActions = (0, import_vue29.computed)(() => {
3124
3150
  const m = manifest.value;
3125
3151
  const reg = registry.value;
3126
3152
  if (!m || !reg) return [];
@@ -3192,8 +3218,8 @@ async function runFlow(def, row, registry, providers) {
3192
3218
  }
3193
3219
  }
3194
3220
 
3195
- // src/use-platform-tenant-actions.ts
3196
- var import_vue31 = require("vue");
3221
+ // src/vue/use-platform-tenant-actions.ts
3222
+ var import_vue30 = require("vue");
3197
3223
  var DEFAULT_VISIBLE_FOR_ROW = (def, row) => {
3198
3224
  if (def.actionKey === "tenants.suspend") return row.isActive;
3199
3225
  if (def.actionKey === "tenants.reactivate") return !row.isActive;
@@ -3225,7 +3251,7 @@ function usePlatformTenantActions(options) {
3225
3251
  const iconForActionKey = options.iconForActionKey ?? DEFAULT_ICON_FOR_ACTION_KEY;
3226
3252
  const toneForActionKey = options.toneForActionKey ?? DEFAULT_TONE_FOR_ACTION_KEY;
3227
3253
  const visibleForRow = options.visibleForRow ?? DEFAULT_VISIBLE_FOR_ROW;
3228
- const mfa = (0, import_vue31.ref)({ show: false, description: "", error: "" });
3254
+ const mfa = (0, import_vue30.ref)({ show: false, description: "", error: "" });
3229
3255
  let pendingMfaResolve = null;
3230
3256
  function showMfaDialog(def, ctx) {
3231
3257
  return new Promise((resolve) => {
@@ -3250,7 +3276,7 @@ function usePlatformTenantActions(options) {
3250
3276
  pendingMfaResolve(null);
3251
3277
  }
3252
3278
  }
3253
- const confirmDialog = (0, import_vue31.ref)({
3279
+ const confirmDialog = (0, import_vue30.ref)({
3254
3280
  show: false,
3255
3281
  def: null,
3256
3282
  row: null
@@ -3292,7 +3318,7 @@ function usePlatformTenantActions(options) {
3292
3318
  },
3293
3319
  visibleForRow
3294
3320
  });
3295
- const manifestActions = (0, import_vue31.computed)(
3321
+ const manifestActions = (0, import_vue30.computed)(
3296
3322
  () => flow.availableActions.value.map((def) => ({
3297
3323
  id: def.id,
3298
3324
  label: def.label,
@@ -3306,7 +3332,7 @@ function usePlatformTenantActions(options) {
3306
3332
  }
3307
3333
  }))
3308
3334
  );
3309
- const realOrphans = (0, import_vue31.computed)(() => {
3335
+ const realOrphans = (0, import_vue30.computed)(() => {
3310
3336
  const caps = options.manifest.value?.capabilities ?? {};
3311
3337
  const defs = options.manifest.value?.tenants?.actions ?? [];
3312
3338
  const defByActionKey = new Map(defs.map((d) => [d.actionKey, d]));
@@ -3314,298 +3340,52 @@ function usePlatformTenantActions(options) {
3314
3340
  const def = defByActionKey.get(actionKey);
3315
3341
  const requiredCap = def?.requiredCapability;
3316
3342
  if (!requiredCap) return true;
3317
- return caps[requiredCap] === true;
3318
- });
3319
- });
3320
- return {
3321
- manifestActions,
3322
- realOrphans,
3323
- mfa,
3324
- onMfaConfirm,
3325
- onMfaDialogVisibility,
3326
- confirmDialog,
3327
- onConfirmSubmit,
3328
- onConfirmCancel,
3329
- onConfirmDialogVisibility
3330
- };
3331
- }
3332
-
3333
- // src/use-batch-columns.ts
3334
- var import_vue32 = require("vue");
3335
- function useBatchColumns(manifest, tenantIds, options = {}) {
3336
- const fetcher = new BatchColumnFetcher(options);
3337
- const data = (0, import_vue32.ref)({});
3338
- const loading = (0, import_vue32.ref)(false);
3339
- const error = (0, import_vue32.ref)(null);
3340
- async function load() {
3341
- if (!manifest.value || tenantIds.value.length === 0) {
3342
- data.value = {};
3343
- return;
3344
- }
3345
- loading.value = true;
3346
- error.value = null;
3347
- try {
3348
- data.value = await fetcher.fetchAll(manifest.value, tenantIds.value);
3349
- } catch (err) {
3350
- error.value = err instanceof Error ? err : new Error(String(err));
3351
- data.value = {};
3352
- } finally {
3353
- loading.value = false;
3354
- }
3355
- }
3356
- (0, import_vue32.watch)([manifest, tenantIds], () => {
3357
- void load();
3358
- });
3359
- return { data, loading, error, reload: load };
3360
- }
3361
-
3362
- // src/plan-versions-catalog.ts
3363
- var dec = (s) => Number(s);
3364
- function extractQuotas(p) {
3365
- if (p.quotas && Object.keys(p.quotas).length > 0) {
3366
- return { ...p.quotas };
3367
- }
3368
- const out = {};
3369
- if (typeof p.maxUsers === "number") out.users = p.maxUsers;
3370
- if (typeof p.maxVehicles === "number") out.vehicles = p.maxVehicles;
3371
- if (typeof p.maxStorageGb === "number") out.storageGb = p.maxStorageGb;
3372
- return out;
3373
- }
3374
- var sortByPublishedDesc = (rows) => [...rows].sort((a, b) => {
3375
- const at = a.publishedAt ? Date.parse(a.publishedAt) : Number.POSITIVE_INFINITY;
3376
- const bt = b.publishedAt ? Date.parse(b.publishedAt) : Number.POSITIVE_INFINITY;
3377
- if (at !== bt) return bt - at;
3378
- return b.version - a.version;
3379
- });
3380
- function findLiveAt(rows, asOfMs) {
3381
- const candidates = rows.filter((r) => {
3382
- if (r.publishedAt === null) return false;
3383
- if (Date.parse(r.publishedAt) > asOfMs) return false;
3384
- if (r.supersededAt !== null && Date.parse(r.supersededAt) <= asOfMs) return false;
3385
- return true;
3386
- });
3387
- if (candidates.length === 0) return null;
3388
- return [...candidates].sort((a, b) => b.version - a.version)[0] ?? null;
3389
- }
3390
- function findCurrentLive(rows) {
3391
- return rows.find((r) => r.publishedAt !== null && r.supersededAt === null) ?? null;
3392
- }
3393
- function findDraft(rows) {
3394
- return rows.find((r) => r.publishedAt === null) ?? null;
3395
- }
3396
- function groupBy(rows, keyOf) {
3397
- const out = /* @__PURE__ */ new Map();
3398
- for (const r of rows) {
3399
- const k = keyOf(r);
3400
- const list = out.get(k) ?? [];
3401
- list.push(r);
3402
- out.set(k, list);
3403
- }
3404
- return out;
3405
- }
3406
- function resolvePlan(source, liveBase) {
3407
- const quotas = extractQuotas(source);
3408
- return {
3409
- source,
3410
- liveBase,
3411
- isDraft: source.publishedAt === null,
3412
- planId: source.planId,
3413
- features: source.features,
3414
- quotas,
3415
- monthlyNet: dec(source.monthlyNet),
3416
- yearlyNet: dec(source.yearlyNet),
3417
- marketed: source.marketed,
3418
- version: source.version,
3419
- maxUsers: quotas.users,
3420
- maxVehicles: quotas.vehicles,
3421
- maxStorageGb: quotas.storageGb
3422
- };
3423
- }
3424
- function dateLabelDe(iso) {
3425
- return new Date(iso).toLocaleDateString("de-DE", {
3426
- day: "2-digit",
3427
- month: "short",
3428
- year: "numeric"
3429
- });
3430
- }
3431
- function buildDraftsSnapshot(data, sortPlansFn) {
3432
- const planByKey = groupBy(data.planVersions, (r) => r.planId);
3433
- const plans = [];
3434
- for (const [, rows] of planByKey) {
3435
- const draft = findDraft(rows);
3436
- const live = findCurrentLive(rows);
3437
- if (draft) plans.push(resolvePlan(draft, live));
3438
- else if (live) plans.push(resolvePlan(live, null));
3439
- }
3440
- const draftCount = plans.filter((p) => p.isDraft).length;
3441
- return {
3442
- id: "drafts",
3443
- kind: "drafts",
3444
- status: "DRAFT",
3445
- label: "Arbeitsstand",
3446
- title: draftCount === 0 ? "Keine offenen Drafts" : `${draftCount} offene${draftCount === 1 ? "r" : ""} Draft${draftCount === 1 ? "" : "s"} bereit f\xFCr Publish`,
3447
- description: draftCount === 0 ? "Lege einen Draft an, um Limits, Preise oder Features zu \xE4ndern. Drafts sind ohne MFA editierbar und werden erst beim Publish wirksam." : "Diese Tabelle zeigt, wie der Catalog nach Publish aller Drafts aussehen w\xFCrde. Bestehende Mandanten bleiben auf der vorherigen Version, bis sie aktiv migriert werden.",
3448
- asOf: null,
3449
- createdAt: null,
3450
- publishedAt: null,
3451
- authorEmail: null,
3452
- plans: sortPlansFn(plans),
3453
- draftCount,
3454
- regressionCount: 0
3455
- };
3456
- }
3457
- function buildActiveSnapshot(data, sortPlansFn) {
3458
- const planByKey = groupBy(data.planVersions, (r) => r.planId);
3459
- const plans = [];
3460
- for (const [, rows] of planByKey) {
3461
- const live = findCurrentLive(rows);
3462
- if (live) plans.push(resolvePlan(live, null));
3463
- }
3464
- if (plans.length === 0) return null;
3465
- const publishedDates = plans.map((p) => p.source.publishedAt).filter((t) => t !== null).sort();
3466
- const latestPublished = publishedDates[publishedDates.length - 1];
3467
- return {
3468
- id: "active",
3469
- kind: "active",
3470
- status: "ACTIVE",
3471
- label: "Aktuell",
3472
- title: "Live-Catalog",
3473
- description: "Diese Plan-Versionen sind aktuell f\xFCr neue Onboardings und f\xFCr Renewals nach Stichtag aktiv.",
3474
- asOf: latestPublished ?? null,
3475
- createdAt: null,
3476
- publishedAt: latestPublished ?? null,
3477
- authorEmail: null,
3478
- plans: sortPlansFn(plans),
3479
- draftCount: 0,
3480
- regressionCount: 0
3481
- };
3482
- }
3483
- function buildHistoricalSnapshots(data, sortPlansFn) {
3484
- const allPublishedAt = /* @__PURE__ */ new Set();
3485
- for (const r of data.planVersions) if (r.publishedAt) allPublishedAt.add(r.publishedAt);
3486
- if (allPublishedAt.size === 0) return [];
3487
- const planByKey = groupBy(data.planVersions, (r) => r.planId);
3488
- const liveTimestamps = /* @__PURE__ */ new Set();
3489
- for (const rows of planByKey.values()) {
3490
- const live = findCurrentLive(rows);
3491
- if (live?.publishedAt) liveTimestamps.add(live.publishedAt);
3492
- }
3493
- const liveSorted = [...liveTimestamps].sort();
3494
- const latestLive = liveSorted[liveSorted.length - 1];
3495
- const sortedDesc = [...allPublishedAt].sort((a, b) => Date.parse(b) - Date.parse(a));
3496
- const out = [];
3497
- for (const ts of sortedDesc) {
3498
- if (latestLive && Date.parse(ts) >= Date.parse(latestLive)) continue;
3499
- const tMs = Date.parse(ts) + 1;
3500
- const plans = [];
3501
- for (const [, rows] of planByKey) {
3502
- const live = findLiveAt(rows, tMs);
3503
- if (live) plans.push(resolvePlan(live, null));
3504
- }
3505
- if (plans.length === 0) continue;
3506
- const publishedAtThisEvent = data.planVersions.filter((r) => r.publishedAt === ts);
3507
- const regressionCount = publishedAtThisEvent.filter((r) => !r.nonRegressive).length;
3508
- const author = publishedAtThisEvent[0]?.publishedByUserId ?? null;
3509
- out.push({
3510
- id: `hist-${ts}`,
3511
- kind: "historical",
3512
- status: "ARCHIVED",
3513
- label: dateLabelDe(ts),
3514
- title: summarizeEvent(publishedAtThisEvent),
3515
- description: detailEvent(publishedAtThisEvent),
3516
- asOf: ts,
3517
- createdAt: ts,
3518
- publishedAt: ts,
3519
- authorEmail: author,
3520
- plans: sortPlansFn(plans),
3521
- draftCount: 0,
3522
- regressionCount
3523
- });
3524
- }
3525
- return out;
3526
- }
3527
- function summarizeEvent(rows) {
3528
- if (rows.length === 0) return "Publish-Event";
3529
- if (rows.length === 1) {
3530
- const r = rows[0];
3531
- return `${r.planId} v${r.version} publiziert`;
3532
- }
3533
- return `${rows.length} Versionen publiziert`;
3534
- }
3535
- function detailEvent(rows) {
3536
- if (rows.length === 0) return "";
3537
- const note = rows[0]?.changeNote ?? "";
3538
- return note.length > 0 ? note : "Snapshot rekonstruiert aus per-Entity-Versionen.";
3539
- }
3540
- function sortPlansBy(order) {
3541
- if (!order || order.length === 0) {
3542
- return (rows) => [...rows].sort((a, b) => a.planId.localeCompare(b.planId));
3543
- }
3544
- const idx = {};
3545
- for (let i = 0; i < order.length; i++) idx[order[i]] = i;
3546
- return (rows) => [...rows].sort((a, b) => {
3547
- const ai = idx[a.planId] ?? Number.POSITIVE_INFINITY;
3548
- const bi = idx[b.planId] ?? Number.POSITIVE_INFINITY;
3549
- if (ai !== bi) return ai - bi;
3550
- return a.planId.localeCompare(b.planId);
3343
+ return caps[requiredCap] === true;
3344
+ });
3551
3345
  });
3552
- }
3553
- function buildSnapshots(data, options = {}) {
3554
- const sortPlansFn = sortPlansBy(options.planSortOrder);
3555
- const drafts = buildDraftsSnapshot(data, sortPlansFn);
3556
- const active = buildActiveSnapshot(data, sortPlansFn);
3557
- const historical = buildHistoricalSnapshots(data, sortPlansFn);
3558
- const out = [drafts];
3559
- if (active) out.push(active);
3560
- out.push(...historical);
3561
- return out;
3562
- }
3563
- function listOpenDrafts(data) {
3564
3346
  return {
3565
- plans: sortByPublishedDesc(data.planVersions).filter((r) => r.publishedAt === null)
3347
+ manifestActions,
3348
+ realOrphans,
3349
+ mfa,
3350
+ onMfaConfirm,
3351
+ onMfaDialogVisibility,
3352
+ confirmDialog,
3353
+ onConfirmSubmit,
3354
+ onConfirmCancel,
3355
+ onConfirmDialogVisibility
3566
3356
  };
3567
3357
  }
3568
3358
 
3569
- // src/components/dialogs/types.ts
3570
- var DEFAULT_PILOT_COPY = {
3571
- tenantSubtitle: "Stammdaten des Mandanten",
3572
- tenantNameLabel: "Name des Mandanten",
3573
- tenantNamePlaceholder: "z. B. Muster GmbH",
3574
- slugPlaceholder: "muster-mandant",
3575
- adminEmailPlaceholder: "admin@example.com",
3576
- notePlaceholder: "z. B. Sales-Pilot \xB7 Demo-Zugang"
3577
- };
3578
-
3579
- // src/components/bundle-editor/catalog-i18n.ts
3580
- var CATALOG_DEFAULT_LOCALE = "de";
3581
- function localized(i18n, locale, field) {
3582
- if (locale === CATALOG_DEFAULT_LOCALE) return void 0;
3583
- const value = i18n?.[locale]?.[field];
3584
- return value && value.trim().length > 0 ? value : void 0;
3585
- }
3586
- function resolveLabel(baseLabel, i18n, locale) {
3587
- return localized(i18n, locale, "label") ?? (baseLabel || void 0);
3588
- }
3589
- function buildFeatureRegistry(featureCatalog, locale) {
3590
- const registry = {};
3591
- for (const feature of featureCatalog) {
3592
- const label = resolveLabel(feature.label, feature.i18n, locale);
3593
- if (label) registry[feature.featureKey] = { label, core: feature.core };
3594
- }
3595
- return registry;
3596
- }
3597
- function buildQuotaRegistry(quotaCatalog, locale) {
3598
- const registry = {};
3599
- for (const quota of quotaCatalog) {
3600
- registry[quota.quotaKey] = {
3601
- label: resolveLabel(quota.label, quota.i18n, locale),
3602
- unit: localized(quota.i18n, locale, "unit") ?? (quota.unit || void 0)
3603
- };
3359
+ // src/vue/use-batch-columns.ts
3360
+ var import_vue31 = require("vue");
3361
+ function useBatchColumns(manifest, tenantIds, options = {}) {
3362
+ const fetcher = new BatchColumnFetcher(options);
3363
+ const data = (0, import_vue31.ref)({});
3364
+ const loading = (0, import_vue31.ref)(false);
3365
+ const error = (0, import_vue31.ref)(null);
3366
+ async function load() {
3367
+ if (!manifest.value || tenantIds.value.length === 0) {
3368
+ data.value = {};
3369
+ return;
3370
+ }
3371
+ loading.value = true;
3372
+ error.value = null;
3373
+ try {
3374
+ data.value = await fetcher.fetchAll(manifest.value, tenantIds.value);
3375
+ } catch (err) {
3376
+ error.value = err instanceof Error ? err : new Error(String(err));
3377
+ data.value = {};
3378
+ } finally {
3379
+ loading.value = false;
3380
+ }
3604
3381
  }
3605
- return registry;
3382
+ (0, import_vue31.watch)([manifest, tenantIds], () => {
3383
+ void load();
3384
+ });
3385
+ return { data, loading, error, reload: load };
3606
3386
  }
3607
3387
 
3608
- // src/platform-loaders.ts
3388
+ // src/vue/platform-loaders.ts
3609
3389
  function resolveEndpoints(endpoints) {
3610
3390
  return {
3611
3391
  apiBase: endpoints.apiBase,
@@ -3629,15 +3409,15 @@ function createPlatformLoaders(options) {
3629
3409
  return { bootLoader, manifestLoader };
3630
3410
  }
3631
3411
 
3632
- // src/manifest-store-factory.ts
3633
- var import_pinia2 = require("pinia");
3634
- var import_vue33 = require("vue");
3412
+ // src/vue/manifest-store-factory.ts
3413
+ var import_pinia = require("pinia");
3414
+ var import_vue32 = require("vue");
3635
3415
  function createManifestStore(options) {
3636
- return (0, import_pinia2.defineStore)(options.id ?? "admin-manifest", () => {
3637
- const manifest = (0, import_vue33.ref)(null);
3638
- const loading = (0, import_vue33.ref)(false);
3639
- const error = (0, import_vue33.ref)(null);
3640
- const loaded = (0, import_vue33.ref)(false);
3416
+ return (0, import_pinia.defineStore)(options.id ?? "admin-manifest", () => {
3417
+ const manifest = (0, import_vue32.ref)(null);
3418
+ const loading = (0, import_vue32.ref)(false);
3419
+ const error = (0, import_vue32.ref)(null);
3420
+ const loaded = (0, import_vue32.ref)(false);
3641
3421
  let inflight = null;
3642
3422
  async function load() {
3643
3423
  loading.value = true;
@@ -3675,6 +3455,179 @@ function createManifestStore(options) {
3675
3455
  return { manifest, loading, error, loaded, ensureLoaded, clearCache, reload };
3676
3456
  });
3677
3457
  }
3458
+
3459
+ // src/components/dialogs/types.ts
3460
+ var DEFAULT_PILOT_COPY = {
3461
+ tenantSubtitle: "Stammdaten des Mandanten",
3462
+ tenantNameLabel: "Name des Mandanten",
3463
+ tenantNamePlaceholder: "z. B. Muster GmbH",
3464
+ slugPlaceholder: "muster-mandant",
3465
+ adminEmailPlaceholder: "admin@example.com",
3466
+ notePlaceholder: "z. B. Sales-Pilot \xB7 Demo-Zugang"
3467
+ };
3468
+
3469
+ // src/components/bundle-editor/catalog-i18n.ts
3470
+ var CATALOG_DEFAULT_LOCALE = "de";
3471
+ function localized(i18n, locale, field) {
3472
+ if (locale === CATALOG_DEFAULT_LOCALE) return void 0;
3473
+ const value = i18n?.[locale]?.[field];
3474
+ return value && value.trim().length > 0 ? value : void 0;
3475
+ }
3476
+ function resolveLabel(baseLabel, i18n, locale) {
3477
+ return localized(i18n, locale, "label") ?? (baseLabel || void 0);
3478
+ }
3479
+ function buildFeatureRegistry(featureCatalog, locale) {
3480
+ const registry = {};
3481
+ for (const feature of featureCatalog) {
3482
+ const label = resolveLabel(feature.label, feature.i18n, locale);
3483
+ if (label) registry[feature.featureKey] = { label, core: feature.core };
3484
+ }
3485
+ return registry;
3486
+ }
3487
+ function buildQuotaRegistry(quotaCatalog, locale) {
3488
+ const registry = {};
3489
+ for (const quota of quotaCatalog) {
3490
+ registry[quota.quotaKey] = {
3491
+ label: resolveLabel(quota.label, quota.i18n, locale),
3492
+ unit: localized(quota.i18n, locale, "unit") ?? (quota.unit || void 0)
3493
+ };
3494
+ }
3495
+ return registry;
3496
+ }
3497
+
3498
+ // src/pages-tenant/default-i18n.ts
3499
+ var DEFAULT_I18N_DE = {
3500
+ sectionTitle: "Paket & Verbrauch",
3501
+ sectionSubtitle: "Aktuelles Paket, Verbrauch und Bundles.",
3502
+ loading: "Lade \u2026",
3503
+ noSubscription: "Keine Subscription f\xFCr diesen Mandanten gefunden.",
3504
+ activePlan: "Aktuelles Paket",
3505
+ cycleMonthly: "Monatlich",
3506
+ cycleYearly: "J\xE4hrlich",
3507
+ statusActive: "Aktiv",
3508
+ statusTrial: "Testphase",
3509
+ statusPastDue: "Zahlung \xFCberf\xE4llig",
3510
+ statusCanceled: "Gek\xFCndigt",
3511
+ statusPendingSales: "Vertrieb-Kl\xE4rung",
3512
+ trialEndsAt: "Testphase endet am",
3513
+ pilotEndsAt: "Pilot endet am",
3514
+ nextBillingDate: "N\xE4chste Abrechnung",
3515
+ pendingChange: "Ausstehender Plan-Wechsel",
3516
+ changeFromTo: "Wechsel zu",
3517
+ changeEffectiveAt: "wirksam ab",
3518
+ changePlanButton: "Paket wechseln",
3519
+ cancelSubscriptionButton: "Abonnement k\xFCndigen",
3520
+ usageTitle: "Verbrauch",
3521
+ featuresOverviewTitle: "Leistungsumfang",
3522
+ featuresActive: "Enthalten",
3523
+ featuresLocked: "Nicht enthalten",
3524
+ bundlesStoreTitle: "Bundles",
3525
+ bundlesBookedTitle: "Gebuchte Bundles",
3526
+ bundlesAvailableTitle: "Verf\xFCgbare Bundles",
3527
+ bundlesAvailableEmpty: "Aktuell sind keine zus\xE4tzlichen Bundles verf\xFCgbar.",
3528
+ bundlesPerMonth: "netto/Monat",
3529
+ bundleBookAction: "Bundle buchen",
3530
+ bundleBookInProgress: "Buchung l\xE4uft \u2026",
3531
+ bundleCancelAction: "K\xFCndigen",
3532
+ bundleReactivateAction: "Reaktivieren",
3533
+ bundleReactivateConfirmTitle: "Bundle reaktivieren",
3534
+ bundleReactivateConfirmBody: "Die K\xFCndigung wird r\xFCckg\xE4ngig gemacht. Das Bundle bleibt gebucht, l\xE4uft regul\xE4r weiter und wird wieder abgerechnet.",
3535
+ bundleCanceledAt: "Gek\xFCndigt zum",
3536
+ bundleMinimumTermUntil: "Mindestlaufzeit bis",
3537
+ bundleIncludesLabel: "Enth\xE4lt",
3538
+ bundleAlreadyBooked: "Bereits gebucht",
3539
+ bundleIncompatible: "Nicht kompatibel mit aktuellem Paket",
3540
+ bundleMissingRequires: "Ben\xF6tigt",
3541
+ bundlePreviewAddTitle: "Bundle buchen",
3542
+ bundlePreviewCancelTitle: "Bundle k\xFCndigen",
3543
+ bundlePreviewLoading: "Vorschau wird berechnet \u2026",
3544
+ bundlePreviewProrationTitle: "Anteilige Abrechnung",
3545
+ bundlePreviewProratedNow: "Heute anteilig f\xE4llig",
3546
+ bundlePreviewProrationDays: "Tage",
3547
+ bundlePreviewNextPeriod: "Regul\xE4r ab n\xE4chster Periode",
3548
+ bundlePreviewTrialNote: "W\xE4hrend der Testphase f\xE4llt nichts an \u2014 die Abrechnung beginnt mit der ersten bezahlten Periode.",
3549
+ bundlePreviewNoPrice: "F\xFCr den aktuellen Abrechnungszyklus ist kein Listenpreis gepflegt.",
3550
+ bundlePreviewMinimumTermLabel: "Mindestlaufzeit",
3551
+ bundlePreviewMinimumTermMonths: "Monate, bis",
3552
+ bundlePreviewMinimumTermNone: "Keine Mindestlaufzeit",
3553
+ bundlePreviewRedundantTitle: "Bereits enthaltene Features (w\xFCrden doppelt bezahlt)",
3554
+ bundlePreviewRedundantCoveredByPlan: "bereits im Plan",
3555
+ bundlePreviewRedundantCoveredByBundle: "bereits im Bundle",
3556
+ bundlePreviewMissingRequiresTitle: "Fehlende Voraussetzungen",
3557
+ bundlePreviewBlockersTitle: "Buchung nicht m\xF6glich",
3558
+ bundlePreviewWarningsTitle: "Hinweise",
3559
+ bundlePreviewEffectiveAt: "K\xFCndigung wirksam zum",
3560
+ bundlePreviewSavings: "Ersparnis pro Periode ab Wirksamkeit",
3561
+ bundlePreviewConfirmAdd: "Kostenpflichtig buchen",
3562
+ bundlePreviewConfirmCancel: "K\xFCndigung best\xE4tigen",
3563
+ bundlePreviewInProgress: "Wird ausgef\xFChrt \u2026",
3564
+ bundlePreviewClose: "Abbrechen",
3565
+ pendingVersionTitle: "Anstehende Plan-\xC4nderung",
3566
+ pendingVersionChipNonRegressive: "Verbessernd",
3567
+ pendingVersionChipRegressive: "Best\xE4tigung erforderlich",
3568
+ pendingVersionEffectiveAt: "Wirksam ab",
3569
+ pendingVersionAcceptAction: "\xC4nderungen akzeptieren",
3570
+ pendingVersionAcceptInProgress: "Akzeptiere \u2026",
3571
+ pendingVersionAcceptedAt: "Akzeptiert am",
3572
+ wizardTitle: "Paket wechseln",
3573
+ wizardClose: "Schlie\xDFen",
3574
+ wizardCurrent: "Aktuell",
3575
+ wizardBadgeCurrent: "aktiv",
3576
+ wizardBadgePopular: "beliebt",
3577
+ wizardPriceUnitMonthly: "netto/Monat",
3578
+ wizardPriceUnitYearly: "netto/Jahr",
3579
+ wizardPriceOnRequest: "auf Anfrage",
3580
+ wizardStepChoose: "Paket w\xE4hlen",
3581
+ wizardStepChooseIntro: "W\xE4hle dein Ziel-Paket und den Abrechnungszyklus. Im n\xE4chsten Schritt zeigen wir Verbrauchs-Check und Feature-Diff.",
3582
+ wizardStepPreview: "Vorschau",
3583
+ wizardStepConfirm: "Best\xE4tigen",
3584
+ wizardNext: "Weiter",
3585
+ wizardBack: "Zur\xFCck",
3586
+ wizardPreviewLoading: "Vorschau wird berechnet \u2026",
3587
+ wizardEffectiveAtLabel: "Wirksam ab",
3588
+ wizardEffectiveImmediate: "Wirksam sofort",
3589
+ wizardProrationTitle: "Anteilige Abrechnung",
3590
+ wizardProrationLine: "Mehrbetrag bis Periodenende:",
3591
+ wizardProrationDays: "Tage",
3592
+ wizardLimitsTitle: "Limit-Vergleich",
3593
+ wizardLimitsUsed: "Verbrauch",
3594
+ wizardLimitsCurrent: "Aktuell",
3595
+ wizardLimitsTarget: "Ziel",
3596
+ wizardFeaturesGained: "Neu freigeschaltet",
3597
+ wizardFeaturesLost: "Wegfallende Features",
3598
+ wizardBlockersTitle: "Verhinderungs-Gr\xFCnde",
3599
+ wizardConfirmImmediate: "Der Wechsel wird sofort wirksam.",
3600
+ wizardConfirmScheduled: "Der Wechsel wird wirksam zum",
3601
+ wizardConfirmAction: "Wechsel best\xE4tigen",
3602
+ wizardConfirmInProgress: "Wechsel l\xE4uft \u2026",
3603
+ wizardConfirmPriceTitle: "Preis\xFCbersicht",
3604
+ wizardConfirmProratedNow: "Heute anteilig f\xE4llig",
3605
+ wizardConfirmRecurringNext: "Regul\xE4r ab n\xE4chster Periode",
3606
+ wizardConfirmRecurringFrom: "Regul\xE4r f\xE4llig ab",
3607
+ wizardConfirmPerCycleMonthly: "pro Monat",
3608
+ wizardConfirmPerCycleYearly: "pro Jahr",
3609
+ wizardConfirmTrialNote: "W\xE4hrend der Testphase f\xE4llt nichts an.",
3610
+ wizardConfirmRecurringTrialEnd: "Regul\xE4r ab Ende der Testphase",
3611
+ wizardChangeTypeUpgrade: "Upgrade",
3612
+ wizardChangeTypeDowngrade: "Downgrade",
3613
+ wizardChangeTypeCycle: "Zyklus-Wechsel",
3614
+ wizardChangeTypeNoop: "Keine \xC4nderung",
3615
+ packageSnapshotTitle: "Gebuchtes Paket (Snapshot)",
3616
+ packageSnapshotSubtitle: "Schreibgesch\xFCtzte Kopie des Pakets, wie es beim Abschluss vermarktet wurde.",
3617
+ packageSnapshotCapturedAt: "Erfasst am",
3618
+ packageSnapshotOfferRef: "Angebots-Referenz",
3619
+ packageSnapshotPlanLabel: "Plan",
3620
+ packageSnapshotPlanVersionLabel: "Plan-Version",
3621
+ packageSnapshotCycleLabel: "Abrechnungszyklus",
3622
+ packageSnapshotBundlesLabel: "Enthaltene Bundles",
3623
+ packageSnapshotBundlesEmpty: "Keine Bundles im Paket.",
3624
+ packageSnapshotPriceMonthly: "Preis monatlich",
3625
+ packageSnapshotPriceYearly: "Preis j\xE4hrlich",
3626
+ packageSnapshotPriceTotal: "Gesamtpreis",
3627
+ packageSnapshotNone: "Diese Subscription wurde nicht \xFCber ein Webseiten-Angebot abgeschlossen.",
3628
+ packageSnapshotShowRaw: "Rohdaten anzeigen",
3629
+ packageSnapshotHideRaw: "Rohdaten ausblenden"
3630
+ };
3678
3631
  // Annotate the CommonJS export names for ESM import in node:
3679
3632
  0 && (module.exports = {
3680
3633
  ADMIN_UI_VERSION,
@@ -3711,6 +3664,7 @@ function createManifestStore(options) {
3711
3664
  SUPER_ADMIN_HTTP_KEY,
3712
3665
  SUPER_ADMIN_LOGIN_ADAPTER_KEY,
3713
3666
  SUPER_ADMIN_MANIFEST_KEY,
3667
+ SUPER_ADMIN_NOTIFY_KEY,
3714
3668
  TenantSubscriptionBundlesApiError,
3715
3669
  buildFeatureRegistry,
3716
3670
  buildFeatureRouterGuard,
@@ -3723,7 +3677,6 @@ function createManifestStore(options) {
3723
3677
  createManifestStore,
3724
3678
  createPlatformLoaders,
3725
3679
  createProjectPageHostRoute,
3726
- createSuperAdminApp,
3727
3680
  defaultHttpClient,
3728
3681
  defaultIconForActionKey,
3729
3682
  defaultKvStore,