@saasicat/ui-vue 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -4
- package/dist/chunk-A3W2N26K.js +714 -0
- package/dist/chunk-QZOSDCNP.js +98 -0
- package/dist/chunk-X5SQ5HMB.js +3231 -0
- package/dist/client/index.cjs +3988 -0
- package/dist/client/index.d.cts +321 -0
- package/dist/client/index.d.ts +321 -0
- package/dist/client/index.js +70 -0
- package/dist/index.cjs +4446 -1249
- package/dist/index.d.cts +297 -888
- package/dist/index.d.ts +297 -888
- package/dist/index.js +609 -1348
- package/dist/messages-7b0P8W75.d.cts +1652 -0
- package/dist/messages-7b0P8W75.d.ts +1652 -0
- package/dist/quasar/index.cjs +3340 -0
- package/dist/quasar/index.d.cts +95 -0
- package/dist/quasar/index.d.ts +95 -0
- package/dist/quasar/index.js +96 -0
- package/dist/testing-e2e/admin-pages-suite.cjs +14 -11
- package/dist/testing-e2e/admin-pages-suite.js +14 -11
- package/dist/use-super-admin-i18n-B3v3-SWZ.d.ts +210 -0
- package/dist/use-super-admin-i18n-DMktRVEt.d.cts +210 -0
- package/package.json +23 -3
- package/src/{action-registry.ts → client/action-registry.ts} +4 -4
- package/src/{batch-column-fetcher.ts → client/batch-column-fetcher.ts} +4 -4
- package/src/{boot-loader.ts → client/boot-loader.ts} +3 -3
- package/src/client/i18n/currency.ts +42 -0
- package/src/client/i18n/define.ts +59 -0
- package/src/client/i18n/format.ts +12 -0
- package/src/client/i18n/index.ts +14 -0
- package/src/client/i18n/locale.ts +15 -0
- package/src/client/i18n/messages/audit.ts +42 -0
- package/src/client/i18n/messages/bundles.ts +459 -0
- package/src/client/i18n/messages/common.ts +147 -0
- package/src/client/i18n/messages/dashboard.ts +16 -0
- package/src/client/i18n/messages/discovery.ts +229 -0
- package/src/client/i18n/messages/email.ts +125 -0
- package/src/client/i18n/messages/marketing.ts +270 -0
- package/src/client/i18n/messages/nav.ts +72 -0
- package/src/client/i18n/messages/pilots.ts +192 -0
- package/src/client/i18n/messages/plan-detail.ts +266 -0
- package/src/client/i18n/messages/plan-editor.ts +177 -0
- package/src/client/i18n/messages/plan-versions.ts +251 -0
- package/src/client/i18n/messages/plans.ts +503 -0
- package/src/client/i18n/messages/promos.ts +202 -0
- package/src/client/i18n/messages/shell.ts +145 -0
- package/src/client/i18n/messages/tenants.ts +104 -0
- package/src/client/i18n/messages/users.ts +78 -0
- package/src/client/i18n/messages.ts +84 -0
- package/src/client/index.ts +27 -0
- package/src/{manifest-loader.ts → client/manifest-loader.ts} +4 -4
- package/src/{nav-builder.ts → client/nav-builder.ts} +44 -51
- package/src/{plan-versions-catalog.ts → client/plan-versions-catalog.ts} +42 -30
- package/src/components/BundleVersionPublishDialog.vue +35 -24
- package/src/components/FeatureGate.vue +1 -1
- package/src/components/MarketingPromotionsTab.vue +112 -59
- package/src/components/MfaPromptDialog.vue +11 -11
- package/src/components/TenantActionConfirmDialog.vue +19 -17
- package/src/components/VersionDiffPreview.vue +72 -43
- package/src/components/bundle-editor/BundleCreatePanel.vue +76 -63
- package/src/components/bundle-editor/BundleFeaturesEditor.vue +13 -6
- package/src/components/bundle-editor/BundlePlanCompatPicker.vue +19 -14
- package/src/components/bundle-editor/BundleQuotasEditor.vue +7 -4
- package/src/components/bundle-editor/BundleStatusBanner.vue +36 -19
- package/src/components/bundle-editor/BundleVersionInlineEditor.vue +51 -35
- package/src/components/bundle-editor/BundleVersionStrip.vue +24 -12
- package/src/components/bundle-editor/bundle-version-status.ts +29 -29
- package/src/components/dialogs/PilotCreateDialog.vue +58 -63
- package/src/components/dialogs/PilotEditDialog.vue +40 -34
- package/src/components/dialogs/PromoCodeCreateDialog.vue +89 -63
- package/src/components/dialogs/PromoCodeEditDialog.vue +98 -66
- package/src/components/dialogs/types.ts +3 -13
- package/src/components/plan/PlanCycleToggle.vue +21 -10
- package/src/components/plan/PlanGrid.vue +2 -2
- package/src/components/plan/PriceSummary.vue +1 -1
- package/src/components/plan/PromoCodeInput.vue +1 -1
- package/src/components/plan/PublicBundleGrid.vue +1 -1
- package/src/components/plan-cockpit/PlanCockpit.vue +34 -15
- package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +13 -6
- package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +18 -9
- package/src/components/plan-cockpit/PlanCockpitHeader.vue +9 -6
- package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +24 -11
- package/src/components/plan-cockpit/PlanCockpitKpis.vue +28 -17
- package/src/components/plan-cockpit/PlanCockpitVersions.vue +50 -25
- package/src/components/plan-create-dialog/PlanCreateDialog.vue +45 -35
- package/src/components/plan-detail/PlanAuditLog.vue +11 -6
- package/src/components/plan-detail/PlanDetail.vue +21 -23
- package/src/components/plan-detail/PlanDetailHeader.vue +20 -11
- package/src/components/plan-detail/PlanDetailKpis.vue +30 -17
- package/src/components/plan-detail/PlanTerminateDialog.vue +22 -9
- package/src/components/plan-detail/PlanVersionDiffPanel.vue +61 -27
- package/src/components/plan-detail/PlanVersionsPanel.vue +71 -36
- package/src/components/plan-list/PlanList.vue +99 -57
- package/src/components/plan-matrix/PlanMatrix.vue +85 -38
- package/src/components/plan-review/PlanReview.vue +105 -80
- package/src/components/plan-version-editor/PlanCatalogPreview.vue +43 -20
- package/src/components/plan-version-editor/PlanComponentPool.vue +15 -7
- package/src/components/plan-version-editor/PlanVersionBasket.vue +45 -24
- package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +8 -7
- package/src/components/plan-version-editor/PlanVersionEditor.vue +74 -40
- package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +24 -12
- package/src/index.ts +82 -79
- package/src/pages-standard/AdminLayout.vue +43 -18
- package/src/pages-standard/AdminManifestErrorPage.vue +10 -10
- package/src/pages-standard/AuditPage.vue +58 -19
- package/src/pages-standard/BundlesPage.vue +29 -18
- package/src/pages-standard/DashboardPage.vue +31 -17
- package/src/pages-standard/DiscoveryPage.vue +40 -28
- package/src/pages-standard/EmailHistoryPage.vue +98 -69
- package/src/pages-standard/MarketingCatalogPage.vue +40 -21
- package/src/pages-standard/PilotsPage.vue +64 -52
- package/src/pages-standard/PlanVersionsPage.vue +9 -5
- package/src/pages-standard/PlansPage.vue +91 -59
- package/src/pages-standard/PlatformEmailPage.vue +120 -53
- package/src/pages-standard/PromoCodeDetailPage.vue +66 -63
- package/src/pages-standard/PromoCodesPage.vue +51 -54
- package/src/pages-standard/SubscriptionsPage.vue +14 -9
- package/src/pages-standard/SuperAdminLoginPage.vue +19 -16
- package/src/pages-standard/SuperAdminSetupWizard.vue +53 -49
- package/src/pages-standard/TenantDetailPage.vue +54 -40
- package/src/pages-standard/TenantsPage.vue +57 -44
- package/src/pages-standard/UsersPage.vue +70 -52
- package/src/pages-standard/bundles-page/BundleAccordionList.vue +20 -8
- package/src/pages-standard/bundles-page/BundleDetailPanel.vue +28 -18
- package/src/pages-standard/bundles-page/BundlesFilterBar.vue +4 -1
- package/src/pages-standard/bundles-page/BundlesHeader.vue +9 -8
- package/src/pages-standard/bundles-page/BundlesKpis.vue +29 -11
- package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +20 -8
- package/src/pages-standard/discovery-page/DiscoveryCapList.vue +5 -3
- package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +43 -24
- package/src/pages-standard/discovery-page/DiscoveryHeader.vue +7 -4
- package/src/pages-standard/discovery-page/DiscoveryKpis.vue +28 -15
- package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +20 -10
- package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +9 -6
- package/src/pages-standard/discovery-page/discovery-ui.ts +40 -52
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +81 -49
- package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +13 -10
- package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +24 -20
- package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +6 -3
- package/src/pages-standard/plan-versions/PlanDiffCard.vue +16 -6
- package/src/pages-standard/plan-versions/PlanVersionHeader.vue +42 -26
- package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +46 -27
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +8 -6
- package/src/pages-standard/plan-versions/PlanVersionsList.vue +40 -17
- package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +1 -1
- package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +25 -13
- package/src/pages-standard/plan-versions/format.ts +33 -17
- package/src/pages-standard/plans-page/PlanArchiveDialog.vue +12 -8
- package/src/pages-standard/plans-page/PlanBundleOverview.vue +15 -6
- package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +18 -7
- package/src/pages-standard/plans-page/PlanPublishDialog.vue +23 -12
- package/src/pages-standard/tenants/format.ts +3 -2
- package/src/pages-tenant/MySubscriptionBundlesPage.vue +73 -38
- package/src/pages-tenant/OnboardingConfigurator.vue +3 -3
- package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
- package/src/pages-tenant/PlanChangeWizard.vue +10 -6
- package/src/pages-tenant/TenantPlanSection.vue +20 -16
- package/src/pages-tenant/default-i18n.ts +217 -1
- package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +9 -7
- package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +11 -12
- package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +4 -1
- package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +1 -1
- package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +1 -1
- package/src/quasar/create-super-admin-app.ts +192 -0
- package/src/quasar/index.ts +11 -0
- package/src/quasar/notify.ts +30 -0
- package/src/testing-e2e/admin-pages-suite.ts +14 -11
- package/src/{manifest-store-factory.ts → vue/manifest-store-factory.ts} +1 -1
- package/src/{platform-loaders.ts → vue/platform-loaders.ts} +4 -4
- package/src/{project-page-host.ts → vue/project-page-host.ts} +6 -4
- package/src/{create-super-admin-app.ts → vue/super-admin-context.ts} +20 -139
- package/src/vue/ui-notify.ts +30 -0
- package/src/{use-actions.ts → vue/use-actions.ts} +1 -1
- package/src/{use-api-list.ts → vue/use-api-list.ts} +1 -1
- package/src/{use-audit-entries.ts → vue/use-audit-entries.ts} +3 -3
- package/src/{use-batch-columns.ts → vue/use-batch-columns.ts} +1 -1
- package/src/{use-bulk-publish.ts → vue/use-bulk-publish.ts} +7 -5
- package/src/{use-bundle-versions-map.ts → vue/use-bundle-versions-map.ts} +7 -5
- package/src/{use-bundles.ts → vue/use-bundles.ts} +14 -14
- package/src/{use-catalog-entries.ts → vue/use-catalog-entries.ts} +14 -11
- package/src/{use-discovery.ts → vue/use-discovery.ts} +9 -6
- package/src/{use-entitlement.ts → vue/use-entitlement.ts} +4 -4
- package/src/{use-live-plan-versions.ts → vue/use-live-plan-versions.ts} +5 -5
- package/src/{use-manifest.ts → vue/use-manifest.ts} +1 -1
- package/src/{use-marketing-projections.ts → vue/use-marketing-projections.ts} +10 -7
- package/src/{use-nav.ts → vue/use-nav.ts} +9 -3
- package/src/{use-plan-editor.ts → vue/use-plan-editor.ts} +2 -2
- package/src/{use-plan-versions.ts → vue/use-plan-versions.ts} +3 -3
- package/src/{use-plans.ts → vue/use-plans.ts} +13 -13
- package/src/{use-platform-tenant-actions.ts → vue/use-platform-tenant-actions.ts} +9 -6
- package/src/{use-promotions.ts → vue/use-promotions.ts} +10 -11
- package/src/{use-public-boot.ts → vue/use-public-boot.ts} +1 -1
- package/src/{use-subscription-draft.ts → vue/use-subscription-draft.ts} +2 -3
- package/src/{use-super-admin-context.ts → vue/use-super-admin-context.ts} +5 -5
- package/src/vue/use-super-admin-i18n.ts +82 -0
- package/src/{use-tenant-action-flow.ts → vue/use-tenant-action-flow.ts} +12 -7
- package/src/{use-tenant-billing-catalog.ts → vue/use-tenant-billing-catalog.ts} +2 -2
- package/src/{use-tenant-billing.ts → vue/use-tenant-billing.ts} +2 -2
- package/src/{use-tenant-manifest.ts → vue/use-tenant-manifest.ts} +2 -4
- package/src/{use-tenant-subscription-bundles.ts → vue/use-tenant-subscription-bundles.ts} +5 -5
- package/src/{use-tenants.ts → vue/use-tenants.ts} +3 -3
- package/dist/testing-e2e/admin-pages-suite.d.cts +0 -37
- package/dist/testing-e2e/admin-pages-suite.d.ts +0 -37
- package/src/components/BusinessTypeVersionEditorDialog.vue +0 -417
- package/src/components/BusinessTypeVersionPublishDialog.vue +0 -258
- package/src/pages-standard/BusinessTypesPage.vue +0 -662
- package/src/pages-standard/plan-versions/VersionDiffPreview.vue +0 -167
- package/src/use-business-types.ts +0 -269
- /package/src/{http-json.ts → client/http-json.ts} +0 -0
- /package/src/{types.ts → client/types.ts} +0 -0
- /package/src/{version.ts → client/version.ts} +0 -0
- /package/src/{create-admin-routes.ts → vue/create-admin-routes.ts} +0 -0
- /package/src/{entitlement-provider.ts → vue/entitlement-provider.ts} +0 -0
- /package/src/{feature-router-guard.ts → vue/feature-router-guard.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,174 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
ADMIN_UI_VERSION,
|
|
3
|
+
ActionDefNotInManifestError,
|
|
4
|
+
ActionRegistry,
|
|
5
|
+
BatchColumnDriftError,
|
|
6
|
+
BatchColumnFetcher,
|
|
7
|
+
BootLoadError,
|
|
8
|
+
BootLoader,
|
|
9
|
+
DEFAULT_STANDARD_PAGE_ROUTES,
|
|
10
|
+
HttpJsonError,
|
|
11
|
+
ManifestLoadError,
|
|
12
|
+
ManifestLoader,
|
|
13
|
+
MissingHandlerError,
|
|
14
|
+
buildRoutes,
|
|
15
|
+
buildSidebar,
|
|
16
|
+
buildSnapshots,
|
|
17
|
+
defaultSectionOrder,
|
|
18
|
+
getJson,
|
|
19
|
+
listOpenDrafts,
|
|
20
|
+
postJson,
|
|
21
|
+
resolveExtension,
|
|
22
|
+
trimTrailingSlashes
|
|
23
|
+
} from "./chunk-A3W2N26K.js";
|
|
24
|
+
import {
|
|
25
|
+
SUPER_ADMIN_ACTIONS_KEY,
|
|
26
|
+
SUPER_ADMIN_BRAND_KEY,
|
|
27
|
+
SUPER_ADMIN_ENDPOINTS_KEY,
|
|
28
|
+
SUPER_ADMIN_EXTENSIONS_KEY,
|
|
29
|
+
SUPER_ADMIN_HTTP_KEY,
|
|
30
|
+
SUPER_ADMIN_I18N_KEY,
|
|
31
|
+
SUPER_ADMIN_LOGIN_ADAPTER_KEY,
|
|
32
|
+
SUPER_ADMIN_MANIFEST_KEY,
|
|
33
|
+
SUPER_ADMIN_NOTIFY_KEY,
|
|
34
|
+
buildNavigationGuard,
|
|
35
|
+
createSuperAdminI18n,
|
|
36
|
+
useSaMessages,
|
|
37
|
+
useSuperAdminI18n
|
|
38
|
+
} from "./chunk-QZOSDCNP.js";
|
|
39
|
+
import {
|
|
40
|
+
DEFAULT_SA_LOCALE,
|
|
41
|
+
SA_INTL_LOCALES,
|
|
42
|
+
SA_LOCALES,
|
|
43
|
+
SA_MESSAGES,
|
|
44
|
+
defaultHttpClient,
|
|
45
|
+
defaultKvStore,
|
|
46
|
+
defineMessages,
|
|
47
|
+
formatCurrency,
|
|
48
|
+
formatMessage,
|
|
49
|
+
mergeMessages,
|
|
50
|
+
resolveMessages
|
|
51
|
+
} from "./chunk-X5SQ5HMB.js";
|
|
3
52
|
|
|
4
|
-
// src/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
get: () => null,
|
|
9
|
-
set: () => {
|
|
10
|
-
},
|
|
11
|
-
remove: () => {
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
const ls = globalThis.localStorage;
|
|
16
|
-
return {
|
|
17
|
-
get: (k) => ls.getItem(k),
|
|
18
|
-
set: (k, v) => ls.setItem(k, v),
|
|
19
|
-
remove: (k) => ls.removeItem(k)
|
|
20
|
-
};
|
|
53
|
+
// src/vue/use-super-admin-context.ts
|
|
54
|
+
import { inject } from "vue";
|
|
55
|
+
function useSuperAdminExtensions() {
|
|
56
|
+
return inject(SUPER_ADMIN_EXTENSIONS_KEY, {});
|
|
21
57
|
}
|
|
22
|
-
function
|
|
23
|
-
return (
|
|
58
|
+
function useSuperAdminActions() {
|
|
59
|
+
return inject(SUPER_ADMIN_ACTIONS_KEY, {});
|
|
24
60
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.code = code;
|
|
32
|
-
this.name = "HttpJsonError";
|
|
33
|
-
}
|
|
34
|
-
status;
|
|
35
|
-
code;
|
|
36
|
-
};
|
|
37
|
-
async function extractCode(res) {
|
|
38
|
-
try {
|
|
39
|
-
const body = await res.json();
|
|
40
|
-
return typeof body?.code === "string" ? body.code : void 0;
|
|
41
|
-
} catch {
|
|
42
|
-
return void 0;
|
|
61
|
+
function useSuperAdminBrand() {
|
|
62
|
+
const brand = inject(SUPER_ADMIN_BRAND_KEY);
|
|
63
|
+
if (!brand) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
"useSuperAdminBrand(): no SuperAdmin branding in inject scope. Was the component mounted inside createSuperAdminApp()?"
|
|
66
|
+
);
|
|
43
67
|
}
|
|
68
|
+
return brand;
|
|
44
69
|
}
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const res = await http(url);
|
|
52
|
-
if (res.status < 200 || res.status >= 300) {
|
|
53
|
-
throw new HttpJsonError(res.status, await extractCode(res));
|
|
70
|
+
function useSuperAdminEndpoints() {
|
|
71
|
+
const endpoints = inject(SUPER_ADMIN_ENDPOINTS_KEY);
|
|
72
|
+
if (!endpoints) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
"useSuperAdminEndpoints(): no endpoints in inject scope. Was the component mounted inside createSuperAdminApp()?"
|
|
75
|
+
);
|
|
54
76
|
}
|
|
55
|
-
return
|
|
77
|
+
return endpoints;
|
|
56
78
|
}
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
headers: { "Content-Type": "application/json" },
|
|
61
|
-
body: JSON.stringify(body)
|
|
62
|
-
});
|
|
63
|
-
if (res.status < 200 || res.status >= 300) {
|
|
64
|
-
throw new HttpJsonError(res.status, await extractCode(res));
|
|
65
|
-
}
|
|
66
|
-
return await res.json();
|
|
79
|
+
function useSuperAdminManifest() {
|
|
80
|
+
const accessor = inject(SUPER_ADMIN_MANIFEST_KEY, null);
|
|
81
|
+
return accessor ? accessor() : null;
|
|
67
82
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
import { createApp } from "vue";
|
|
75
|
-
import { Quasar, Notify, Dialog, Loading } from "quasar";
|
|
76
|
-
import { createPinia } from "pinia";
|
|
77
|
-
import {
|
|
78
|
-
createRouter,
|
|
79
|
-
createWebHistory
|
|
80
|
-
} from "vue-router";
|
|
81
|
-
var SUPER_ADMIN_BRAND_KEY = /* @__PURE__ */ Symbol.for(
|
|
82
|
-
"@saasicat/ui-vue/SUPER_ADMIN_BRAND"
|
|
83
|
-
);
|
|
84
|
-
var SUPER_ADMIN_ENDPOINTS_KEY = /* @__PURE__ */ Symbol.for(
|
|
85
|
-
"@saasicat/ui-vue/SUPER_ADMIN_ENDPOINTS"
|
|
86
|
-
);
|
|
87
|
-
var SUPER_ADMIN_EXTENSIONS_KEY = /* @__PURE__ */ Symbol.for(
|
|
88
|
-
"@saasicat/ui-vue/SUPER_ADMIN_EXTENSIONS"
|
|
89
|
-
);
|
|
90
|
-
var SUPER_ADMIN_ACTIONS_KEY = /* @__PURE__ */ Symbol.for(
|
|
91
|
-
"@saasicat/ui-vue/SUPER_ADMIN_ACTIONS"
|
|
92
|
-
);
|
|
93
|
-
var SUPER_ADMIN_MANIFEST_KEY = /* @__PURE__ */ Symbol.for(
|
|
94
|
-
"@saasicat/ui-vue/SUPER_ADMIN_MANIFEST"
|
|
95
|
-
);
|
|
96
|
-
var SUPER_ADMIN_LOGIN_ADAPTER_KEY = /* @__PURE__ */ Symbol.for(
|
|
97
|
-
"@saasicat/ui-vue/SUPER_ADMIN_LOGIN_ADAPTER"
|
|
98
|
-
);
|
|
99
|
-
var SUPER_ADMIN_HTTP_KEY = /* @__PURE__ */ Symbol.for(
|
|
100
|
-
"@saasicat/ui-vue/SUPER_ADMIN_HTTP"
|
|
101
|
-
);
|
|
102
|
-
var DEFAULT_QUASAR_OPTIONS = {
|
|
103
|
-
plugins: { Notify, Dialog, Loading },
|
|
104
|
-
config: { notify: { position: "top-right", timeout: 3e3 } }
|
|
105
|
-
};
|
|
106
|
-
function createSuperAdminApp(options) {
|
|
107
|
-
const app = createApp(options.rootComponent);
|
|
108
|
-
app.use(Quasar, options.quasarOptions ?? DEFAULT_QUASAR_OPTIONS);
|
|
109
|
-
const pinia = createPinia();
|
|
110
|
-
app.use(pinia);
|
|
111
|
-
const router = createRouter({
|
|
112
|
-
history: options.routerHistory ?? createWebHistory(),
|
|
113
|
-
routes: options.appRoutes
|
|
114
|
-
});
|
|
115
|
-
const navGuard = buildNavigationGuard(options);
|
|
116
|
-
if (navGuard) {
|
|
117
|
-
router.beforeEach(navGuard);
|
|
118
|
-
}
|
|
119
|
-
app.use(router);
|
|
120
|
-
const endpoints = {
|
|
121
|
-
apiBase: options.endpoints.apiBase,
|
|
122
|
-
publicBootEndpoint: options.endpoints.publicBootEndpoint ?? `${options.endpoints.apiBase}/boot`,
|
|
123
|
-
manifestEndpoint: options.endpoints.manifestEndpoint ?? `${options.endpoints.apiBase}/manifest`
|
|
124
|
-
};
|
|
125
|
-
app.provide(SUPER_ADMIN_BRAND_KEY, { tag: "SuperAdmin", ...options.brand });
|
|
126
|
-
app.provide(SUPER_ADMIN_ENDPOINTS_KEY, endpoints);
|
|
127
|
-
app.provide(SUPER_ADMIN_EXTENSIONS_KEY, options.extensions ?? {});
|
|
128
|
-
app.provide(SUPER_ADMIN_ACTIONS_KEY, options.actions ?? {});
|
|
129
|
-
if (options.manifestGuard?.getManifest) {
|
|
130
|
-
app.provide(SUPER_ADMIN_MANIFEST_KEY, options.manifestGuard.getManifest);
|
|
131
|
-
}
|
|
132
|
-
if (options.loginAdapter) {
|
|
133
|
-
app.provide(SUPER_ADMIN_LOGIN_ADAPTER_KEY, options.loginAdapter);
|
|
134
|
-
}
|
|
135
|
-
app.provide(SUPER_ADMIN_HTTP_KEY, options.http ?? defaultHttpClient());
|
|
136
|
-
for (const plugin of options.installPlugins ?? []) {
|
|
137
|
-
plugin(app);
|
|
83
|
+
function useSuperAdminLoginAdapter() {
|
|
84
|
+
const adapter = inject(SUPER_ADMIN_LOGIN_ADAPTER_KEY);
|
|
85
|
+
if (!adapter) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"useSuperAdminLoginAdapter(): no login adapter registered. Pass it via createSuperAdminApp({ loginAdapter })."
|
|
88
|
+
);
|
|
138
89
|
}
|
|
139
|
-
return
|
|
140
|
-
app,
|
|
141
|
-
router,
|
|
142
|
-
pinia,
|
|
143
|
-
mount: (selector) => app.mount(selector)
|
|
144
|
-
};
|
|
90
|
+
return adapter;
|
|
145
91
|
}
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
if (!authGuard && !manifestGuard) return null;
|
|
149
|
-
return async (to) => {
|
|
150
|
-
if (to.meta?.public === true) return true;
|
|
151
|
-
if (authGuard) {
|
|
152
|
-
if (!authGuard.isAuthenticated()) return authGuard.onUnauthenticated();
|
|
153
|
-
if (authGuard.isSuperAdmin && !authGuard.isSuperAdmin()) {
|
|
154
|
-
return authGuard.onUnauthenticated();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (manifestGuard) {
|
|
158
|
-
try {
|
|
159
|
-
await manifestGuard.ensureLoaded();
|
|
160
|
-
} catch (err) {
|
|
161
|
-
if (manifestGuard.errorRoute && to.path !== manifestGuard.errorRoute) {
|
|
162
|
-
return manifestGuard.errorRoute;
|
|
163
|
-
}
|
|
164
|
-
console.error("[SuperAdmin] manifest load failed", err);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return true;
|
|
168
|
-
};
|
|
92
|
+
function useSuperAdminHttp() {
|
|
93
|
+
return inject(SUPER_ADMIN_HTTP_KEY, defaultHttpClient());
|
|
169
94
|
}
|
|
170
95
|
|
|
171
|
-
// src/project-page-host.ts
|
|
96
|
+
// src/vue/project-page-host.ts
|
|
97
|
+
import { computed, defineAsyncComponent, defineComponent, h, inject as inject2 } from "vue";
|
|
98
|
+
import { useRoute } from "vue-router";
|
|
172
99
|
var asyncComponentCache = /* @__PURE__ */ new WeakMap();
|
|
173
100
|
function resolveAsync(extensions, key) {
|
|
174
101
|
let bucket = asyncComponentCache.get(extensions);
|
|
@@ -188,8 +115,8 @@ var ProjectPageHost = defineComponent({
|
|
|
188
115
|
name: "ProjectPageHost",
|
|
189
116
|
setup() {
|
|
190
117
|
const route = useRoute();
|
|
191
|
-
const extensions =
|
|
192
|
-
const manifestAccessor =
|
|
118
|
+
const extensions = inject2(SUPER_ADMIN_EXTENSIONS_KEY, {});
|
|
119
|
+
const manifestAccessor = inject2(SUPER_ADMIN_MANIFEST_KEY, null);
|
|
193
120
|
const matchingPage = computed(() => {
|
|
194
121
|
const manifest = manifestAccessor ? manifestAccessor() : null;
|
|
195
122
|
if (!manifest) return null;
|
|
@@ -206,10 +133,10 @@ var ProjectPageHost = defineComponent({
|
|
|
206
133
|
const cmp = resolvedComponent.value;
|
|
207
134
|
if (page && cmp) return h(cmp);
|
|
208
135
|
return h("div", { class: "sa-project-page-host__missing", role: "alert" }, [
|
|
209
|
-
h("h2", "
|
|
136
|
+
h("h2", "Page not available"),
|
|
210
137
|
h(
|
|
211
138
|
"p",
|
|
212
|
-
page ? `componentKey "${page.componentKey}"
|
|
139
|
+
page ? `componentKey "${page.componentKey}" is not registered in the extensions: map. Apps must register the component in createSuperAdminApp({ extensions }).` : `No manifest definition for ${route.path}. Either the ProjectPage is missing from the manifest, or the capability is not active.`
|
|
213
140
|
)
|
|
214
141
|
]);
|
|
215
142
|
};
|
|
@@ -223,7 +150,7 @@ function createProjectPageHostRoute(options) {
|
|
|
223
150
|
};
|
|
224
151
|
}
|
|
225
152
|
|
|
226
|
-
// src/create-admin-routes.ts
|
|
153
|
+
// src/vue/create-admin-routes.ts
|
|
227
154
|
function createAdminRoutes(options) {
|
|
228
155
|
return [
|
|
229
156
|
{
|
|
@@ -257,444 +184,7 @@ function createAdminRoutes(options) {
|
|
|
257
184
|
];
|
|
258
185
|
}
|
|
259
186
|
|
|
260
|
-
// src/
|
|
261
|
-
var BootLoadError = class extends Error {
|
|
262
|
-
constructor(status, message) {
|
|
263
|
-
super(message);
|
|
264
|
-
this.status = status;
|
|
265
|
-
this.name = "BootLoadError";
|
|
266
|
-
}
|
|
267
|
-
status;
|
|
268
|
-
};
|
|
269
|
-
var BootLoader = class {
|
|
270
|
-
endpoint;
|
|
271
|
-
http;
|
|
272
|
-
constructor(options) {
|
|
273
|
-
if (!options?.endpoint) {
|
|
274
|
-
throw new Error(
|
|
275
|
-
'BootLoader: `endpoint` ist Pflicht (z. B. "/api/admin/boot" oder "/api/v1/admin/boot"). Plattform hat keinen Default, weil Apps unterschiedliche globalPrefix-Konventionen haben.'
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
this.endpoint = options.endpoint;
|
|
279
|
-
this.http = options.http ?? defaultHttpClient();
|
|
280
|
-
}
|
|
281
|
-
async load() {
|
|
282
|
-
const res = await this.http(this.endpoint);
|
|
283
|
-
if (res.status !== 200) {
|
|
284
|
-
throw new BootLoadError(res.status, `Boot-Endpunkt antwortete HTTP ${res.status}`);
|
|
285
|
-
}
|
|
286
|
-
return await res.json();
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
// src/manifest-loader.ts
|
|
291
|
-
var ManifestLoadError = class extends Error {
|
|
292
|
-
constructor(status, message) {
|
|
293
|
-
super(message);
|
|
294
|
-
this.status = status;
|
|
295
|
-
this.name = "ManifestLoadError";
|
|
296
|
-
}
|
|
297
|
-
status;
|
|
298
|
-
};
|
|
299
|
-
var ManifestLoader = class {
|
|
300
|
-
endpoint;
|
|
301
|
-
http;
|
|
302
|
-
storage;
|
|
303
|
-
bodyKey;
|
|
304
|
-
etagKey;
|
|
305
|
-
getAuthToken;
|
|
306
|
-
constructor(options) {
|
|
307
|
-
if (!options?.endpoint) {
|
|
308
|
-
throw new Error(
|
|
309
|
-
'ManifestLoader: `endpoint` ist Pflicht (z. B. "/api/admin/manifest" oder "/api/v1/admin/manifest"). Plattform hat keinen Default, weil Apps unterschiedliche globalPrefix-Konventionen haben.'
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
this.endpoint = options.endpoint;
|
|
313
|
-
this.http = options.http ?? defaultHttpClient();
|
|
314
|
-
this.storage = options.storage ?? defaultKvStore();
|
|
315
|
-
const prefix = options.storageKeyPrefix ?? "";
|
|
316
|
-
this.bodyKey = `${prefix}manifest:body`;
|
|
317
|
-
this.etagKey = `${prefix}manifest:etag`;
|
|
318
|
-
this.getAuthToken = options.getAuthToken;
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Loads the current manifest. On a cache hit (304) the cached
|
|
322
|
-
* body is returned — otherwise the fresh server body.
|
|
323
|
-
*/
|
|
324
|
-
async load() {
|
|
325
|
-
const cachedEtag = this.storage.get(this.etagKey);
|
|
326
|
-
const headers = {};
|
|
327
|
-
const token = this.getAuthToken?.();
|
|
328
|
-
if (token) headers.Authorization = `Bearer ${token}`;
|
|
329
|
-
if (cachedEtag) headers["If-None-Match"] = cachedEtag;
|
|
330
|
-
const res = await this.http(this.endpoint, { method: "GET", headers });
|
|
331
|
-
if (res.status === 304) {
|
|
332
|
-
const cachedBody = this.readCachedBody();
|
|
333
|
-
if (!cachedBody) {
|
|
334
|
-
throw new ManifestLoadError(
|
|
335
|
-
304,
|
|
336
|
-
"Server lieferte 304, aber Cache-Body fehlt \u2014 bitte clearCache() + reload"
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
return cachedBody.body;
|
|
340
|
-
}
|
|
341
|
-
if (res.status !== 200) {
|
|
342
|
-
throw new ManifestLoadError(
|
|
343
|
-
res.status,
|
|
344
|
-
`Manifest-Endpunkt antwortete HTTP ${res.status}`
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
const body = await res.json();
|
|
348
|
-
const etag = res.headers.get("ETag") ?? res.headers.get("etag");
|
|
349
|
-
if (etag) {
|
|
350
|
-
this.storage.set(this.etagKey, etag);
|
|
351
|
-
this.storage.set(this.bodyKey, JSON.stringify(body));
|
|
352
|
-
}
|
|
353
|
-
return body;
|
|
354
|
-
}
|
|
355
|
-
/** Reads the cached manifest body from storage; null if absent. */
|
|
356
|
-
readCachedBody() {
|
|
357
|
-
const etag = this.storage.get(this.etagKey);
|
|
358
|
-
const bodyStr = this.storage.get(this.bodyKey);
|
|
359
|
-
if (!etag || !bodyStr) return null;
|
|
360
|
-
try {
|
|
361
|
-
return { etag, body: JSON.parse(bodyStr) };
|
|
362
|
-
} catch {
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
/** Clears the cache — e.g. on logout or after `manifest reload`. */
|
|
367
|
-
clearCache() {
|
|
368
|
-
this.storage.remove(this.etagKey);
|
|
369
|
-
this.storage.remove(this.bodyKey);
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
// src/nav-builder.ts
|
|
374
|
-
var DEFAULT_STANDARD_PAGE_ROUTES = {
|
|
375
|
-
dashboard: "/admin/dashboard",
|
|
376
|
-
tenants: "/admin/tenants",
|
|
377
|
-
subscriptions: "/admin/subscriptions",
|
|
378
|
-
promoCodes: "/admin/promo-codes",
|
|
379
|
-
plans: "/admin/plans",
|
|
380
|
-
planVersions: "/admin/plan-versions",
|
|
381
|
-
audit: "/admin/audit",
|
|
382
|
-
users: "/admin/users",
|
|
383
|
-
pilots: "/admin/pilots",
|
|
384
|
-
discovery: "/admin/discovery",
|
|
385
|
-
bundles: "/admin/bundles",
|
|
386
|
-
businessTypes: "/admin/business-types",
|
|
387
|
-
marketingCatalog: "/admin/marketing-catalog",
|
|
388
|
-
platformEmail: "/admin/platform-email",
|
|
389
|
-
platformEmailHistory: "/admin/platform-email-history"
|
|
390
|
-
};
|
|
391
|
-
var DEFAULT_LABELS = {
|
|
392
|
-
dashboard: "Dashboard",
|
|
393
|
-
tenants: "Mandanten",
|
|
394
|
-
subscriptions: "Abonnements",
|
|
395
|
-
promoCodes: "Promo-Codes",
|
|
396
|
-
plans: "Pl\xE4ne & Versionen",
|
|
397
|
-
planVersions: "Plan-Versionen",
|
|
398
|
-
audit: "Audit-Log",
|
|
399
|
-
users: "Benutzer",
|
|
400
|
-
pilots: "Piloten",
|
|
401
|
-
discovery: "Discovery",
|
|
402
|
-
bundles: "Bundles",
|
|
403
|
-
businessTypes: "Gesch\xE4ftstypen",
|
|
404
|
-
marketingCatalog: "Marketing-Catalog",
|
|
405
|
-
platformEmail: "Plattform-E-Mail",
|
|
406
|
-
platformEmailHistory: "E-Mail-Verlauf"
|
|
407
|
-
};
|
|
408
|
-
var DEFAULT_ICONS = {
|
|
409
|
-
dashboard: "dashboard",
|
|
410
|
-
tenants: "business",
|
|
411
|
-
subscriptions: "card_membership",
|
|
412
|
-
promoCodes: "local_activity",
|
|
413
|
-
plans: "workspace_premium",
|
|
414
|
-
planVersions: "history_edu",
|
|
415
|
-
audit: "history",
|
|
416
|
-
users: "people",
|
|
417
|
-
pilots: "flight_takeoff",
|
|
418
|
-
discovery: "travel_explore",
|
|
419
|
-
bundles: "inventory_2",
|
|
420
|
-
businessTypes: "category",
|
|
421
|
-
marketingCatalog: "campaign",
|
|
422
|
-
platformEmail: "mail",
|
|
423
|
-
platformEmailHistory: "mark_email_read"
|
|
424
|
-
};
|
|
425
|
-
var DEFAULT_NAV_SECTIONS = {
|
|
426
|
-
dashboard: "\xDCbersicht",
|
|
427
|
-
discovery: "Produktkatalog",
|
|
428
|
-
businessTypes: "Produktkatalog",
|
|
429
|
-
bundles: "Produktkatalog",
|
|
430
|
-
plans: "Produktkatalog",
|
|
431
|
-
planVersions: "Produktkatalog",
|
|
432
|
-
marketingCatalog: "Produktkatalog",
|
|
433
|
-
promoCodes: "Produktkatalog",
|
|
434
|
-
tenants: "Kunden",
|
|
435
|
-
subscriptions: "Kunden",
|
|
436
|
-
users: "Kunden",
|
|
437
|
-
pilots: "Kunden",
|
|
438
|
-
audit: "System",
|
|
439
|
-
platformEmail: "System",
|
|
440
|
-
platformEmailHistory: "System"
|
|
441
|
-
};
|
|
442
|
-
var DEFAULT_SECTION_ORDER = [
|
|
443
|
-
"\xDCbersicht",
|
|
444
|
-
"Produktkatalog",
|
|
445
|
-
"Kunden",
|
|
446
|
-
"System"
|
|
447
|
-
];
|
|
448
|
-
function buildRoutes(manifest, options = {}) {
|
|
449
|
-
const routes = [];
|
|
450
|
-
const capabilities = manifest.capabilities ?? {};
|
|
451
|
-
const standard = manifest.navigation?.standardPages ?? {};
|
|
452
|
-
const overrideRoutes = { ...DEFAULT_STANDARD_PAGE_ROUTES, ...options.standardPageRoutes };
|
|
453
|
-
const overrideLabels = { ...DEFAULT_LABELS, ...options.standardPageLabels };
|
|
454
|
-
const overrideIcons = { ...DEFAULT_ICONS, ...options.standardPageIcons };
|
|
455
|
-
const overrideNavSection = { ...DEFAULT_NAV_SECTIONS, ...options.standardPageNavSection };
|
|
456
|
-
for (const key of Object.keys(standard)) {
|
|
457
|
-
const def = standard[key];
|
|
458
|
-
if (!def?.enabled) continue;
|
|
459
|
-
const cap = def.requiredCapability ?? null;
|
|
460
|
-
if (cap && capabilities[cap] !== true) continue;
|
|
461
|
-
routes.push({
|
|
462
|
-
id: key,
|
|
463
|
-
path: overrideRoutes[key],
|
|
464
|
-
label: overrideLabels[key],
|
|
465
|
-
icon: overrideIcons[key],
|
|
466
|
-
navSection: overrideNavSection[key],
|
|
467
|
-
componentKey: `platform-${key}`,
|
|
468
|
-
requiredCapability: cap,
|
|
469
|
-
isStandard: true
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
const projectPages = manifest.navigation?.projectPages ?? [];
|
|
473
|
-
const exts = options.availableExtensions;
|
|
474
|
-
for (const p of projectPages) {
|
|
475
|
-
const cap = p.requiredCapability ?? null;
|
|
476
|
-
if (cap && capabilities[cap] !== true) continue;
|
|
477
|
-
if (exts && !exts.has(p.componentKey)) continue;
|
|
478
|
-
routes.push({
|
|
479
|
-
id: p.id,
|
|
480
|
-
path: p.route,
|
|
481
|
-
label: p.label,
|
|
482
|
-
icon: p.icon,
|
|
483
|
-
navSection: p.navSection,
|
|
484
|
-
componentKey: p.componentKey,
|
|
485
|
-
requiredCapability: cap,
|
|
486
|
-
isStandard: false,
|
|
487
|
-
prefetchOnIdle: p.prefetchOnIdle
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
return routes;
|
|
491
|
-
}
|
|
492
|
-
function buildSidebar(routes, sectionOrder = DEFAULT_SECTION_ORDER) {
|
|
493
|
-
const bySection = /* @__PURE__ */ new Map();
|
|
494
|
-
for (const r of routes) {
|
|
495
|
-
const section = r.navSection ?? null;
|
|
496
|
-
const list = bySection.get(section) ?? [];
|
|
497
|
-
list.push({ id: r.id, path: r.path, label: r.label, icon: r.icon });
|
|
498
|
-
bySection.set(section, list);
|
|
499
|
-
}
|
|
500
|
-
const sections = [];
|
|
501
|
-
if (bySection.has(null)) {
|
|
502
|
-
sections.push({ section: null, items: bySection.get(null) });
|
|
503
|
-
}
|
|
504
|
-
const ordered = /* @__PURE__ */ new Set();
|
|
505
|
-
for (const name of sectionOrder) {
|
|
506
|
-
if (bySection.has(name)) {
|
|
507
|
-
sections.push({ section: name, items: bySection.get(name) });
|
|
508
|
-
ordered.add(name);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
const remaining = [...bySection.entries()].filter(([s]) => s !== null && !ordered.has(s)).sort((a, b) => a[0].localeCompare(b[0]));
|
|
512
|
-
for (const [section, items] of remaining) {
|
|
513
|
-
sections.push({ section, items });
|
|
514
|
-
}
|
|
515
|
-
return sections;
|
|
516
|
-
}
|
|
517
|
-
function resolveExtension(componentKey, extensions) {
|
|
518
|
-
return extensions[componentKey] ?? null;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// src/action-registry.ts
|
|
522
|
-
var MissingHandlerError = class extends Error {
|
|
523
|
-
constructor(actionKey) {
|
|
524
|
-
super(
|
|
525
|
-
`Kein Handler f\xFCr actionKey "${actionKey}" registriert. Konsument-Shell muss \`actions["${actionKey}"]\` bereitstellen.`
|
|
526
|
-
);
|
|
527
|
-
this.name = "MissingHandlerError";
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
var ActionDefNotInManifestError = class extends Error {
|
|
531
|
-
constructor(actionKey) {
|
|
532
|
-
super(
|
|
533
|
-
`actionKey "${actionKey}" ist im Manifest nicht deklariert. Bitte als TenantAction in einer ManifestContribution registrieren.`
|
|
534
|
-
);
|
|
535
|
-
this.name = "ActionDefNotInManifestError";
|
|
536
|
-
}
|
|
537
|
-
};
|
|
538
|
-
var ActionRegistry = class {
|
|
539
|
-
defs = /* @__PURE__ */ new Map();
|
|
540
|
-
handlers = /* @__PURE__ */ new Map();
|
|
541
|
-
constructor(manifest, handlers = {}) {
|
|
542
|
-
for (const def of manifest.tenants?.actions ?? []) {
|
|
543
|
-
this.defs.set(def.actionKey, def);
|
|
544
|
-
}
|
|
545
|
-
for (const [key, handler] of Object.entries(handlers)) {
|
|
546
|
-
this.handlers.set(key, handler);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Registers a handler after the fact (e.g. when consumer code loads
|
|
551
|
-
* lazily). Throws `ActionDefNotInManifestError` if the `actionKey` is not
|
|
552
|
-
* declared in the manifest — prevents dead registrations.
|
|
553
|
-
*/
|
|
554
|
-
register(actionKey, handler) {
|
|
555
|
-
if (!this.defs.has(actionKey)) {
|
|
556
|
-
throw new ActionDefNotInManifestError(actionKey);
|
|
557
|
-
}
|
|
558
|
-
this.handlers.set(actionKey, handler);
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* Returns the `{def, handler}` pair. Throws if the key is missing from
|
|
562
|
-
* the manifest or no handler is registered. The shell's UI layer calls
|
|
563
|
-
* the method, checks `def.requiresMfa` / `def.confirmType` for the
|
|
564
|
-
* pre-flow, and then calls `handler(input)`.
|
|
565
|
-
*/
|
|
566
|
-
get(actionKey) {
|
|
567
|
-
const def = this.defs.get(actionKey);
|
|
568
|
-
if (!def) throw new ActionDefNotInManifestError(actionKey);
|
|
569
|
-
const handler = this.handlers.get(actionKey);
|
|
570
|
-
if (!handler) throw new MissingHandlerError(actionKey);
|
|
571
|
-
return { def, handler };
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Convenience: `get(key).handler(input)`. UI convenience for actions
|
|
575
|
-
* that need neither MFA nor confirm.
|
|
576
|
-
*/
|
|
577
|
-
async dispatch(actionKey, input) {
|
|
578
|
-
return this.get(actionKey).handler(input);
|
|
579
|
-
}
|
|
580
|
-
/**
|
|
581
|
-
* List of actionKeys that are declared in the manifest but have no
|
|
582
|
-
* handler. Consumers use this in a doctor check to detect drift between
|
|
583
|
-
* the manifest and the shell build.
|
|
584
|
-
*/
|
|
585
|
-
listOrphanedDefs() {
|
|
586
|
-
return [...this.defs.keys()].filter((k) => !this.handlers.has(k));
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* List of registered handlers that are missing from the manifest. Drift
|
|
590
|
-
* in the other direction.
|
|
591
|
-
*/
|
|
592
|
-
listOrphanedHandlers() {
|
|
593
|
-
return [...this.handlers.keys()].filter((k) => !this.defs.has(k));
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
// src/batch-column-fetcher.ts
|
|
598
|
-
var BatchColumnDriftError = class extends Error {
|
|
599
|
-
constructor(column, reason) {
|
|
600
|
-
super(`Spalte "${column.key}": ${reason}`);
|
|
601
|
-
this.column = column;
|
|
602
|
-
this.name = "BatchColumnDriftError";
|
|
603
|
-
}
|
|
604
|
-
column;
|
|
605
|
-
};
|
|
606
|
-
var BatchColumnFetcher = class {
|
|
607
|
-
http;
|
|
608
|
-
paramStyle;
|
|
609
|
-
getAuthToken;
|
|
610
|
-
constructor(options = {}) {
|
|
611
|
-
this.http = options.http ?? defaultHttpClient();
|
|
612
|
-
this.paramStyle = options.paramStyle ?? "comma";
|
|
613
|
-
this.getAuthToken = options.getAuthToken;
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Loads the data for all columns declared in the manifest in parallel
|
|
617
|
-
* (one request per column, all `tenantIds` in the batch). Columns without
|
|
618
|
-
* a satisfied capability are ignored.
|
|
619
|
-
*/
|
|
620
|
-
async fetchAll(manifest, tenantIds) {
|
|
621
|
-
if (tenantIds.length === 0) return {};
|
|
622
|
-
const cols = this.eligibleColumns(manifest);
|
|
623
|
-
const results = await Promise.all(
|
|
624
|
-
cols.map(
|
|
625
|
-
(col) => this.fetchOne(col, tenantIds).then((data) => [col.key, data])
|
|
626
|
-
)
|
|
627
|
-
);
|
|
628
|
-
const out = {};
|
|
629
|
-
for (const [key, data] of results) out[key] = data;
|
|
630
|
-
return out;
|
|
631
|
-
}
|
|
632
|
-
/** Fetch a single column (consumers may also use this directly). */
|
|
633
|
-
async fetchOne(column, tenantIds) {
|
|
634
|
-
this.validateBatchEndpoint(column);
|
|
635
|
-
if (tenantIds.length === 0) return {};
|
|
636
|
-
const url = this.buildUrl(column.endpoint, tenantIds);
|
|
637
|
-
const headers = {};
|
|
638
|
-
const token = this.getAuthToken?.();
|
|
639
|
-
if (token) headers.Authorization = `Bearer ${token}`;
|
|
640
|
-
const res = await this.http(url, { method: "GET", headers });
|
|
641
|
-
if (res.status !== 200) {
|
|
642
|
-
throw new Error(
|
|
643
|
-
`Spalte "${column.key}" \u2014 Endpoint ${column.endpoint} antwortete HTTP ${res.status}`
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
const body = await res.json();
|
|
647
|
-
return body;
|
|
648
|
-
}
|
|
649
|
-
/**
|
|
650
|
-
* Column drift against the manifest. Consumers use this for CI smoke
|
|
651
|
-
* tests of the manifest-vs-shell build.
|
|
652
|
-
*/
|
|
653
|
-
listDriftIssues(manifest) {
|
|
654
|
-
const issues = [];
|
|
655
|
-
for (const col of manifest.tenants?.columns ?? []) {
|
|
656
|
-
try {
|
|
657
|
-
this.validateBatchEndpoint(col);
|
|
658
|
-
} catch (err) {
|
|
659
|
-
if (err instanceof BatchColumnDriftError) issues.push(err);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
return issues;
|
|
663
|
-
}
|
|
664
|
-
/**
|
|
665
|
-
* Which columns have a satisfied `requiredCapability`? Manifest =
|
|
666
|
-
* discovery, so a local check is enough.
|
|
667
|
-
*/
|
|
668
|
-
eligibleColumns(manifest) {
|
|
669
|
-
const caps = manifest.capabilities ?? {};
|
|
670
|
-
return (manifest.tenants?.columns ?? []).filter((col) => {
|
|
671
|
-
if (!col.requiredCapability) return true;
|
|
672
|
-
return caps[col.requiredCapability] === true;
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
validateBatchEndpoint(col) {
|
|
676
|
-
if (!col.endpoint || col.endpoint.trim().length === 0) {
|
|
677
|
-
throw new BatchColumnDriftError(col, "endpoint ist leer");
|
|
678
|
-
}
|
|
679
|
-
if (col.endpoint.includes("{slug}") || col.endpoint.includes("{tenantId}")) {
|
|
680
|
-
throw new BatchColumnDriftError(
|
|
681
|
-
col,
|
|
682
|
-
"endpoint enth\xE4lt per-Tenant-Placeholder ({slug}/{tenantId}). Spalten-Endpoints m\xFCssen batchf\xE4hig sein \u2014 bitte Backend auf `?tenantIds=...`-Parameter umstellen."
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
buildUrl(endpoint, tenantIds) {
|
|
687
|
-
const sep = endpoint.includes("?") ? "&" : "?";
|
|
688
|
-
if (this.paramStyle === "comma") {
|
|
689
|
-
const enc = tenantIds.map(encodeURIComponent).join(",");
|
|
690
|
-
return `${endpoint}${sep}tenantIds=${enc}`;
|
|
691
|
-
}
|
|
692
|
-
const params = tenantIds.map((id) => `tenantIds=${encodeURIComponent(id)}`).join("&");
|
|
693
|
-
return `${endpoint}${sep}${params}`;
|
|
694
|
-
}
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
// src/use-api-list.ts
|
|
187
|
+
// src/vue/use-api-list.ts
|
|
698
188
|
import { ref, watch } from "vue";
|
|
699
189
|
function useApiList(options) {
|
|
700
190
|
const http = options.http ?? defaultHttpClient();
|
|
@@ -784,12 +274,12 @@ function useApiList(options) {
|
|
|
784
274
|
};
|
|
785
275
|
}
|
|
786
276
|
|
|
787
|
-
// src/use-tenants.ts
|
|
277
|
+
// src/vue/use-tenants.ts
|
|
788
278
|
import { ref as ref2 } from "vue";
|
|
789
279
|
function useTenants(options) {
|
|
790
280
|
if (!options?.endpoint) {
|
|
791
281
|
throw new Error(
|
|
792
|
-
'useTenants: `endpoint`
|
|
282
|
+
'useTenants: `endpoint` is required (e.g. "/api/v1/admin/tenants" or "/api/admin/tenants"). The platform has no default because apps use different globalPrefix conventions.'
|
|
793
283
|
);
|
|
794
284
|
}
|
|
795
285
|
const filter = options.filter ?? ref2({});
|
|
@@ -803,12 +293,12 @@ function useTenants(options) {
|
|
|
803
293
|
return { ...list, filter };
|
|
804
294
|
}
|
|
805
295
|
|
|
806
|
-
// src/use-audit-entries.ts
|
|
296
|
+
// src/vue/use-audit-entries.ts
|
|
807
297
|
import { ref as ref3 } from "vue";
|
|
808
298
|
function useAuditEntries(options) {
|
|
809
299
|
if (!options?.endpoint) {
|
|
810
300
|
throw new Error(
|
|
811
|
-
'useAuditEntries: `endpoint`
|
|
301
|
+
'useAuditEntries: `endpoint` is required (e.g. "/api/admin/audit" or "/api/v1/admin/audit"). The platform has no default because apps use different globalPrefix conventions.'
|
|
812
302
|
);
|
|
813
303
|
}
|
|
814
304
|
const filter = options.filter ?? ref3({});
|
|
@@ -822,12 +312,12 @@ function useAuditEntries(options) {
|
|
|
822
312
|
return { ...list, filter };
|
|
823
313
|
}
|
|
824
314
|
|
|
825
|
-
// src/use-entitlement.ts
|
|
315
|
+
// src/vue/use-entitlement.ts
|
|
826
316
|
import { ref as ref4 } from "vue";
|
|
827
317
|
function useEntitlement(options) {
|
|
828
318
|
if (!options?.endpoint) {
|
|
829
319
|
throw new Error(
|
|
830
|
-
'useEntitlement: `endpoint`
|
|
320
|
+
'useEntitlement: `endpoint` is required (e.g. "/api/billing/entitlement" or "/api/v1/billing/entitlement"). The platform has no default because apps use different globalPrefix conventions.'
|
|
831
321
|
);
|
|
832
322
|
}
|
|
833
323
|
const endpoint = options.endpoint;
|
|
@@ -863,13 +353,11 @@ function useEntitlement(options) {
|
|
|
863
353
|
return { entitlement, loading, error, load, hasFeature };
|
|
864
354
|
}
|
|
865
355
|
|
|
866
|
-
// src/use-tenant-manifest.ts
|
|
356
|
+
// src/vue/use-tenant-manifest.ts
|
|
867
357
|
import { ref as ref5 } from "vue";
|
|
868
358
|
function useTenantManifest(options) {
|
|
869
359
|
if (!options?.endpoint) {
|
|
870
|
-
throw new Error(
|
|
871
|
-
'useTenantManifest: `endpoint` ist Pflicht (z. B. "/api/tenant/manifest").'
|
|
872
|
-
);
|
|
360
|
+
throw new Error('useTenantManifest: `endpoint` is required (e.g. "/api/tenant/manifest").');
|
|
873
361
|
}
|
|
874
362
|
const http = options.http ?? defaultHttpClient();
|
|
875
363
|
const manifest = ref5(null);
|
|
@@ -907,8 +395,8 @@ function useTenantManifest(options) {
|
|
|
907
395
|
return { manifest, loading, error, load, hasFeature, quotaLimit };
|
|
908
396
|
}
|
|
909
397
|
|
|
910
|
-
// src/entitlement-provider.ts
|
|
911
|
-
import { inject as
|
|
398
|
+
// src/vue/entitlement-provider.ts
|
|
399
|
+
import { inject as inject3 } from "vue";
|
|
912
400
|
var ENTITLEMENT_INJECTION_KEY = /* @__PURE__ */ Symbol.for(
|
|
913
401
|
"@saasicat/ui-vue/ENTITLEMENT"
|
|
914
402
|
);
|
|
@@ -916,10 +404,10 @@ function provideEntitlement(app, entitlement) {
|
|
|
916
404
|
app.provide(ENTITLEMENT_INJECTION_KEY, entitlement);
|
|
917
405
|
}
|
|
918
406
|
function useInjectedEntitlement() {
|
|
919
|
-
return
|
|
407
|
+
return inject3(ENTITLEMENT_INJECTION_KEY, null);
|
|
920
408
|
}
|
|
921
409
|
|
|
922
|
-
// src/feature-router-guard.ts
|
|
410
|
+
// src/vue/feature-router-guard.ts
|
|
923
411
|
function extractRequired(to) {
|
|
924
412
|
const raw = to.meta?.requiresFeature;
|
|
925
413
|
if (!raw) return [];
|
|
@@ -944,7 +432,7 @@ function buildFeatureRouterGuard(options) {
|
|
|
944
432
|
};
|
|
945
433
|
}
|
|
946
434
|
|
|
947
|
-
// src/use-tenant-billing-catalog.ts
|
|
435
|
+
// src/vue/use-tenant-billing-catalog.ts
|
|
948
436
|
import { ref as ref6 } from "vue";
|
|
949
437
|
function useTenantBillingCatalog(options = {}) {
|
|
950
438
|
const apiPrefix = trimTrailingSlashes(options.apiPrefix ?? "/billing");
|
|
@@ -1002,7 +490,7 @@ function useTenantBillingCatalog(options = {}) {
|
|
|
1002
490
|
return { plans, featureRegistry, bundles, loading, error, load };
|
|
1003
491
|
}
|
|
1004
492
|
|
|
1005
|
-
// src/use-tenant-billing.ts
|
|
493
|
+
// src/vue/use-tenant-billing.ts
|
|
1006
494
|
import { ref as ref7 } from "vue";
|
|
1007
495
|
function useTenantBilling(options = {}) {
|
|
1008
496
|
const apiPrefix = trimTrailingSlashes(options.apiPrefix ?? "/billing");
|
|
@@ -1143,7 +631,7 @@ function useTenantBilling(options = {}) {
|
|
|
1143
631
|
};
|
|
1144
632
|
}
|
|
1145
633
|
|
|
1146
|
-
// src/use-subscription-draft.ts
|
|
634
|
+
// src/vue/use-subscription-draft.ts
|
|
1147
635
|
import { computed as computed2, ref as ref8 } from "vue";
|
|
1148
636
|
import {
|
|
1149
637
|
selectChargeableBundles
|
|
@@ -1302,7 +790,7 @@ function useSubscriptionDraft(options) {
|
|
|
1302
790
|
}
|
|
1303
791
|
function toApiPayload() {
|
|
1304
792
|
if (!plan.value) {
|
|
1305
|
-
throw new Error("useSubscriptionDraft: plan
|
|
793
|
+
throw new Error("useSubscriptionDraft: plan is not set");
|
|
1306
794
|
}
|
|
1307
795
|
const payload = {
|
|
1308
796
|
plan: plan.value,
|
|
@@ -1337,147 +825,13 @@ function useSubscriptionDraft(options) {
|
|
|
1337
825
|
};
|
|
1338
826
|
}
|
|
1339
827
|
|
|
1340
|
-
// src/
|
|
1341
|
-
var DEFAULT_I18N_DE = {
|
|
1342
|
-
sectionTitle: "Paket & Verbrauch",
|
|
1343
|
-
sectionSubtitle: "Aktuelles Paket, Verbrauch und Bundles.",
|
|
1344
|
-
loading: "Lade \u2026",
|
|
1345
|
-
noSubscription: "Keine Subscription f\xFCr diesen Mandanten gefunden.",
|
|
1346
|
-
activePlan: "Aktuelles Paket",
|
|
1347
|
-
cycleMonthly: "Monatlich",
|
|
1348
|
-
cycleYearly: "J\xE4hrlich",
|
|
1349
|
-
statusActive: "Aktiv",
|
|
1350
|
-
statusTrial: "Testphase",
|
|
1351
|
-
statusPastDue: "Zahlung \xFCberf\xE4llig",
|
|
1352
|
-
statusCanceled: "Gek\xFCndigt",
|
|
1353
|
-
statusPendingSales: "Vertrieb-Kl\xE4rung",
|
|
1354
|
-
trialEndsAt: "Testphase endet am",
|
|
1355
|
-
pilotEndsAt: "Pilot endet am",
|
|
1356
|
-
nextBillingDate: "N\xE4chste Abrechnung",
|
|
1357
|
-
pendingChange: "Ausstehender Plan-Wechsel",
|
|
1358
|
-
changeFromTo: "Wechsel zu",
|
|
1359
|
-
changeEffectiveAt: "wirksam ab",
|
|
1360
|
-
changePlanButton: "Paket wechseln",
|
|
1361
|
-
cancelSubscriptionButton: "Abonnement k\xFCndigen",
|
|
1362
|
-
usageTitle: "Verbrauch",
|
|
1363
|
-
featuresOverviewTitle: "Leistungsumfang",
|
|
1364
|
-
featuresActive: "Enthalten",
|
|
1365
|
-
featuresLocked: "Nicht enthalten",
|
|
1366
|
-
bundlesStoreTitle: "Bundles",
|
|
1367
|
-
bundlesBookedTitle: "Gebuchte Bundles",
|
|
1368
|
-
bundlesAvailableTitle: "Verf\xFCgbare Bundles",
|
|
1369
|
-
bundlesAvailableEmpty: "Aktuell sind keine zus\xE4tzlichen Bundles verf\xFCgbar.",
|
|
1370
|
-
bundlesPerMonth: "netto/Monat",
|
|
1371
|
-
bundleBookAction: "Bundle buchen",
|
|
1372
|
-
bundleBookInProgress: "Buchung l\xE4uft \u2026",
|
|
1373
|
-
bundleCancelAction: "K\xFCndigen",
|
|
1374
|
-
bundleReactivateAction: "Reaktivieren",
|
|
1375
|
-
bundleReactivateConfirmTitle: "Bundle reaktivieren",
|
|
1376
|
-
bundleReactivateConfirmBody: "Die K\xFCndigung wird r\xFCckg\xE4ngig gemacht. Das Bundle bleibt gebucht, l\xE4uft regul\xE4r weiter und wird wieder abgerechnet.",
|
|
1377
|
-
bundleCanceledAt: "Gek\xFCndigt zum",
|
|
1378
|
-
bundleMinimumTermUntil: "Mindestlaufzeit bis",
|
|
1379
|
-
bundleIncludesLabel: "Enth\xE4lt",
|
|
1380
|
-
bundleAlreadyBooked: "Bereits gebucht",
|
|
1381
|
-
bundleIncompatible: "Nicht kompatibel mit aktuellem Paket",
|
|
1382
|
-
bundleMissingRequires: "Ben\xF6tigt",
|
|
1383
|
-
bundlePreviewAddTitle: "Bundle buchen",
|
|
1384
|
-
bundlePreviewCancelTitle: "Bundle k\xFCndigen",
|
|
1385
|
-
bundlePreviewLoading: "Vorschau wird berechnet \u2026",
|
|
1386
|
-
bundlePreviewProrationTitle: "Anteilige Abrechnung",
|
|
1387
|
-
bundlePreviewProratedNow: "Heute anteilig f\xE4llig",
|
|
1388
|
-
bundlePreviewProrationDays: "Tage",
|
|
1389
|
-
bundlePreviewNextPeriod: "Regul\xE4r ab n\xE4chster Periode",
|
|
1390
|
-
bundlePreviewTrialNote: "W\xE4hrend der Testphase f\xE4llt nichts an \u2014 die Abrechnung beginnt mit der ersten bezahlten Periode.",
|
|
1391
|
-
bundlePreviewNoPrice: "F\xFCr den aktuellen Abrechnungszyklus ist kein Listenpreis gepflegt.",
|
|
1392
|
-
bundlePreviewMinimumTermLabel: "Mindestlaufzeit",
|
|
1393
|
-
bundlePreviewMinimumTermMonths: "Monate, bis",
|
|
1394
|
-
bundlePreviewMinimumTermNone: "Keine Mindestlaufzeit",
|
|
1395
|
-
bundlePreviewRedundantTitle: "Bereits enthaltene Features (w\xFCrden doppelt bezahlt)",
|
|
1396
|
-
bundlePreviewRedundantCoveredByPlan: "bereits im Plan",
|
|
1397
|
-
bundlePreviewRedundantCoveredByBundle: "bereits im Bundle",
|
|
1398
|
-
bundlePreviewMissingRequiresTitle: "Fehlende Voraussetzungen",
|
|
1399
|
-
bundlePreviewBlockersTitle: "Buchung nicht m\xF6glich",
|
|
1400
|
-
bundlePreviewWarningsTitle: "Hinweise",
|
|
1401
|
-
bundlePreviewEffectiveAt: "K\xFCndigung wirksam zum",
|
|
1402
|
-
bundlePreviewSavings: "Ersparnis pro Periode ab Wirksamkeit",
|
|
1403
|
-
bundlePreviewConfirmAdd: "Kostenpflichtig buchen",
|
|
1404
|
-
bundlePreviewConfirmCancel: "K\xFCndigung best\xE4tigen",
|
|
1405
|
-
bundlePreviewInProgress: "Wird ausgef\xFChrt \u2026",
|
|
1406
|
-
bundlePreviewClose: "Abbrechen",
|
|
1407
|
-
pendingVersionTitle: "Anstehende Plan-\xC4nderung",
|
|
1408
|
-
pendingVersionChipNonRegressive: "Verbessernd",
|
|
1409
|
-
pendingVersionChipRegressive: "Best\xE4tigung erforderlich",
|
|
1410
|
-
pendingVersionEffectiveAt: "Wirksam ab",
|
|
1411
|
-
pendingVersionAcceptAction: "\xC4nderungen akzeptieren",
|
|
1412
|
-
pendingVersionAcceptInProgress: "Akzeptiere \u2026",
|
|
1413
|
-
pendingVersionAcceptedAt: "Akzeptiert am",
|
|
1414
|
-
wizardTitle: "Paket wechseln",
|
|
1415
|
-
wizardClose: "Schlie\xDFen",
|
|
1416
|
-
wizardCurrent: "Aktuell",
|
|
1417
|
-
wizardBadgeCurrent: "aktiv",
|
|
1418
|
-
wizardBadgePopular: "beliebt",
|
|
1419
|
-
wizardPriceUnitMonthly: "netto/Monat",
|
|
1420
|
-
wizardPriceUnitYearly: "netto/Jahr",
|
|
1421
|
-
wizardPriceOnRequest: "auf Anfrage",
|
|
1422
|
-
wizardStepChoose: "Paket w\xE4hlen",
|
|
1423
|
-
wizardStepChooseIntro: "W\xE4hle dein Ziel-Paket und den Abrechnungszyklus. Im n\xE4chsten Schritt zeigen wir Verbrauchs-Check und Feature-Diff.",
|
|
1424
|
-
wizardStepPreview: "Vorschau",
|
|
1425
|
-
wizardStepConfirm: "Best\xE4tigen",
|
|
1426
|
-
wizardNext: "Weiter",
|
|
1427
|
-
wizardBack: "Zur\xFCck",
|
|
1428
|
-
wizardPreviewLoading: "Vorschau wird berechnet \u2026",
|
|
1429
|
-
wizardEffectiveAtLabel: "Wirksam ab",
|
|
1430
|
-
wizardEffectiveImmediate: "Wirksam sofort",
|
|
1431
|
-
wizardProrationTitle: "Anteilige Abrechnung",
|
|
1432
|
-
wizardProrationLine: "Mehrbetrag bis Periodenende:",
|
|
1433
|
-
wizardProrationDays: "Tage",
|
|
1434
|
-
wizardLimitsTitle: "Limit-Vergleich",
|
|
1435
|
-
wizardLimitsUsed: "Verbrauch",
|
|
1436
|
-
wizardLimitsCurrent: "Aktuell",
|
|
1437
|
-
wizardLimitsTarget: "Ziel",
|
|
1438
|
-
wizardFeaturesGained: "Neu freigeschaltet",
|
|
1439
|
-
wizardFeaturesLost: "Wegfallende Features",
|
|
1440
|
-
wizardBlockersTitle: "Verhinderungs-Gr\xFCnde",
|
|
1441
|
-
wizardConfirmImmediate: "Der Wechsel wird sofort wirksam.",
|
|
1442
|
-
wizardConfirmScheduled: "Der Wechsel wird wirksam zum",
|
|
1443
|
-
wizardConfirmAction: "Wechsel best\xE4tigen",
|
|
1444
|
-
wizardConfirmInProgress: "Wechsel l\xE4uft \u2026",
|
|
1445
|
-
wizardConfirmPriceTitle: "Preis\xFCbersicht",
|
|
1446
|
-
wizardConfirmProratedNow: "Heute anteilig f\xE4llig",
|
|
1447
|
-
wizardConfirmRecurringNext: "Regul\xE4r ab n\xE4chster Periode",
|
|
1448
|
-
wizardConfirmRecurringFrom: "Regul\xE4r f\xE4llig ab",
|
|
1449
|
-
wizardConfirmPerCycleMonthly: "pro Monat",
|
|
1450
|
-
wizardConfirmPerCycleYearly: "pro Jahr",
|
|
1451
|
-
wizardConfirmTrialNote: "W\xE4hrend der Testphase f\xE4llt nichts an.",
|
|
1452
|
-
wizardConfirmRecurringTrialEnd: "Regul\xE4r ab Ende der Testphase",
|
|
1453
|
-
wizardChangeTypeUpgrade: "Upgrade",
|
|
1454
|
-
wizardChangeTypeDowngrade: "Downgrade",
|
|
1455
|
-
wizardChangeTypeCycle: "Zyklus-Wechsel",
|
|
1456
|
-
wizardChangeTypeNoop: "Keine \xC4nderung",
|
|
1457
|
-
packageSnapshotTitle: "Gebuchtes Paket (Snapshot)",
|
|
1458
|
-
packageSnapshotSubtitle: "Schreibgesch\xFCtzte Kopie des Pakets, wie es beim Abschluss vermarktet wurde.",
|
|
1459
|
-
packageSnapshotCapturedAt: "Erfasst am",
|
|
1460
|
-
packageSnapshotOfferRef: "Angebots-Referenz",
|
|
1461
|
-
packageSnapshotPlanLabel: "Plan",
|
|
1462
|
-
packageSnapshotPlanVersionLabel: "Plan-Version",
|
|
1463
|
-
packageSnapshotCycleLabel: "Abrechnungszyklus",
|
|
1464
|
-
packageSnapshotBundlesLabel: "Enthaltene Bundles",
|
|
1465
|
-
packageSnapshotBundlesEmpty: "Keine Bundles im Paket.",
|
|
1466
|
-
packageSnapshotPriceMonthly: "Preis monatlich",
|
|
1467
|
-
packageSnapshotPriceYearly: "Preis j\xE4hrlich",
|
|
1468
|
-
packageSnapshotPriceTotal: "Gesamtpreis",
|
|
1469
|
-
packageSnapshotNone: "Diese Subscription wurde nicht \xFCber ein Webseiten-Angebot abgeschlossen.",
|
|
1470
|
-
packageSnapshotShowRaw: "Rohdaten anzeigen",
|
|
1471
|
-
packageSnapshotHideRaw: "Rohdaten ausblenden"
|
|
1472
|
-
};
|
|
1473
|
-
|
|
1474
|
-
// src/use-plan-versions.ts
|
|
828
|
+
// src/vue/use-plan-versions.ts
|
|
1475
829
|
import { ref as ref9 } from "vue";
|
|
1476
830
|
function buildVersionsComposable(label) {
|
|
1477
831
|
return function(options) {
|
|
1478
832
|
if (!options?.endpoint) {
|
|
1479
833
|
throw new Error(
|
|
1480
|
-
`${label}: \`endpoint\`
|
|
834
|
+
`${label}: \`endpoint\` is required (e.g. "/api/admin/${label}" or "/api/v1/admin/${label}"). The platform has no default because apps use different globalPrefix conventions.`
|
|
1481
835
|
);
|
|
1482
836
|
}
|
|
1483
837
|
const filter = options.filter ?? ref9({});
|
|
@@ -1493,12 +847,13 @@ function buildVersionsComposable(label) {
|
|
|
1493
847
|
}
|
|
1494
848
|
var usePlanVersionsCatalog = buildVersionsComposable("plan-versions");
|
|
1495
849
|
|
|
1496
|
-
// src/use-bulk-publish.ts
|
|
850
|
+
// src/vue/use-bulk-publish.ts
|
|
1497
851
|
import { computed as computed3, ref as ref10 } from "vue";
|
|
1498
852
|
function useBulkPublish(options) {
|
|
853
|
+
const msg = useSaMessages("planVersions");
|
|
1499
854
|
if (!options?.endpoints) {
|
|
1500
855
|
throw new Error(
|
|
1501
|
-
"useBulkPublish: `endpoints`
|
|
856
|
+
"useBulkPublish: `endpoints` is required (mapping plan \u2192 URL builder). The platform has no default because apps use different globalPrefix conventions."
|
|
1502
857
|
);
|
|
1503
858
|
}
|
|
1504
859
|
const http = options.http ?? defaultHttpClient();
|
|
@@ -1549,7 +904,7 @@ function useBulkPublish(options) {
|
|
|
1549
904
|
if (note.length === 0) {
|
|
1550
905
|
for (const item of items.value) {
|
|
1551
906
|
item.status = "failed";
|
|
1552
|
-
item.error =
|
|
907
|
+
item.error = msg.value.bulkPublish.changeNoteRequired;
|
|
1553
908
|
}
|
|
1554
909
|
return;
|
|
1555
910
|
}
|
|
@@ -1568,12 +923,12 @@ function useBulkPublish(options) {
|
|
|
1568
923
|
};
|
|
1569
924
|
}
|
|
1570
925
|
|
|
1571
|
-
// src/use-plan-editor.ts
|
|
926
|
+
// src/vue/use-plan-editor.ts
|
|
1572
927
|
import { computed as computed4, ref as ref11 } from "vue";
|
|
1573
928
|
var PlannedOnlyFeatureError = class extends Error {
|
|
1574
929
|
constructor(violations) {
|
|
1575
930
|
super(
|
|
1576
|
-
`
|
|
931
|
+
`The following features are marked as plannedOnly in the catalog and must not appear in a plan version: ${violations.sort().join(", ")}.`
|
|
1577
932
|
);
|
|
1578
933
|
this.violations = violations;
|
|
1579
934
|
this.name = "PlannedOnlyFeatureError";
|
|
@@ -1645,7 +1000,7 @@ function usePlanEditor(manifest, options = {}) {
|
|
|
1645
1000
|
};
|
|
1646
1001
|
}
|
|
1647
1002
|
|
|
1648
|
-
// src/use-public-boot.ts
|
|
1003
|
+
// src/vue/use-public-boot.ts
|
|
1649
1004
|
import { ref as ref12 } from "vue";
|
|
1650
1005
|
function usePublicBoot(options) {
|
|
1651
1006
|
const loader = new BootLoader(options);
|
|
@@ -1667,7 +1022,7 @@ function usePublicBoot(options) {
|
|
|
1667
1022
|
return { boot, loading, error, load };
|
|
1668
1023
|
}
|
|
1669
1024
|
|
|
1670
|
-
// src/use-discovery.ts
|
|
1025
|
+
// src/vue/use-discovery.ts
|
|
1671
1026
|
import { ref as ref13 } from "vue";
|
|
1672
1027
|
var DiscoveryLoadError = class extends Error {
|
|
1673
1028
|
constructor(status, message) {
|
|
@@ -1678,9 +1033,10 @@ var DiscoveryLoadError = class extends Error {
|
|
|
1678
1033
|
status;
|
|
1679
1034
|
};
|
|
1680
1035
|
function useDiscovery(options) {
|
|
1036
|
+
const msg = useSaMessages("discovery");
|
|
1681
1037
|
if (!options?.endpoint) {
|
|
1682
1038
|
throw new Error(
|
|
1683
|
-
'useDiscovery: `endpoint`
|
|
1039
|
+
'useDiscovery: `endpoint` is required (e.g. "/api/admin/discovery" or "/api/v1/admin/discovery"). The platform has no default because apps use different globalPrefix conventions.'
|
|
1684
1040
|
);
|
|
1685
1041
|
}
|
|
1686
1042
|
const http = options.http ?? defaultHttpClient();
|
|
@@ -1705,7 +1061,7 @@ function useDiscovery(options) {
|
|
|
1705
1061
|
if (res.status !== 200) {
|
|
1706
1062
|
throw new DiscoveryLoadError(
|
|
1707
1063
|
res.status,
|
|
1708
|
-
|
|
1064
|
+
formatMessage(msg.value.errorDiscoveryHttp, { status: res.status })
|
|
1709
1065
|
);
|
|
1710
1066
|
}
|
|
1711
1067
|
const body = await res.json();
|
|
@@ -1735,7 +1091,7 @@ function useDiscovery(options) {
|
|
|
1735
1091
|
if (res.status !== 200 && res.status !== 201) {
|
|
1736
1092
|
throw new DiscoveryLoadError(
|
|
1737
1093
|
res.status,
|
|
1738
|
-
|
|
1094
|
+
formatMessage(msg.value.errorRescanHttp, { status: res.status })
|
|
1739
1095
|
);
|
|
1740
1096
|
}
|
|
1741
1097
|
const body = await res.json();
|
|
@@ -1753,7 +1109,7 @@ function useDiscovery(options) {
|
|
|
1753
1109
|
return { snapshot, etag, loading, error, load, reload, rescan };
|
|
1754
1110
|
}
|
|
1755
1111
|
|
|
1756
|
-
// src/use-catalog-entries.ts
|
|
1112
|
+
// src/vue/use-catalog-entries.ts
|
|
1757
1113
|
import { ref as ref14 } from "vue";
|
|
1758
1114
|
var CatalogEntriesApiError = class extends Error {
|
|
1759
1115
|
constructor(status, body, message) {
|
|
@@ -1767,11 +1123,12 @@ var CatalogEntriesApiError = class extends Error {
|
|
|
1767
1123
|
};
|
|
1768
1124
|
function useCatalogEntries(options) {
|
|
1769
1125
|
if (!options?.adminEndpoint) {
|
|
1770
|
-
throw new Error("useCatalogEntries: `adminEndpoint`
|
|
1126
|
+
throw new Error("useCatalogEntries: `adminEndpoint` is required.");
|
|
1771
1127
|
}
|
|
1772
1128
|
if (!options?.projectKey) {
|
|
1773
|
-
throw new Error("useCatalogEntries: `projectKey`
|
|
1129
|
+
throw new Error("useCatalogEntries: `projectKey` is required.");
|
|
1774
1130
|
}
|
|
1131
|
+
const msg = useSaMessages("discovery");
|
|
1775
1132
|
const http = options.http ?? defaultHttpClient();
|
|
1776
1133
|
const capabilities = ref14([]);
|
|
1777
1134
|
const features = ref14([]);
|
|
@@ -1796,7 +1153,7 @@ function useCatalogEntries(options) {
|
|
|
1796
1153
|
throw new CatalogEntriesApiError(
|
|
1797
1154
|
res.status,
|
|
1798
1155
|
body,
|
|
1799
|
-
|
|
1156
|
+
formatMessage(msg.value.errorCatalogHttp, { status: res.status })
|
|
1800
1157
|
);
|
|
1801
1158
|
}
|
|
1802
1159
|
return body;
|
|
@@ -1824,7 +1181,7 @@ function useCatalogEntries(options) {
|
|
|
1824
1181
|
`${base}/features/${encodeURIComponent(featureKey)}/review?projectKey=${pk}`,
|
|
1825
1182
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
1826
1183
|
);
|
|
1827
|
-
if (!updated) throw new CatalogEntriesApiError(0, null, "Review
|
|
1184
|
+
if (!updated) throw new CatalogEntriesApiError(0, null, "Review returned no body");
|
|
1828
1185
|
features.value = features.value.map((f) => f.featureKey === featureKey ? updated : f);
|
|
1829
1186
|
return updated;
|
|
1830
1187
|
}
|
|
@@ -1833,7 +1190,7 @@ function useCatalogEntries(options) {
|
|
|
1833
1190
|
`${base}/quotas/${encodeURIComponent(quotaKey)}/review?projectKey=${pk}`,
|
|
1834
1191
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
1835
1192
|
);
|
|
1836
|
-
if (!updated) throw new CatalogEntriesApiError(0, null, "Review
|
|
1193
|
+
if (!updated) throw new CatalogEntriesApiError(0, null, "Review returned no body");
|
|
1837
1194
|
quotas.value = quotas.value.map((q) => q.quotaKey === quotaKey ? updated : q);
|
|
1838
1195
|
return updated;
|
|
1839
1196
|
}
|
|
@@ -1842,7 +1199,7 @@ function useCatalogEntries(options) {
|
|
|
1842
1199
|
`${base}/features/${encodeURIComponent(featureKey)}/i18n?projectKey=${pk}`,
|
|
1843
1200
|
{ method: "PATCH", body: JSON.stringify({ i18n }) }
|
|
1844
1201
|
);
|
|
1845
|
-
if (!updated) throw new CatalogEntriesApiError(0, null, "i18n
|
|
1202
|
+
if (!updated) throw new CatalogEntriesApiError(0, null, "i18n returned no body");
|
|
1846
1203
|
features.value = features.value.map((f) => f.featureKey === featureKey ? updated : f);
|
|
1847
1204
|
return updated;
|
|
1848
1205
|
}
|
|
@@ -1851,7 +1208,7 @@ function useCatalogEntries(options) {
|
|
|
1851
1208
|
`${base}/quotas/${encodeURIComponent(quotaKey)}/i18n?projectKey=${pk}`,
|
|
1852
1209
|
{ method: "PATCH", body: JSON.stringify({ i18n }) }
|
|
1853
1210
|
);
|
|
1854
|
-
if (!updated) throw new CatalogEntriesApiError(0, null, "i18n
|
|
1211
|
+
if (!updated) throw new CatalogEntriesApiError(0, null, "i18n returned no body");
|
|
1855
1212
|
quotas.value = quotas.value.map((q) => q.quotaKey === quotaKey ? updated : q);
|
|
1856
1213
|
return updated;
|
|
1857
1214
|
}
|
|
@@ -1860,7 +1217,7 @@ function useCatalogEntries(options) {
|
|
|
1860
1217
|
`${base}/features/${encodeURIComponent(featureKey)}?projectKey=${pk}`,
|
|
1861
1218
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
1862
1219
|
);
|
|
1863
|
-
if (!updated) throw new CatalogEntriesApiError(0, null, "Base
|
|
1220
|
+
if (!updated) throw new CatalogEntriesApiError(0, null, "Base returned no body");
|
|
1864
1221
|
features.value = features.value.map((f) => f.featureKey === featureKey ? updated : f);
|
|
1865
1222
|
return updated;
|
|
1866
1223
|
}
|
|
@@ -1869,7 +1226,7 @@ function useCatalogEntries(options) {
|
|
|
1869
1226
|
`${base}/quotas/${encodeURIComponent(quotaKey)}?projectKey=${pk}`,
|
|
1870
1227
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
1871
1228
|
);
|
|
1872
|
-
if (!updated) throw new CatalogEntriesApiError(0, null, "Base
|
|
1229
|
+
if (!updated) throw new CatalogEntriesApiError(0, null, "Base returned no body");
|
|
1873
1230
|
quotas.value = quotas.value.map((q) => q.quotaKey === quotaKey ? updated : q);
|
|
1874
1231
|
return updated;
|
|
1875
1232
|
}
|
|
@@ -1878,7 +1235,7 @@ function useCatalogEntries(options) {
|
|
|
1878
1235
|
method: "POST",
|
|
1879
1236
|
body: JSON.stringify({ snapshot })
|
|
1880
1237
|
});
|
|
1881
|
-
if (!result) throw new CatalogEntriesApiError(0, null, "Sync
|
|
1238
|
+
if (!result) throw new CatalogEntriesApiError(0, null, "Sync returned no body");
|
|
1882
1239
|
await load();
|
|
1883
1240
|
return result;
|
|
1884
1241
|
}
|
|
@@ -1902,7 +1259,7 @@ function useCatalogEntries(options) {
|
|
|
1902
1259
|
};
|
|
1903
1260
|
}
|
|
1904
1261
|
|
|
1905
|
-
// src/use-bundles.ts
|
|
1262
|
+
// src/vue/use-bundles.ts
|
|
1906
1263
|
import { ref as ref15 } from "vue";
|
|
1907
1264
|
var BundlesApiError = class extends Error {
|
|
1908
1265
|
constructor(status, body, message) {
|
|
@@ -1917,11 +1274,11 @@ var BundlesApiError = class extends Error {
|
|
|
1917
1274
|
function useBundles(options) {
|
|
1918
1275
|
if (!options?.adminEndpoint) {
|
|
1919
1276
|
throw new Error(
|
|
1920
|
-
'useBundles: `adminEndpoint`
|
|
1277
|
+
'useBundles: `adminEndpoint` is required (e.g. "/api/admin" or "/api/v1/admin"). The platform has no default, because apps use different globalPrefix conventions.'
|
|
1921
1278
|
);
|
|
1922
1279
|
}
|
|
1923
1280
|
if (!options?.projectKey) {
|
|
1924
|
-
throw new Error("useBundles: `projectKey`
|
|
1281
|
+
throw new Error("useBundles: `projectKey` is required.");
|
|
1925
1282
|
}
|
|
1926
1283
|
const http = options.http ?? defaultHttpClient();
|
|
1927
1284
|
const bundles = ref15([]);
|
|
@@ -1944,7 +1301,7 @@ function useBundles(options) {
|
|
|
1944
1301
|
throw new BundlesApiError(
|
|
1945
1302
|
res.status,
|
|
1946
1303
|
body,
|
|
1947
|
-
`Bundles
|
|
1304
|
+
`Bundles API responded with HTTP ${res.status}`
|
|
1948
1305
|
);
|
|
1949
1306
|
}
|
|
1950
1307
|
return body;
|
|
@@ -1968,7 +1325,7 @@ function useBundles(options) {
|
|
|
1968
1325
|
method: "POST",
|
|
1969
1326
|
body: JSON.stringify(data)
|
|
1970
1327
|
});
|
|
1971
|
-
if (!created) throw new BundlesApiError(0, null, "Create
|
|
1328
|
+
if (!created) throw new BundlesApiError(0, null, "Create returned no body");
|
|
1972
1329
|
bundles.value = [...bundles.value, created];
|
|
1973
1330
|
return created;
|
|
1974
1331
|
}
|
|
@@ -1977,7 +1334,7 @@ function useBundles(options) {
|
|
|
1977
1334
|
method: "PATCH",
|
|
1978
1335
|
body: JSON.stringify(data)
|
|
1979
1336
|
});
|
|
1980
|
-
if (!updated) throw new BundlesApiError(0, null, "Update
|
|
1337
|
+
if (!updated) throw new BundlesApiError(0, null, "Update returned no body");
|
|
1981
1338
|
bundles.value = bundles.value.map((b) => b.id === bundleId ? updated : b);
|
|
1982
1339
|
return updated;
|
|
1983
1340
|
}
|
|
@@ -1992,10 +1349,10 @@ function useBundles(options) {
|
|
|
1992
1349
|
}
|
|
1993
1350
|
function useBundleVersions(options) {
|
|
1994
1351
|
if (!options?.adminEndpoint) {
|
|
1995
|
-
throw new Error("useBundleVersions: `adminEndpoint`
|
|
1352
|
+
throw new Error("useBundleVersions: `adminEndpoint` is required.");
|
|
1996
1353
|
}
|
|
1997
1354
|
if (!options?.bundleId) {
|
|
1998
|
-
throw new Error("useBundleVersions: `bundleId`
|
|
1355
|
+
throw new Error("useBundleVersions: `bundleId` is required.");
|
|
1999
1356
|
}
|
|
2000
1357
|
const http = options.http ?? defaultHttpClient();
|
|
2001
1358
|
const versions = ref15([]);
|
|
@@ -2019,7 +1376,7 @@ function useBundleVersions(options) {
|
|
|
2019
1376
|
throw new BundlesApiError(
|
|
2020
1377
|
res.status,
|
|
2021
1378
|
body,
|
|
2022
|
-
`BundleVersions
|
|
1379
|
+
`BundleVersions API responded with HTTP ${res.status}`
|
|
2023
1380
|
);
|
|
2024
1381
|
}
|
|
2025
1382
|
return body;
|
|
@@ -2041,7 +1398,7 @@ function useBundleVersions(options) {
|
|
|
2041
1398
|
method: "POST",
|
|
2042
1399
|
body: JSON.stringify(data)
|
|
2043
1400
|
});
|
|
2044
|
-
if (!result) throw new BundlesApiError(0, null, "CreateDraft
|
|
1401
|
+
if (!result) throw new BundlesApiError(0, null, "CreateDraft returned no body");
|
|
2045
1402
|
versions.value = [...versions.value, result.bundleVersion];
|
|
2046
1403
|
return result;
|
|
2047
1404
|
}
|
|
@@ -2050,7 +1407,7 @@ function useBundleVersions(options) {
|
|
|
2050
1407
|
`${versionUrlBase}/${versionId}`,
|
|
2051
1408
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
2052
1409
|
);
|
|
2053
|
-
if (!result) throw new BundlesApiError(0, null, "UpdateDraft
|
|
1410
|
+
if (!result) throw new BundlesApiError(0, null, "UpdateDraft returned no body");
|
|
2054
1411
|
versions.value = versions.value.map((v) => v.id === versionId ? result.bundleVersion : v);
|
|
2055
1412
|
return result;
|
|
2056
1413
|
}
|
|
@@ -2059,7 +1416,7 @@ function useBundleVersions(options) {
|
|
|
2059
1416
|
`${versionUrlBase}/${versionId}/publish`,
|
|
2060
1417
|
{ method: "POST", body: JSON.stringify(opts) }
|
|
2061
1418
|
);
|
|
2062
|
-
if (!result) throw new BundlesApiError(0, null, "Publish
|
|
1419
|
+
if (!result) throw new BundlesApiError(0, null, "Publish returned no body");
|
|
2063
1420
|
await load();
|
|
2064
1421
|
return result;
|
|
2065
1422
|
}
|
|
@@ -2073,14 +1430,14 @@ function useBundleVersions(options) {
|
|
|
2073
1430
|
return { versions, loading, error, load, createDraft, updateDraft, publish, discardDraft };
|
|
2074
1431
|
}
|
|
2075
1432
|
|
|
2076
|
-
// src/use-bundle-versions-map.ts
|
|
1433
|
+
// src/vue/use-bundle-versions-map.ts
|
|
2077
1434
|
import { ref as ref16, watch as watch2 } from "vue";
|
|
2078
1435
|
function useBundleVersionsMap(options) {
|
|
2079
1436
|
if (!options?.adminEndpoint) {
|
|
2080
|
-
throw new Error("useBundleVersionsMap: `adminEndpoint`
|
|
1437
|
+
throw new Error("useBundleVersionsMap: `adminEndpoint` is required.");
|
|
2081
1438
|
}
|
|
2082
1439
|
if (!options?.bundles) {
|
|
2083
|
-
throw new Error("useBundleVersionsMap: `bundles`
|
|
1440
|
+
throw new Error("useBundleVersionsMap: `bundles` is required.");
|
|
2084
1441
|
}
|
|
2085
1442
|
const http = options.http ?? defaultHttpClient();
|
|
2086
1443
|
const versionsByBundle = ref16({});
|
|
@@ -2096,7 +1453,9 @@ function useBundleVersionsMap(options) {
|
|
|
2096
1453
|
headers: { "content-type": "application/json", ...authHeaders() }
|
|
2097
1454
|
});
|
|
2098
1455
|
if (res.status >= 400) {
|
|
2099
|
-
throw new Error(
|
|
1456
|
+
throw new Error(
|
|
1457
|
+
`HTTP ${res.status} while loading the versions for bundle '${bundleId}'`
|
|
1458
|
+
);
|
|
2100
1459
|
}
|
|
2101
1460
|
return await res.json().catch(() => null) ?? [];
|
|
2102
1461
|
}
|
|
@@ -2111,7 +1470,7 @@ function useBundleVersionsMap(options) {
|
|
|
2111
1470
|
return [b.id, versions];
|
|
2112
1471
|
} catch (err) {
|
|
2113
1472
|
console.warn(
|
|
2114
|
-
`useBundleVersionsMap:
|
|
1473
|
+
`useBundleVersionsMap: loading versions for bundle '${b.bundleKey}' failed`,
|
|
2115
1474
|
err
|
|
2116
1475
|
);
|
|
2117
1476
|
return [b.id, []];
|
|
@@ -2144,7 +1503,7 @@ function useBundleVersionsMap(options) {
|
|
|
2144
1503
|
return { versionsByBundle, loading, error, refresh, refreshOne };
|
|
2145
1504
|
}
|
|
2146
1505
|
|
|
2147
|
-
// src/use-tenant-subscription-bundles.ts
|
|
1506
|
+
// src/vue/use-tenant-subscription-bundles.ts
|
|
2148
1507
|
import { ref as ref17 } from "vue";
|
|
2149
1508
|
var TenantSubscriptionBundlesApiError = class extends Error {
|
|
2150
1509
|
constructor(status, body, message) {
|
|
@@ -2159,7 +1518,7 @@ var TenantSubscriptionBundlesApiError = class extends Error {
|
|
|
2159
1518
|
function useTenantSubscriptionBundles(options) {
|
|
2160
1519
|
if (!options?.billingEndpoint) {
|
|
2161
1520
|
throw new Error(
|
|
2162
|
-
'useTenantSubscriptionBundles: `billingEndpoint`
|
|
1521
|
+
'useTenantSubscriptionBundles: `billingEndpoint` is required (e.g. "/api/v1").'
|
|
2163
1522
|
);
|
|
2164
1523
|
}
|
|
2165
1524
|
const http = options.http ?? defaultHttpClient();
|
|
@@ -2183,7 +1542,7 @@ function useTenantSubscriptionBundles(options) {
|
|
|
2183
1542
|
throw new TenantSubscriptionBundlesApiError(
|
|
2184
1543
|
res.status,
|
|
2185
1544
|
body,
|
|
2186
|
-
`SubscriptionBundle
|
|
1545
|
+
`SubscriptionBundle API responded with HTTP ${res.status}`
|
|
2187
1546
|
);
|
|
2188
1547
|
}
|
|
2189
1548
|
return body;
|
|
@@ -2206,7 +1565,7 @@ function useTenantSubscriptionBundles(options) {
|
|
|
2206
1565
|
body: JSON.stringify(data)
|
|
2207
1566
|
});
|
|
2208
1567
|
if (!result) {
|
|
2209
|
-
throw new TenantSubscriptionBundlesApiError(0, null, "add
|
|
1568
|
+
throw new TenantSubscriptionBundlesApiError(0, null, "add returned no body");
|
|
2210
1569
|
}
|
|
2211
1570
|
const hydrated = rehydrateDates(result);
|
|
2212
1571
|
bundles.value = [hydrated, ...bundles.value];
|
|
@@ -2218,7 +1577,7 @@ function useTenantSubscriptionBundles(options) {
|
|
|
2218
1577
|
{ method: "DELETE", body: JSON.stringify(opts) }
|
|
2219
1578
|
);
|
|
2220
1579
|
if (!result) {
|
|
2221
|
-
throw new TenantSubscriptionBundlesApiError(0, null, "cancel
|
|
1580
|
+
throw new TenantSubscriptionBundlesApiError(0, null, "cancel returned no body");
|
|
2222
1581
|
}
|
|
2223
1582
|
const hydrated = rehydrateDates(result);
|
|
2224
1583
|
bundles.value = bundles.value.map((b) => b.id === subscriptionBundleId ? hydrated : b);
|
|
@@ -2239,177 +1598,8 @@ function rehydrateDates(raw) {
|
|
|
2239
1598
|
};
|
|
2240
1599
|
}
|
|
2241
1600
|
|
|
2242
|
-
// src/use-
|
|
1601
|
+
// src/vue/use-marketing-projections.ts
|
|
2243
1602
|
import { ref as ref18 } from "vue";
|
|
2244
|
-
var BusinessTypesApiError = class extends Error {
|
|
2245
|
-
constructor(status, body, message) {
|
|
2246
|
-
super(message);
|
|
2247
|
-
this.status = status;
|
|
2248
|
-
this.body = body;
|
|
2249
|
-
this.name = "BusinessTypesApiError";
|
|
2250
|
-
}
|
|
2251
|
-
status;
|
|
2252
|
-
body;
|
|
2253
|
-
};
|
|
2254
|
-
function useBusinessTypes(options) {
|
|
2255
|
-
if (!options?.adminEndpoint) {
|
|
2256
|
-
throw new Error("useBusinessTypes: `adminEndpoint` ist Pflicht.");
|
|
2257
|
-
}
|
|
2258
|
-
if (!options?.projectKey) {
|
|
2259
|
-
throw new Error("useBusinessTypes: `projectKey` ist Pflicht.");
|
|
2260
|
-
}
|
|
2261
|
-
const http = options.http ?? defaultHttpClient();
|
|
2262
|
-
const businessTypes = ref18([]);
|
|
2263
|
-
const loading = ref18(false);
|
|
2264
|
-
const error = ref18(null);
|
|
2265
|
-
const baseUrl = `${options.adminEndpoint}/catalog/business-types`;
|
|
2266
|
-
function authHeaders() {
|
|
2267
|
-
const token = options.getAuthToken?.();
|
|
2268
|
-
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
2269
|
-
}
|
|
2270
|
-
async function fetchJson(url, init) {
|
|
2271
|
-
const res = await http(url, {
|
|
2272
|
-
method: init?.method ?? "GET",
|
|
2273
|
-
headers: { "content-type": "application/json", ...authHeaders(), ...init?.headers },
|
|
2274
|
-
body: init?.body
|
|
2275
|
-
});
|
|
2276
|
-
if (res.status === 204) return null;
|
|
2277
|
-
const body = await res.json().catch(() => null);
|
|
2278
|
-
if (res.status >= 400) {
|
|
2279
|
-
throw new BusinessTypesApiError(
|
|
2280
|
-
res.status,
|
|
2281
|
-
body,
|
|
2282
|
-
`BusinessTypes-API antwortete mit HTTP ${res.status}`
|
|
2283
|
-
);
|
|
2284
|
-
}
|
|
2285
|
-
return body;
|
|
2286
|
-
}
|
|
2287
|
-
async function load() {
|
|
2288
|
-
loading.value = true;
|
|
2289
|
-
error.value = null;
|
|
2290
|
-
try {
|
|
2291
|
-
const data = await fetchJson(
|
|
2292
|
-
`${baseUrl}?projectKey=${encodeURIComponent(options.projectKey)}`
|
|
2293
|
-
);
|
|
2294
|
-
businessTypes.value = data ?? [];
|
|
2295
|
-
} catch (err) {
|
|
2296
|
-
error.value = err instanceof Error ? err : new Error(String(err));
|
|
2297
|
-
} finally {
|
|
2298
|
-
loading.value = false;
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
async function create(data) {
|
|
2302
|
-
const created = await fetchJson(baseUrl, {
|
|
2303
|
-
method: "POST",
|
|
2304
|
-
body: JSON.stringify(data)
|
|
2305
|
-
});
|
|
2306
|
-
if (!created) throw new BusinessTypesApiError(0, null, "Create gab keinen Body zur\xFCck");
|
|
2307
|
-
businessTypes.value = [...businessTypes.value, created];
|
|
2308
|
-
return created;
|
|
2309
|
-
}
|
|
2310
|
-
async function update(businessTypeId, data) {
|
|
2311
|
-
const updated = await fetchJson(`${baseUrl}/${businessTypeId}`, {
|
|
2312
|
-
method: "PATCH",
|
|
2313
|
-
body: JSON.stringify(data)
|
|
2314
|
-
});
|
|
2315
|
-
if (!updated) throw new BusinessTypesApiError(0, null, "Update gab keinen Body zur\xFCck");
|
|
2316
|
-
businessTypes.value = businessTypes.value.map(
|
|
2317
|
-
(b) => b.id === businessTypeId ? updated : b
|
|
2318
|
-
);
|
|
2319
|
-
return updated;
|
|
2320
|
-
}
|
|
2321
|
-
async function softDelete(businessTypeId) {
|
|
2322
|
-
await fetchJson(`${baseUrl}/${businessTypeId}`, { method: "DELETE" });
|
|
2323
|
-
businessTypes.value = businessTypes.value.filter((b) => b.id !== businessTypeId);
|
|
2324
|
-
}
|
|
2325
|
-
if (options.autoLoad) {
|
|
2326
|
-
void load();
|
|
2327
|
-
}
|
|
2328
|
-
return { businessTypes, loading, error, load, create, update, softDelete };
|
|
2329
|
-
}
|
|
2330
|
-
function useBusinessTypeVersions(options) {
|
|
2331
|
-
if (!options?.adminEndpoint) {
|
|
2332
|
-
throw new Error("useBusinessTypeVersions: `adminEndpoint` ist Pflicht.");
|
|
2333
|
-
}
|
|
2334
|
-
if (!options?.businessTypeId) {
|
|
2335
|
-
throw new Error("useBusinessTypeVersions: `businessTypeId` ist Pflicht.");
|
|
2336
|
-
}
|
|
2337
|
-
const http = options.http ?? defaultHttpClient();
|
|
2338
|
-
const versions = ref18([]);
|
|
2339
|
-
const loading = ref18(false);
|
|
2340
|
-
const error = ref18(null);
|
|
2341
|
-
const versionsUrl = `${options.adminEndpoint}/catalog/business-types/${options.businessTypeId}/versions`;
|
|
2342
|
-
const versionUrlBase = `${options.adminEndpoint}/catalog/business-type-versions`;
|
|
2343
|
-
function authHeaders() {
|
|
2344
|
-
const token = options.getAuthToken?.();
|
|
2345
|
-
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
2346
|
-
}
|
|
2347
|
-
async function fetchJson(url, init) {
|
|
2348
|
-
const res = await http(url, {
|
|
2349
|
-
method: init?.method ?? "GET",
|
|
2350
|
-
headers: { "content-type": "application/json", ...authHeaders(), ...init?.headers },
|
|
2351
|
-
body: init?.body
|
|
2352
|
-
});
|
|
2353
|
-
if (res.status === 204) return null;
|
|
2354
|
-
const body = await res.json().catch(() => null);
|
|
2355
|
-
if (res.status >= 400) {
|
|
2356
|
-
throw new BusinessTypesApiError(
|
|
2357
|
-
res.status,
|
|
2358
|
-
body,
|
|
2359
|
-
`BusinessTypeVersions-API antwortete mit HTTP ${res.status}`
|
|
2360
|
-
);
|
|
2361
|
-
}
|
|
2362
|
-
return body;
|
|
2363
|
-
}
|
|
2364
|
-
async function load() {
|
|
2365
|
-
loading.value = true;
|
|
2366
|
-
error.value = null;
|
|
2367
|
-
try {
|
|
2368
|
-
const data = await fetchJson(versionsUrl);
|
|
2369
|
-
versions.value = data ?? [];
|
|
2370
|
-
} catch (err) {
|
|
2371
|
-
error.value = err instanceof Error ? err : new Error(String(err));
|
|
2372
|
-
} finally {
|
|
2373
|
-
loading.value = false;
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
async function createDraft(data) {
|
|
2377
|
-
const result = await fetchJson(versionsUrl, {
|
|
2378
|
-
method: "POST",
|
|
2379
|
-
body: JSON.stringify(data)
|
|
2380
|
-
});
|
|
2381
|
-
if (!result) throw new BusinessTypesApiError(0, null, "CreateDraft gab keinen Body zur\xFCck");
|
|
2382
|
-
versions.value = [...versions.value, result.businessTypeVersion];
|
|
2383
|
-
return result;
|
|
2384
|
-
}
|
|
2385
|
-
async function updateDraft(versionId, data) {
|
|
2386
|
-
const result = await fetchJson(
|
|
2387
|
-
`${versionUrlBase}/${versionId}`,
|
|
2388
|
-
{ method: "PATCH", body: JSON.stringify(data) }
|
|
2389
|
-
);
|
|
2390
|
-
if (!result) throw new BusinessTypesApiError(0, null, "UpdateDraft gab keinen Body zur\xFCck");
|
|
2391
|
-
versions.value = versions.value.map(
|
|
2392
|
-
(v) => v.id === versionId ? result.businessTypeVersion : v
|
|
2393
|
-
);
|
|
2394
|
-
return result;
|
|
2395
|
-
}
|
|
2396
|
-
async function publish(versionId, opts = {}) {
|
|
2397
|
-
const result = await fetchJson(
|
|
2398
|
-
`${versionUrlBase}/${versionId}/publish`,
|
|
2399
|
-
{ method: "POST", body: JSON.stringify(opts) }
|
|
2400
|
-
);
|
|
2401
|
-
if (!result) throw new BusinessTypesApiError(0, null, "Publish gab keinen Body zur\xFCck");
|
|
2402
|
-
await load();
|
|
2403
|
-
return result;
|
|
2404
|
-
}
|
|
2405
|
-
if (options.autoLoad) {
|
|
2406
|
-
void load();
|
|
2407
|
-
}
|
|
2408
|
-
return { versions, loading, error, load, createDraft, updateDraft, publish };
|
|
2409
|
-
}
|
|
2410
|
-
|
|
2411
|
-
// src/use-marketing-projections.ts
|
|
2412
|
-
import { ref as ref19 } from "vue";
|
|
2413
1603
|
var MarketingProjectionsApiError = class extends Error {
|
|
2414
1604
|
constructor(status, body, message) {
|
|
2415
1605
|
super(message);
|
|
@@ -2422,16 +1612,17 @@ var MarketingProjectionsApiError = class extends Error {
|
|
|
2422
1612
|
};
|
|
2423
1613
|
function useMarketingProjections(options) {
|
|
2424
1614
|
if (!options?.adminEndpoint) {
|
|
2425
|
-
throw new Error("useMarketingProjections: `adminEndpoint`
|
|
1615
|
+
throw new Error("useMarketingProjections: `adminEndpoint` is required.");
|
|
2426
1616
|
}
|
|
2427
1617
|
if (!options?.filter?.projectKey) {
|
|
2428
|
-
throw new Error("useMarketingProjections: `filter.projectKey`
|
|
1618
|
+
throw new Error("useMarketingProjections: `filter.projectKey` is required.");
|
|
2429
1619
|
}
|
|
1620
|
+
const msg = useSaMessages("marketing");
|
|
2430
1621
|
const http = options.http ?? defaultHttpClient();
|
|
2431
|
-
const projections =
|
|
2432
|
-
const filter =
|
|
2433
|
-
const loading =
|
|
2434
|
-
const error =
|
|
1622
|
+
const projections = ref18([]);
|
|
1623
|
+
const filter = ref18({ ...options.filter });
|
|
1624
|
+
const loading = ref18(false);
|
|
1625
|
+
const error = ref18(null);
|
|
2435
1626
|
const baseUrl = `${options.adminEndpoint}/catalog/marketing-projections`;
|
|
2436
1627
|
function authHeaders() {
|
|
2437
1628
|
const token = options.getAuthToken?.();
|
|
@@ -2458,7 +1649,7 @@ function useMarketingProjections(options) {
|
|
|
2458
1649
|
throw new MarketingProjectionsApiError(
|
|
2459
1650
|
res.status,
|
|
2460
1651
|
body,
|
|
2461
|
-
|
|
1652
|
+
formatMessage(msg.value.errors.projectionsApi, { status: res.status })
|
|
2462
1653
|
);
|
|
2463
1654
|
}
|
|
2464
1655
|
return body;
|
|
@@ -2485,7 +1676,7 @@ function useMarketingProjections(options) {
|
|
|
2485
1676
|
body: JSON.stringify(data)
|
|
2486
1677
|
});
|
|
2487
1678
|
if (!created) {
|
|
2488
|
-
throw new MarketingProjectionsApiError(0, null, "Create
|
|
1679
|
+
throw new MarketingProjectionsApiError(0, null, "Create returned no body");
|
|
2489
1680
|
}
|
|
2490
1681
|
await load();
|
|
2491
1682
|
return created;
|
|
@@ -2496,7 +1687,7 @@ function useMarketingProjections(options) {
|
|
|
2496
1687
|
body: JSON.stringify(data)
|
|
2497
1688
|
});
|
|
2498
1689
|
if (!updated) {
|
|
2499
|
-
throw new MarketingProjectionsApiError(0, null, "Update
|
|
1690
|
+
throw new MarketingProjectionsApiError(0, null, "Update returned no body");
|
|
2500
1691
|
}
|
|
2501
1692
|
projections.value = projections.value.map((p) => p.id === id ? updated : p);
|
|
2502
1693
|
return updated;
|
|
@@ -2511,8 +1702,8 @@ function useMarketingProjections(options) {
|
|
|
2511
1702
|
return { projections, filter, loading, error, setFilter, load, create, update, remove };
|
|
2512
1703
|
}
|
|
2513
1704
|
|
|
2514
|
-
// src/use-promotions.ts
|
|
2515
|
-
import { ref as
|
|
1705
|
+
// src/vue/use-promotions.ts
|
|
1706
|
+
import { ref as ref19 } from "vue";
|
|
2516
1707
|
var PromotionsApiError = class extends Error {
|
|
2517
1708
|
constructor(status, body, message) {
|
|
2518
1709
|
super(message);
|
|
@@ -2525,15 +1716,16 @@ var PromotionsApiError = class extends Error {
|
|
|
2525
1716
|
};
|
|
2526
1717
|
function usePromotions(options) {
|
|
2527
1718
|
if (!options?.adminEndpoint) {
|
|
2528
|
-
throw new Error("usePromotions: `adminEndpoint`
|
|
1719
|
+
throw new Error("usePromotions: `adminEndpoint` is required.");
|
|
2529
1720
|
}
|
|
2530
1721
|
if (!options?.projectKey) {
|
|
2531
|
-
throw new Error("usePromotions: `projectKey`
|
|
1722
|
+
throw new Error("usePromotions: `projectKey` is required.");
|
|
2532
1723
|
}
|
|
1724
|
+
const msg = useSaMessages("promos");
|
|
2533
1725
|
const http = options.http ?? defaultHttpClient();
|
|
2534
|
-
const promotions =
|
|
2535
|
-
const loading =
|
|
2536
|
-
const error =
|
|
1726
|
+
const promotions = ref19([]);
|
|
1727
|
+
const loading = ref19(false);
|
|
1728
|
+
const error = ref19(null);
|
|
2537
1729
|
const baseUrl = `${options.adminEndpoint}/catalog/promotions`;
|
|
2538
1730
|
const pk = encodeURIComponent(options.projectKey);
|
|
2539
1731
|
function authHeaders() {
|
|
@@ -2552,7 +1744,7 @@ function usePromotions(options) {
|
|
|
2552
1744
|
throw new PromotionsApiError(
|
|
2553
1745
|
res.status,
|
|
2554
1746
|
body,
|
|
2555
|
-
|
|
1747
|
+
formatMessage(msg.value.apiErrorHttpStatus, { status: res.status })
|
|
2556
1748
|
);
|
|
2557
1749
|
}
|
|
2558
1750
|
return body;
|
|
@@ -2574,7 +1766,7 @@ function usePromotions(options) {
|
|
|
2574
1766
|
method: "POST",
|
|
2575
1767
|
body: JSON.stringify(data)
|
|
2576
1768
|
});
|
|
2577
|
-
if (!created) throw new PromotionsApiError(0, null, "Create
|
|
1769
|
+
if (!created) throw new PromotionsApiError(0, null, "Create returned no body");
|
|
2578
1770
|
promotions.value = [...promotions.value, created];
|
|
2579
1771
|
return created;
|
|
2580
1772
|
}
|
|
@@ -2583,7 +1775,7 @@ function usePromotions(options) {
|
|
|
2583
1775
|
method: "PATCH",
|
|
2584
1776
|
body: JSON.stringify(data)
|
|
2585
1777
|
});
|
|
2586
|
-
if (!updated) throw new PromotionsApiError(0, null, "Update
|
|
1778
|
+
if (!updated) throw new PromotionsApiError(0, null, "Update returned no body");
|
|
2587
1779
|
promotions.value = promotions.value.map((p) => p.id === id ? updated : p);
|
|
2588
1780
|
return updated;
|
|
2589
1781
|
}
|
|
@@ -2597,8 +1789,8 @@ function usePromotions(options) {
|
|
|
2597
1789
|
return { promotions, loading, error, load, create, update, remove };
|
|
2598
1790
|
}
|
|
2599
1791
|
|
|
2600
|
-
// src/use-plans.ts
|
|
2601
|
-
import { ref as
|
|
1792
|
+
// src/vue/use-plans.ts
|
|
1793
|
+
import { ref as ref20 } from "vue";
|
|
2602
1794
|
var PlansApiError = class extends Error {
|
|
2603
1795
|
constructor(status, body, message) {
|
|
2604
1796
|
super(message);
|
|
@@ -2612,17 +1804,17 @@ var PlansApiError = class extends Error {
|
|
|
2612
1804
|
function usePlans(options) {
|
|
2613
1805
|
if (!options?.adminEndpoint) {
|
|
2614
1806
|
throw new Error(
|
|
2615
|
-
'usePlans: `adminEndpoint`
|
|
1807
|
+
'usePlans: `adminEndpoint` is required (e.g. "/api/admin" or "/api/v1/admin").'
|
|
2616
1808
|
);
|
|
2617
1809
|
}
|
|
2618
1810
|
if (!options?.projectKey) {
|
|
2619
|
-
throw new Error("usePlans: `projectKey`
|
|
1811
|
+
throw new Error("usePlans: `projectKey` is required.");
|
|
2620
1812
|
}
|
|
2621
1813
|
const http = options.http ?? defaultHttpClient();
|
|
2622
|
-
const plans =
|
|
2623
|
-
const loading =
|
|
2624
|
-
const error =
|
|
2625
|
-
const tenantCountsByPlanKey =
|
|
1814
|
+
const plans = ref20([]);
|
|
1815
|
+
const loading = ref20(false);
|
|
1816
|
+
const error = ref20(null);
|
|
1817
|
+
const tenantCountsByPlanKey = ref20({});
|
|
2626
1818
|
const baseUrl = `${options.adminEndpoint}/catalog/plans`;
|
|
2627
1819
|
function authHeaders() {
|
|
2628
1820
|
const token = options.getAuthToken?.();
|
|
@@ -2644,7 +1836,7 @@ function usePlans(options) {
|
|
|
2644
1836
|
throw new PlansApiError(
|
|
2645
1837
|
res.status,
|
|
2646
1838
|
body,
|
|
2647
|
-
`Plans
|
|
1839
|
+
`Plans API responded with HTTP ${res.status}`
|
|
2648
1840
|
);
|
|
2649
1841
|
}
|
|
2650
1842
|
return body;
|
|
@@ -2678,7 +1870,7 @@ function usePlans(options) {
|
|
|
2678
1870
|
method: "POST",
|
|
2679
1871
|
body: JSON.stringify(data)
|
|
2680
1872
|
});
|
|
2681
|
-
if (!created) throw new PlansApiError(0, null, "Create
|
|
1873
|
+
if (!created) throw new PlansApiError(0, null, "Create returned no body");
|
|
2682
1874
|
plans.value = [...plans.value, created];
|
|
2683
1875
|
return created;
|
|
2684
1876
|
}
|
|
@@ -2687,7 +1879,7 @@ function usePlans(options) {
|
|
|
2687
1879
|
method: "PATCH",
|
|
2688
1880
|
body: JSON.stringify(data)
|
|
2689
1881
|
});
|
|
2690
|
-
if (!updated) throw new PlansApiError(0, null, "Update
|
|
1882
|
+
if (!updated) throw new PlansApiError(0, null, "Update returned no body");
|
|
2691
1883
|
plans.value = plans.value.map((p) => p.id === planId ? updated : p);
|
|
2692
1884
|
return updated;
|
|
2693
1885
|
}
|
|
@@ -2717,15 +1909,15 @@ function usePlans(options) {
|
|
|
2717
1909
|
}
|
|
2718
1910
|
function usePlanVersions(options) {
|
|
2719
1911
|
if (!options?.adminEndpoint) {
|
|
2720
|
-
throw new Error("usePlanVersions: `adminEndpoint`
|
|
1912
|
+
throw new Error("usePlanVersions: `adminEndpoint` is required.");
|
|
2721
1913
|
}
|
|
2722
1914
|
if (!options?.planId) {
|
|
2723
|
-
throw new Error("usePlanVersions: `planId`
|
|
1915
|
+
throw new Error("usePlanVersions: `planId` is required.");
|
|
2724
1916
|
}
|
|
2725
1917
|
const http = options.http ?? defaultHttpClient();
|
|
2726
|
-
const versions =
|
|
2727
|
-
const loading =
|
|
2728
|
-
const error =
|
|
1918
|
+
const versions = ref20([]);
|
|
1919
|
+
const loading = ref20(false);
|
|
1920
|
+
const error = ref20(null);
|
|
2729
1921
|
const planVersionsUrl = `${options.adminEndpoint}/catalog/plans/${options.planId}/versions`;
|
|
2730
1922
|
const versionUrlBase = `${options.adminEndpoint}/catalog/plan-versions`;
|
|
2731
1923
|
function authHeaders() {
|
|
@@ -2748,7 +1940,7 @@ function usePlanVersions(options) {
|
|
|
2748
1940
|
throw new PlansApiError(
|
|
2749
1941
|
res.status,
|
|
2750
1942
|
body,
|
|
2751
|
-
`PlanVersions
|
|
1943
|
+
`PlanVersions API responded with HTTP ${res.status}`
|
|
2752
1944
|
);
|
|
2753
1945
|
}
|
|
2754
1946
|
return body;
|
|
@@ -2770,7 +1962,7 @@ function usePlanVersions(options) {
|
|
|
2770
1962
|
method: "POST",
|
|
2771
1963
|
body: JSON.stringify(data)
|
|
2772
1964
|
});
|
|
2773
|
-
if (!result) throw new PlansApiError(0, null, "CreateDraft
|
|
1965
|
+
if (!result) throw new PlansApiError(0, null, "CreateDraft returned no body");
|
|
2774
1966
|
versions.value = [...versions.value, result.planVersion];
|
|
2775
1967
|
return result;
|
|
2776
1968
|
}
|
|
@@ -2779,7 +1971,7 @@ function usePlanVersions(options) {
|
|
|
2779
1971
|
`${versionUrlBase}/${versionId}`,
|
|
2780
1972
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
2781
1973
|
);
|
|
2782
|
-
if (!result) throw new PlansApiError(0, null, "UpdateDraft
|
|
1974
|
+
if (!result) throw new PlansApiError(0, null, "UpdateDraft returned no body");
|
|
2783
1975
|
versions.value = versions.value.map((v) => v.id === versionId ? result.planVersion : v);
|
|
2784
1976
|
return result;
|
|
2785
1977
|
}
|
|
@@ -2788,7 +1980,7 @@ function usePlanVersions(options) {
|
|
|
2788
1980
|
`${versionUrlBase}/${versionId}/publish`,
|
|
2789
1981
|
{ method: "POST", body: JSON.stringify(opts) }
|
|
2790
1982
|
);
|
|
2791
|
-
if (!result) throw new PlansApiError(0, null, "Publish
|
|
1983
|
+
if (!result) throw new PlansApiError(0, null, "Publish returned no body");
|
|
2792
1984
|
versions.value = versions.value.map((v) => v.id === versionId ? result.planVersion : v);
|
|
2793
1985
|
return result;
|
|
2794
1986
|
}
|
|
@@ -2801,7 +1993,7 @@ function usePlanVersions(options) {
|
|
|
2801
1993
|
`${versionUrlBase}/${versionId}/terminate`,
|
|
2802
1994
|
{ method: "POST", body: JSON.stringify({ endsAt }) }
|
|
2803
1995
|
);
|
|
2804
|
-
if (!updated) throw new PlansApiError(0, null, "Terminate
|
|
1996
|
+
if (!updated) throw new PlansApiError(0, null, "Terminate returned no body");
|
|
2805
1997
|
versions.value = versions.value.map((v) => v.id === versionId ? updated : v);
|
|
2806
1998
|
return updated;
|
|
2807
1999
|
}
|
|
@@ -2821,19 +2013,19 @@ function usePlanVersions(options) {
|
|
|
2821
2013
|
};
|
|
2822
2014
|
}
|
|
2823
2015
|
|
|
2824
|
-
// src/use-live-plan-versions.ts
|
|
2825
|
-
import { ref as
|
|
2016
|
+
// src/vue/use-live-plan-versions.ts
|
|
2017
|
+
import { ref as ref21, watch as watch3 } from "vue";
|
|
2826
2018
|
function useLivePlanVersions(options) {
|
|
2827
2019
|
if (!options?.adminEndpoint) {
|
|
2828
|
-
throw new Error("useLivePlanVersions: `adminEndpoint`
|
|
2020
|
+
throw new Error("useLivePlanVersions: `adminEndpoint` is required.");
|
|
2829
2021
|
}
|
|
2830
2022
|
if (!options?.plans) {
|
|
2831
|
-
throw new Error("useLivePlanVersions: `plans`
|
|
2023
|
+
throw new Error("useLivePlanVersions: `plans` is required.");
|
|
2832
2024
|
}
|
|
2833
2025
|
const http = options.http ?? defaultHttpClient();
|
|
2834
|
-
const livePlanVersions =
|
|
2835
|
-
const loading =
|
|
2836
|
-
const error =
|
|
2026
|
+
const livePlanVersions = ref21({});
|
|
2027
|
+
const loading = ref21(false);
|
|
2028
|
+
const error = ref21(null);
|
|
2837
2029
|
function authHeaders() {
|
|
2838
2030
|
const token = options.getAuthToken?.();
|
|
2839
2031
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
@@ -2844,7 +2036,7 @@ function useLivePlanVersions(options) {
|
|
|
2844
2036
|
headers: { "content-type": "application/json", ...authHeaders() }
|
|
2845
2037
|
});
|
|
2846
2038
|
if (res.status >= 400) {
|
|
2847
|
-
throw new Error(`HTTP ${res.status}
|
|
2039
|
+
throw new Error(`HTTP ${res.status} while loading the versions of plan '${planId}'`);
|
|
2848
2040
|
}
|
|
2849
2041
|
const versions = await res.json().catch(() => null);
|
|
2850
2042
|
if (!versions || versions.length === 0) return null;
|
|
@@ -2861,7 +2053,7 @@ function useLivePlanVersions(options) {
|
|
|
2861
2053
|
return [plan.planKey, live];
|
|
2862
2054
|
} catch (err) {
|
|
2863
2055
|
console.warn(
|
|
2864
|
-
`useLivePlanVersions:
|
|
2056
|
+
`useLivePlanVersions: loading the versions of plan '${plan.planKey}' failed`,
|
|
2865
2057
|
err
|
|
2866
2058
|
);
|
|
2867
2059
|
return [plan.planKey, null];
|
|
@@ -2896,13 +2088,13 @@ function findLatestLive(versions) {
|
|
|
2896
2088
|
})[0];
|
|
2897
2089
|
}
|
|
2898
2090
|
|
|
2899
|
-
// src/use-manifest.ts
|
|
2900
|
-
import { ref as
|
|
2091
|
+
// src/vue/use-manifest.ts
|
|
2092
|
+
import { ref as ref22 } from "vue";
|
|
2901
2093
|
function useManifest(options) {
|
|
2902
2094
|
const loader = new ManifestLoader(options);
|
|
2903
|
-
const manifest =
|
|
2904
|
-
const loading =
|
|
2905
|
-
const error =
|
|
2095
|
+
const manifest = ref22(null);
|
|
2096
|
+
const loading = ref22(false);
|
|
2097
|
+
const error = ref22(null);
|
|
2906
2098
|
async function load() {
|
|
2907
2099
|
loading.value = true;
|
|
2908
2100
|
error.value = null;
|
|
@@ -2927,18 +2119,22 @@ function useManifest(options) {
|
|
|
2927
2119
|
return { manifest, loading, error, load, reload, clearCache };
|
|
2928
2120
|
}
|
|
2929
2121
|
|
|
2930
|
-
// src/use-nav.ts
|
|
2122
|
+
// src/vue/use-nav.ts
|
|
2931
2123
|
import { computed as computed5 } from "vue";
|
|
2932
2124
|
function useNav(manifest, options = {}) {
|
|
2125
|
+
const { locale } = useSuperAdminI18n();
|
|
2126
|
+
const activeLocale = computed5(() => options.locale ?? locale.value);
|
|
2933
2127
|
const routes = computed5(() => {
|
|
2934
2128
|
if (!manifest.value) return [];
|
|
2935
|
-
return buildRoutes(manifest.value, options);
|
|
2129
|
+
return buildRoutes(manifest.value, { ...options, locale: activeLocale.value });
|
|
2936
2130
|
});
|
|
2937
|
-
const sidebar = computed5(
|
|
2131
|
+
const sidebar = computed5(
|
|
2132
|
+
() => buildSidebar(routes.value, defaultSectionOrder(activeLocale.value))
|
|
2133
|
+
);
|
|
2938
2134
|
return { routes, sidebar };
|
|
2939
2135
|
}
|
|
2940
2136
|
|
|
2941
|
-
// src/use-actions.ts
|
|
2137
|
+
// src/vue/use-actions.ts
|
|
2942
2138
|
import { computed as computed6 } from "vue";
|
|
2943
2139
|
function useActions(manifest, actions) {
|
|
2944
2140
|
const registry = computed6(() => {
|
|
@@ -2948,55 +2144,11 @@ function useActions(manifest, actions) {
|
|
|
2948
2144
|
return { registry };
|
|
2949
2145
|
}
|
|
2950
2146
|
|
|
2951
|
-
// src/use-tenant-action-flow.ts
|
|
2147
|
+
// src/vue/use-tenant-action-flow.ts
|
|
2952
2148
|
import { computed as computed7 } from "vue";
|
|
2953
|
-
|
|
2954
|
-
// src/use-super-admin-context.ts
|
|
2955
|
-
import { inject as inject3 } from "vue";
|
|
2956
|
-
function useSuperAdminExtensions() {
|
|
2957
|
-
return inject3(SUPER_ADMIN_EXTENSIONS_KEY, {});
|
|
2958
|
-
}
|
|
2959
|
-
function useSuperAdminActions() {
|
|
2960
|
-
return inject3(SUPER_ADMIN_ACTIONS_KEY, {});
|
|
2961
|
-
}
|
|
2962
|
-
function useSuperAdminBrand() {
|
|
2963
|
-
const brand = inject3(SUPER_ADMIN_BRAND_KEY);
|
|
2964
|
-
if (!brand) {
|
|
2965
|
-
throw new Error(
|
|
2966
|
-
"useSuperAdminBrand(): kein SuperAdmin-Branding im Inject-Scope. Wurde die Komponente innerhalb createSuperAdminApp() gemounted?"
|
|
2967
|
-
);
|
|
2968
|
-
}
|
|
2969
|
-
return brand;
|
|
2970
|
-
}
|
|
2971
|
-
function useSuperAdminEndpoints() {
|
|
2972
|
-
const endpoints = inject3(SUPER_ADMIN_ENDPOINTS_KEY);
|
|
2973
|
-
if (!endpoints) {
|
|
2974
|
-
throw new Error(
|
|
2975
|
-
"useSuperAdminEndpoints(): keine Endpoints im Inject-Scope. Wurde die Komponente innerhalb createSuperAdminApp() gemounted?"
|
|
2976
|
-
);
|
|
2977
|
-
}
|
|
2978
|
-
return endpoints;
|
|
2979
|
-
}
|
|
2980
|
-
function useSuperAdminManifest() {
|
|
2981
|
-
const accessor = inject3(SUPER_ADMIN_MANIFEST_KEY, null);
|
|
2982
|
-
return accessor ? accessor() : null;
|
|
2983
|
-
}
|
|
2984
|
-
function useSuperAdminLoginAdapter() {
|
|
2985
|
-
const adapter = inject3(SUPER_ADMIN_LOGIN_ADAPTER_KEY);
|
|
2986
|
-
if (!adapter) {
|
|
2987
|
-
throw new Error(
|
|
2988
|
-
"useSuperAdminLoginAdapter(): kein Login-Adapter registriert. Reiche ihn via createSuperAdminApp({ loginAdapter }) ein."
|
|
2989
|
-
);
|
|
2990
|
-
}
|
|
2991
|
-
return adapter;
|
|
2992
|
-
}
|
|
2993
|
-
function useSuperAdminHttp() {
|
|
2994
|
-
return inject3(SUPER_ADMIN_HTTP_KEY, defaultHttpClient());
|
|
2995
|
-
}
|
|
2996
|
-
|
|
2997
|
-
// src/use-tenant-action-flow.ts
|
|
2998
2149
|
function useTenantActionFlow(manifest, providers = {}) {
|
|
2999
2150
|
const handlers = useSuperAdminActions();
|
|
2151
|
+
const msg = useSaMessages("tenants");
|
|
3000
2152
|
const registry = computed7(() => {
|
|
3001
2153
|
if (!manifest.value) return null;
|
|
3002
2154
|
return new ActionRegistry(manifest.value, handlers);
|
|
@@ -3018,7 +2170,7 @@ function useTenantActionFlow(manifest, providers = {}) {
|
|
|
3018
2170
|
if (!reg) return [];
|
|
3019
2171
|
return availableActions.value.filter((def) => !providers.visibleForRow || providers.visibleForRow(def, row)).map((def) => ({
|
|
3020
2172
|
def,
|
|
3021
|
-
invoke: (r) => runFlow(def, r ?? row, reg, providers)
|
|
2173
|
+
invoke: (r) => runFlow(def, r ?? row, reg, providers, msg.value.actions.successNotify)
|
|
3022
2174
|
}));
|
|
3023
2175
|
}
|
|
3024
2176
|
return { registry, availableActions, actionsForRow, orphanedDefs };
|
|
@@ -3032,13 +2184,13 @@ function passesStaticGates(def, capabilities, orphanedKeys) {
|
|
|
3032
2184
|
}
|
|
3033
2185
|
return true;
|
|
3034
2186
|
}
|
|
3035
|
-
async function runFlow(def, row, registry, providers) {
|
|
2187
|
+
async function runFlow(def, row, registry, providers, successTemplate) {
|
|
3036
2188
|
let reason = null;
|
|
3037
2189
|
let extras = {};
|
|
3038
2190
|
if (def.confirmType && def.confirmType !== "none") {
|
|
3039
2191
|
if (!providers.confirm) {
|
|
3040
2192
|
throw new Error(
|
|
3041
|
-
`useTenantActionFlow:
|
|
2193
|
+
`useTenantActionFlow: action "${def.id}" requires confirmType="${def.confirmType}", but no \`confirm\` provider was supplied.`
|
|
3042
2194
|
);
|
|
3043
2195
|
}
|
|
3044
2196
|
const c = await providers.confirm(def, { row });
|
|
@@ -3050,7 +2202,7 @@ async function runFlow(def, row, registry, providers) {
|
|
|
3050
2202
|
if (def.requiresMfa) {
|
|
3051
2203
|
if (!providers.mfa) {
|
|
3052
2204
|
throw new Error(
|
|
3053
|
-
`useTenantActionFlow:
|
|
2205
|
+
`useTenantActionFlow: action "${def.id}" requires MFA, but no \`mfa\` provider was supplied.`
|
|
3054
2206
|
);
|
|
3055
2207
|
}
|
|
3056
2208
|
mfaCode = await providers.mfa(def, { row });
|
|
@@ -3066,7 +2218,7 @@ async function runFlow(def, row, registry, providers) {
|
|
|
3066
2218
|
try {
|
|
3067
2219
|
const input = { row, mfaCode, reason, extras };
|
|
3068
2220
|
const result = await resolved.handler(input);
|
|
3069
|
-
providers.notify?.("positive",
|
|
2221
|
+
providers.notify?.("positive", formatMessage(successTemplate, { action: def.label }));
|
|
3070
2222
|
providers.onSuccess?.(def, { row });
|
|
3071
2223
|
return result;
|
|
3072
2224
|
} catch (err) {
|
|
@@ -3076,14 +2228,13 @@ async function runFlow(def, row, registry, providers) {
|
|
|
3076
2228
|
}
|
|
3077
2229
|
}
|
|
3078
2230
|
|
|
3079
|
-
// src/use-platform-tenant-actions.ts
|
|
3080
|
-
import { computed as computed8, ref as
|
|
2231
|
+
// src/vue/use-platform-tenant-actions.ts
|
|
2232
|
+
import { computed as computed8, ref as ref23 } from "vue";
|
|
3081
2233
|
var DEFAULT_VISIBLE_FOR_ROW = (def, row) => {
|
|
3082
2234
|
if (def.actionKey === "tenants.suspend") return row.isActive;
|
|
3083
2235
|
if (def.actionKey === "tenants.reactivate") return !row.isActive;
|
|
3084
2236
|
return true;
|
|
3085
2237
|
};
|
|
3086
|
-
var DEFAULT_MFA_DESCRIPTION = (def, row) => `${def.label} \u2014 Tenant \u201E${row.name}". TOTP-Code aus Authenticator eingeben.`;
|
|
3087
2238
|
var DEFAULT_ICON_FOR_ACTION_KEY = (actionKey) => {
|
|
3088
2239
|
if (actionKey.endsWith(".suspend")) return "block";
|
|
3089
2240
|
if (actionKey.endsWith(".reactivate")) return "play_arrow";
|
|
@@ -3105,11 +2256,16 @@ function defaultToneForActionKey(actionKey) {
|
|
|
3105
2256
|
return DEFAULT_TONE_FOR_ACTION_KEY(actionKey);
|
|
3106
2257
|
}
|
|
3107
2258
|
function usePlatformTenantActions(options) {
|
|
3108
|
-
const
|
|
2259
|
+
const msg = useSaMessages("tenants");
|
|
2260
|
+
const defaultMfaDescription = (def, row) => formatMessage(msg.value.actions.mfaDescription, {
|
|
2261
|
+
action: def.label,
|
|
2262
|
+
tenant: row.name
|
|
2263
|
+
});
|
|
2264
|
+
const mfaDescription = options.mfaDescription ?? defaultMfaDescription;
|
|
3109
2265
|
const iconForActionKey = options.iconForActionKey ?? DEFAULT_ICON_FOR_ACTION_KEY;
|
|
3110
2266
|
const toneForActionKey = options.toneForActionKey ?? DEFAULT_TONE_FOR_ACTION_KEY;
|
|
3111
2267
|
const visibleForRow = options.visibleForRow ?? DEFAULT_VISIBLE_FOR_ROW;
|
|
3112
|
-
const mfa =
|
|
2268
|
+
const mfa = ref23({ show: false, description: "", error: "" });
|
|
3113
2269
|
let pendingMfaResolve = null;
|
|
3114
2270
|
function showMfaDialog(def, ctx) {
|
|
3115
2271
|
return new Promise((resolve) => {
|
|
@@ -3134,7 +2290,7 @@ function usePlatformTenantActions(options) {
|
|
|
3134
2290
|
pendingMfaResolve(null);
|
|
3135
2291
|
}
|
|
3136
2292
|
}
|
|
3137
|
-
const confirmDialog =
|
|
2293
|
+
const confirmDialog = ref23({
|
|
3138
2294
|
show: false,
|
|
3139
2295
|
def: null,
|
|
3140
2296
|
row: null
|
|
@@ -3214,13 +2370,13 @@ function usePlatformTenantActions(options) {
|
|
|
3214
2370
|
};
|
|
3215
2371
|
}
|
|
3216
2372
|
|
|
3217
|
-
// src/use-batch-columns.ts
|
|
3218
|
-
import { ref as
|
|
2373
|
+
// src/vue/use-batch-columns.ts
|
|
2374
|
+
import { ref as ref24, watch as watch4 } from "vue";
|
|
3219
2375
|
function useBatchColumns(manifest, tenantIds, options = {}) {
|
|
3220
2376
|
const fetcher = new BatchColumnFetcher(options);
|
|
3221
|
-
const data =
|
|
3222
|
-
const loading =
|
|
3223
|
-
const error =
|
|
2377
|
+
const data = ref24({});
|
|
2378
|
+
const loading = ref24(false);
|
|
2379
|
+
const error = ref24(null);
|
|
3224
2380
|
async function load() {
|
|
3225
2381
|
if (!manifest.value || tenantIds.value.length === 0) {
|
|
3226
2382
|
data.value = {};
|
|
@@ -3243,253 +2399,7 @@ function useBatchColumns(manifest, tenantIds, options = {}) {
|
|
|
3243
2399
|
return { data, loading, error, reload: load };
|
|
3244
2400
|
}
|
|
3245
2401
|
|
|
3246
|
-
// src/
|
|
3247
|
-
var dec = (s) => Number(s);
|
|
3248
|
-
function extractQuotas(p) {
|
|
3249
|
-
if (p.quotas && Object.keys(p.quotas).length > 0) {
|
|
3250
|
-
return { ...p.quotas };
|
|
3251
|
-
}
|
|
3252
|
-
const out = {};
|
|
3253
|
-
if (typeof p.maxUsers === "number") out.users = p.maxUsers;
|
|
3254
|
-
if (typeof p.maxVehicles === "number") out.vehicles = p.maxVehicles;
|
|
3255
|
-
if (typeof p.maxStorageGb === "number") out.storageGb = p.maxStorageGb;
|
|
3256
|
-
return out;
|
|
3257
|
-
}
|
|
3258
|
-
var sortByPublishedDesc = (rows) => [...rows].sort((a, b) => {
|
|
3259
|
-
const at = a.publishedAt ? Date.parse(a.publishedAt) : Number.POSITIVE_INFINITY;
|
|
3260
|
-
const bt = b.publishedAt ? Date.parse(b.publishedAt) : Number.POSITIVE_INFINITY;
|
|
3261
|
-
if (at !== bt) return bt - at;
|
|
3262
|
-
return b.version - a.version;
|
|
3263
|
-
});
|
|
3264
|
-
function findLiveAt(rows, asOfMs) {
|
|
3265
|
-
const candidates = rows.filter((r) => {
|
|
3266
|
-
if (r.publishedAt === null) return false;
|
|
3267
|
-
if (Date.parse(r.publishedAt) > asOfMs) return false;
|
|
3268
|
-
if (r.supersededAt !== null && Date.parse(r.supersededAt) <= asOfMs) return false;
|
|
3269
|
-
return true;
|
|
3270
|
-
});
|
|
3271
|
-
if (candidates.length === 0) return null;
|
|
3272
|
-
return [...candidates].sort((a, b) => b.version - a.version)[0] ?? null;
|
|
3273
|
-
}
|
|
3274
|
-
function findCurrentLive(rows) {
|
|
3275
|
-
return rows.find((r) => r.publishedAt !== null && r.supersededAt === null) ?? null;
|
|
3276
|
-
}
|
|
3277
|
-
function findDraft(rows) {
|
|
3278
|
-
return rows.find((r) => r.publishedAt === null) ?? null;
|
|
3279
|
-
}
|
|
3280
|
-
function groupBy(rows, keyOf) {
|
|
3281
|
-
const out = /* @__PURE__ */ new Map();
|
|
3282
|
-
for (const r of rows) {
|
|
3283
|
-
const k = keyOf(r);
|
|
3284
|
-
const list = out.get(k) ?? [];
|
|
3285
|
-
list.push(r);
|
|
3286
|
-
out.set(k, list);
|
|
3287
|
-
}
|
|
3288
|
-
return out;
|
|
3289
|
-
}
|
|
3290
|
-
function resolvePlan(source, liveBase) {
|
|
3291
|
-
const quotas = extractQuotas(source);
|
|
3292
|
-
return {
|
|
3293
|
-
source,
|
|
3294
|
-
liveBase,
|
|
3295
|
-
isDraft: source.publishedAt === null,
|
|
3296
|
-
planId: source.planId,
|
|
3297
|
-
features: source.features,
|
|
3298
|
-
quotas,
|
|
3299
|
-
monthlyNet: dec(source.monthlyNet),
|
|
3300
|
-
yearlyNet: dec(source.yearlyNet),
|
|
3301
|
-
marketed: source.marketed,
|
|
3302
|
-
version: source.version,
|
|
3303
|
-
maxUsers: quotas.users,
|
|
3304
|
-
maxVehicles: quotas.vehicles,
|
|
3305
|
-
maxStorageGb: quotas.storageGb
|
|
3306
|
-
};
|
|
3307
|
-
}
|
|
3308
|
-
function dateLabelDe(iso) {
|
|
3309
|
-
return new Date(iso).toLocaleDateString("de-DE", {
|
|
3310
|
-
day: "2-digit",
|
|
3311
|
-
month: "short",
|
|
3312
|
-
year: "numeric"
|
|
3313
|
-
});
|
|
3314
|
-
}
|
|
3315
|
-
function buildDraftsSnapshot(data, sortPlansFn) {
|
|
3316
|
-
const planByKey = groupBy(data.planVersions, (r) => r.planId);
|
|
3317
|
-
const plans = [];
|
|
3318
|
-
for (const [, rows] of planByKey) {
|
|
3319
|
-
const draft = findDraft(rows);
|
|
3320
|
-
const live = findCurrentLive(rows);
|
|
3321
|
-
if (draft) plans.push(resolvePlan(draft, live));
|
|
3322
|
-
else if (live) plans.push(resolvePlan(live, null));
|
|
3323
|
-
}
|
|
3324
|
-
const draftCount = plans.filter((p) => p.isDraft).length;
|
|
3325
|
-
return {
|
|
3326
|
-
id: "drafts",
|
|
3327
|
-
kind: "drafts",
|
|
3328
|
-
status: "DRAFT",
|
|
3329
|
-
label: "Arbeitsstand",
|
|
3330
|
-
title: draftCount === 0 ? "Keine offenen Drafts" : `${draftCount} offene${draftCount === 1 ? "r" : ""} Draft${draftCount === 1 ? "" : "s"} bereit f\xFCr Publish`,
|
|
3331
|
-
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.",
|
|
3332
|
-
asOf: null,
|
|
3333
|
-
createdAt: null,
|
|
3334
|
-
publishedAt: null,
|
|
3335
|
-
authorEmail: null,
|
|
3336
|
-
plans: sortPlansFn(plans),
|
|
3337
|
-
draftCount,
|
|
3338
|
-
regressionCount: 0
|
|
3339
|
-
};
|
|
3340
|
-
}
|
|
3341
|
-
function buildActiveSnapshot(data, sortPlansFn) {
|
|
3342
|
-
const planByKey = groupBy(data.planVersions, (r) => r.planId);
|
|
3343
|
-
const plans = [];
|
|
3344
|
-
for (const [, rows] of planByKey) {
|
|
3345
|
-
const live = findCurrentLive(rows);
|
|
3346
|
-
if (live) plans.push(resolvePlan(live, null));
|
|
3347
|
-
}
|
|
3348
|
-
if (plans.length === 0) return null;
|
|
3349
|
-
const publishedDates = plans.map((p) => p.source.publishedAt).filter((t) => t !== null).sort();
|
|
3350
|
-
const latestPublished = publishedDates[publishedDates.length - 1];
|
|
3351
|
-
return {
|
|
3352
|
-
id: "active",
|
|
3353
|
-
kind: "active",
|
|
3354
|
-
status: "ACTIVE",
|
|
3355
|
-
label: "Aktuell",
|
|
3356
|
-
title: "Live-Catalog",
|
|
3357
|
-
description: "Diese Plan-Versionen sind aktuell f\xFCr neue Onboardings und f\xFCr Renewals nach Stichtag aktiv.",
|
|
3358
|
-
asOf: latestPublished ?? null,
|
|
3359
|
-
createdAt: null,
|
|
3360
|
-
publishedAt: latestPublished ?? null,
|
|
3361
|
-
authorEmail: null,
|
|
3362
|
-
plans: sortPlansFn(plans),
|
|
3363
|
-
draftCount: 0,
|
|
3364
|
-
regressionCount: 0
|
|
3365
|
-
};
|
|
3366
|
-
}
|
|
3367
|
-
function buildHistoricalSnapshots(data, sortPlansFn) {
|
|
3368
|
-
const allPublishedAt = /* @__PURE__ */ new Set();
|
|
3369
|
-
for (const r of data.planVersions) if (r.publishedAt) allPublishedAt.add(r.publishedAt);
|
|
3370
|
-
if (allPublishedAt.size === 0) return [];
|
|
3371
|
-
const planByKey = groupBy(data.planVersions, (r) => r.planId);
|
|
3372
|
-
const liveTimestamps = /* @__PURE__ */ new Set();
|
|
3373
|
-
for (const rows of planByKey.values()) {
|
|
3374
|
-
const live = findCurrentLive(rows);
|
|
3375
|
-
if (live?.publishedAt) liveTimestamps.add(live.publishedAt);
|
|
3376
|
-
}
|
|
3377
|
-
const liveSorted = [...liveTimestamps].sort();
|
|
3378
|
-
const latestLive = liveSorted[liveSorted.length - 1];
|
|
3379
|
-
const sortedDesc = [...allPublishedAt].sort((a, b) => Date.parse(b) - Date.parse(a));
|
|
3380
|
-
const out = [];
|
|
3381
|
-
for (const ts of sortedDesc) {
|
|
3382
|
-
if (latestLive && Date.parse(ts) >= Date.parse(latestLive)) continue;
|
|
3383
|
-
const tMs = Date.parse(ts) + 1;
|
|
3384
|
-
const plans = [];
|
|
3385
|
-
for (const [, rows] of planByKey) {
|
|
3386
|
-
const live = findLiveAt(rows, tMs);
|
|
3387
|
-
if (live) plans.push(resolvePlan(live, null));
|
|
3388
|
-
}
|
|
3389
|
-
if (plans.length === 0) continue;
|
|
3390
|
-
const publishedAtThisEvent = data.planVersions.filter((r) => r.publishedAt === ts);
|
|
3391
|
-
const regressionCount = publishedAtThisEvent.filter((r) => !r.nonRegressive).length;
|
|
3392
|
-
const author = publishedAtThisEvent[0]?.publishedByUserId ?? null;
|
|
3393
|
-
out.push({
|
|
3394
|
-
id: `hist-${ts}`,
|
|
3395
|
-
kind: "historical",
|
|
3396
|
-
status: "ARCHIVED",
|
|
3397
|
-
label: dateLabelDe(ts),
|
|
3398
|
-
title: summarizeEvent(publishedAtThisEvent),
|
|
3399
|
-
description: detailEvent(publishedAtThisEvent),
|
|
3400
|
-
asOf: ts,
|
|
3401
|
-
createdAt: ts,
|
|
3402
|
-
publishedAt: ts,
|
|
3403
|
-
authorEmail: author,
|
|
3404
|
-
plans: sortPlansFn(plans),
|
|
3405
|
-
draftCount: 0,
|
|
3406
|
-
regressionCount
|
|
3407
|
-
});
|
|
3408
|
-
}
|
|
3409
|
-
return out;
|
|
3410
|
-
}
|
|
3411
|
-
function summarizeEvent(rows) {
|
|
3412
|
-
if (rows.length === 0) return "Publish-Event";
|
|
3413
|
-
if (rows.length === 1) {
|
|
3414
|
-
const r = rows[0];
|
|
3415
|
-
return `${r.planId} v${r.version} publiziert`;
|
|
3416
|
-
}
|
|
3417
|
-
return `${rows.length} Versionen publiziert`;
|
|
3418
|
-
}
|
|
3419
|
-
function detailEvent(rows) {
|
|
3420
|
-
if (rows.length === 0) return "";
|
|
3421
|
-
const note = rows[0]?.changeNote ?? "";
|
|
3422
|
-
return note.length > 0 ? note : "Snapshot rekonstruiert aus per-Entity-Versionen.";
|
|
3423
|
-
}
|
|
3424
|
-
function sortPlansBy(order) {
|
|
3425
|
-
if (!order || order.length === 0) {
|
|
3426
|
-
return (rows) => [...rows].sort((a, b) => a.planId.localeCompare(b.planId));
|
|
3427
|
-
}
|
|
3428
|
-
const idx = {};
|
|
3429
|
-
for (let i = 0; i < order.length; i++) idx[order[i]] = i;
|
|
3430
|
-
return (rows) => [...rows].sort((a, b) => {
|
|
3431
|
-
const ai = idx[a.planId] ?? Number.POSITIVE_INFINITY;
|
|
3432
|
-
const bi = idx[b.planId] ?? Number.POSITIVE_INFINITY;
|
|
3433
|
-
if (ai !== bi) return ai - bi;
|
|
3434
|
-
return a.planId.localeCompare(b.planId);
|
|
3435
|
-
});
|
|
3436
|
-
}
|
|
3437
|
-
function buildSnapshots(data, options = {}) {
|
|
3438
|
-
const sortPlansFn = sortPlansBy(options.planSortOrder);
|
|
3439
|
-
const drafts = buildDraftsSnapshot(data, sortPlansFn);
|
|
3440
|
-
const active = buildActiveSnapshot(data, sortPlansFn);
|
|
3441
|
-
const historical = buildHistoricalSnapshots(data, sortPlansFn);
|
|
3442
|
-
const out = [drafts];
|
|
3443
|
-
if (active) out.push(active);
|
|
3444
|
-
out.push(...historical);
|
|
3445
|
-
return out;
|
|
3446
|
-
}
|
|
3447
|
-
function listOpenDrafts(data) {
|
|
3448
|
-
return {
|
|
3449
|
-
plans: sortByPublishedDesc(data.planVersions).filter((r) => r.publishedAt === null)
|
|
3450
|
-
};
|
|
3451
|
-
}
|
|
3452
|
-
|
|
3453
|
-
// src/components/dialogs/types.ts
|
|
3454
|
-
var DEFAULT_PILOT_COPY = {
|
|
3455
|
-
tenantSubtitle: "Stammdaten des Mandanten",
|
|
3456
|
-
tenantNameLabel: "Name des Mandanten",
|
|
3457
|
-
tenantNamePlaceholder: "z. B. Muster GmbH",
|
|
3458
|
-
slugPlaceholder: "muster-mandant",
|
|
3459
|
-
adminEmailPlaceholder: "admin@example.com",
|
|
3460
|
-
notePlaceholder: "z. B. Sales-Pilot \xB7 Demo-Zugang"
|
|
3461
|
-
};
|
|
3462
|
-
|
|
3463
|
-
// src/components/bundle-editor/catalog-i18n.ts
|
|
3464
|
-
var CATALOG_DEFAULT_LOCALE = "de";
|
|
3465
|
-
function localized(i18n, locale, field) {
|
|
3466
|
-
if (locale === CATALOG_DEFAULT_LOCALE) return void 0;
|
|
3467
|
-
const value = i18n?.[locale]?.[field];
|
|
3468
|
-
return value && value.trim().length > 0 ? value : void 0;
|
|
3469
|
-
}
|
|
3470
|
-
function resolveLabel(baseLabel, i18n, locale) {
|
|
3471
|
-
return localized(i18n, locale, "label") ?? (baseLabel || void 0);
|
|
3472
|
-
}
|
|
3473
|
-
function buildFeatureRegistry(featureCatalog, locale) {
|
|
3474
|
-
const registry = {};
|
|
3475
|
-
for (const feature of featureCatalog) {
|
|
3476
|
-
const label = resolveLabel(feature.label, feature.i18n, locale);
|
|
3477
|
-
if (label) registry[feature.featureKey] = { label, core: feature.core };
|
|
3478
|
-
}
|
|
3479
|
-
return registry;
|
|
3480
|
-
}
|
|
3481
|
-
function buildQuotaRegistry(quotaCatalog, locale) {
|
|
3482
|
-
const registry = {};
|
|
3483
|
-
for (const quota of quotaCatalog) {
|
|
3484
|
-
registry[quota.quotaKey] = {
|
|
3485
|
-
label: resolveLabel(quota.label, quota.i18n, locale),
|
|
3486
|
-
unit: localized(quota.i18n, locale, "unit") ?? (quota.unit || void 0)
|
|
3487
|
-
};
|
|
3488
|
-
}
|
|
3489
|
-
return registry;
|
|
3490
|
-
}
|
|
3491
|
-
|
|
3492
|
-
// src/platform-loaders.ts
|
|
2402
|
+
// src/vue/platform-loaders.ts
|
|
3493
2403
|
function resolveEndpoints(endpoints) {
|
|
3494
2404
|
return {
|
|
3495
2405
|
apiBase: endpoints.apiBase,
|
|
@@ -3513,15 +2423,15 @@ function createPlatformLoaders(options) {
|
|
|
3513
2423
|
return { bootLoader, manifestLoader };
|
|
3514
2424
|
}
|
|
3515
2425
|
|
|
3516
|
-
// src/manifest-store-factory.ts
|
|
2426
|
+
// src/vue/manifest-store-factory.ts
|
|
3517
2427
|
import { defineStore } from "pinia";
|
|
3518
|
-
import { ref as
|
|
2428
|
+
import { ref as ref25 } from "vue";
|
|
3519
2429
|
function createManifestStore(options) {
|
|
3520
2430
|
return defineStore(options.id ?? "admin-manifest", () => {
|
|
3521
|
-
const manifest =
|
|
3522
|
-
const loading =
|
|
3523
|
-
const error =
|
|
3524
|
-
const loaded =
|
|
2431
|
+
const manifest = ref25(null);
|
|
2432
|
+
const loading = ref25(false);
|
|
2433
|
+
const error = ref25(null);
|
|
2434
|
+
const loaded = ref25(false);
|
|
3525
2435
|
let inflight = null;
|
|
3526
2436
|
async function load() {
|
|
3527
2437
|
loading.value = true;
|
|
@@ -3559,6 +2469,346 @@ function createManifestStore(options) {
|
|
|
3559
2469
|
return { manifest, loading, error, loaded, ensureLoaded, clearCache, reload };
|
|
3560
2470
|
});
|
|
3561
2471
|
}
|
|
2472
|
+
|
|
2473
|
+
// src/components/bundle-editor/catalog-i18n.ts
|
|
2474
|
+
var CATALOG_DEFAULT_LOCALE = "de";
|
|
2475
|
+
function localized(i18n, locale, field) {
|
|
2476
|
+
if (locale === CATALOG_DEFAULT_LOCALE) return void 0;
|
|
2477
|
+
const value = i18n?.[locale]?.[field];
|
|
2478
|
+
return value && value.trim().length > 0 ? value : void 0;
|
|
2479
|
+
}
|
|
2480
|
+
function resolveLabel(baseLabel, i18n, locale) {
|
|
2481
|
+
return localized(i18n, locale, "label") ?? (baseLabel || void 0);
|
|
2482
|
+
}
|
|
2483
|
+
function buildFeatureRegistry(featureCatalog, locale) {
|
|
2484
|
+
const registry = {};
|
|
2485
|
+
for (const feature of featureCatalog) {
|
|
2486
|
+
const label = resolveLabel(feature.label, feature.i18n, locale);
|
|
2487
|
+
if (label) registry[feature.featureKey] = { label, core: feature.core };
|
|
2488
|
+
}
|
|
2489
|
+
return registry;
|
|
2490
|
+
}
|
|
2491
|
+
function buildQuotaRegistry(quotaCatalog, locale) {
|
|
2492
|
+
const registry = {};
|
|
2493
|
+
for (const quota of quotaCatalog) {
|
|
2494
|
+
registry[quota.quotaKey] = {
|
|
2495
|
+
label: resolveLabel(quota.label, quota.i18n, locale),
|
|
2496
|
+
unit: localized(quota.i18n, locale, "unit") ?? (quota.unit || void 0)
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
return registry;
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// src/pages-tenant/default-i18n.ts
|
|
2503
|
+
var DEFAULT_I18N_DE = {
|
|
2504
|
+
sectionTitle: "Paket & Verbrauch",
|
|
2505
|
+
sectionSubtitle: "Aktuelles Paket, Verbrauch und Bundles.",
|
|
2506
|
+
loading: "Lade \u2026",
|
|
2507
|
+
noSubscription: "Keine Subscription f\xFCr diesen Mandanten gefunden.",
|
|
2508
|
+
activePlan: "Aktuelles Paket",
|
|
2509
|
+
cycleMonthly: "Monatlich",
|
|
2510
|
+
cycleYearly: "J\xE4hrlich",
|
|
2511
|
+
statusActive: "Aktiv",
|
|
2512
|
+
statusTrial: "Testphase",
|
|
2513
|
+
statusPastDue: "Zahlung \xFCberf\xE4llig",
|
|
2514
|
+
statusCanceled: "Gek\xFCndigt",
|
|
2515
|
+
statusPendingSales: "Vertrieb-Kl\xE4rung",
|
|
2516
|
+
trialEndsAt: "Testphase endet am",
|
|
2517
|
+
pilotEndsAt: "Pilot endet am",
|
|
2518
|
+
nextBillingDate: "N\xE4chste Abrechnung",
|
|
2519
|
+
pendingChange: "Ausstehender Plan-Wechsel",
|
|
2520
|
+
changeFromTo: "Wechsel zu",
|
|
2521
|
+
changeEffectiveAt: "wirksam ab",
|
|
2522
|
+
changePlanButton: "Paket wechseln",
|
|
2523
|
+
cancelSubscriptionButton: "Abonnement k\xFCndigen",
|
|
2524
|
+
usageTitle: "Verbrauch",
|
|
2525
|
+
featuresOverviewTitle: "Leistungsumfang",
|
|
2526
|
+
featuresActive: "Enthalten",
|
|
2527
|
+
featuresLocked: "Nicht enthalten",
|
|
2528
|
+
bundlesStoreTitle: "Bundles",
|
|
2529
|
+
bundlesBookedTitle: "Gebuchte Bundles",
|
|
2530
|
+
bundlesAvailableTitle: "Verf\xFCgbare Bundles",
|
|
2531
|
+
bundlesAvailableEmpty: "Aktuell sind keine zus\xE4tzlichen Bundles verf\xFCgbar.",
|
|
2532
|
+
bundlesPerMonth: "netto/Monat",
|
|
2533
|
+
bundleBookAction: "Bundle buchen",
|
|
2534
|
+
bundleBookInProgress: "Buchung l\xE4uft \u2026",
|
|
2535
|
+
bundleCancelAction: "K\xFCndigen",
|
|
2536
|
+
bundleReactivateAction: "Reaktivieren",
|
|
2537
|
+
bundleReactivateConfirmTitle: "Bundle reaktivieren",
|
|
2538
|
+
bundleReactivateConfirmBody: "Die K\xFCndigung wird r\xFCckg\xE4ngig gemacht. Das Bundle bleibt gebucht, l\xE4uft regul\xE4r weiter und wird wieder abgerechnet.",
|
|
2539
|
+
bundleCanceledAt: "Gek\xFCndigt zum",
|
|
2540
|
+
bundleMinimumTermUntil: "Mindestlaufzeit bis",
|
|
2541
|
+
bundleIncludesLabel: "Enth\xE4lt",
|
|
2542
|
+
bundleAlreadyBooked: "Bereits gebucht",
|
|
2543
|
+
bundleIncompatible: "Nicht kompatibel mit aktuellem Paket",
|
|
2544
|
+
bundleMissingRequires: "Ben\xF6tigt",
|
|
2545
|
+
bundlePreviewAddTitle: "Bundle buchen",
|
|
2546
|
+
bundlePreviewCancelTitle: "Bundle k\xFCndigen",
|
|
2547
|
+
bundlePreviewLoading: "Vorschau wird berechnet \u2026",
|
|
2548
|
+
bundlePreviewProrationTitle: "Anteilige Abrechnung",
|
|
2549
|
+
bundlePreviewProratedNow: "Heute anteilig f\xE4llig",
|
|
2550
|
+
bundlePreviewProrationDays: "Tage",
|
|
2551
|
+
bundlePreviewNextPeriod: "Regul\xE4r ab n\xE4chster Periode",
|
|
2552
|
+
bundlePreviewTrialNote: "W\xE4hrend der Testphase f\xE4llt nichts an \u2014 die Abrechnung beginnt mit der ersten bezahlten Periode.",
|
|
2553
|
+
bundlePreviewNoPrice: "F\xFCr den aktuellen Abrechnungszyklus ist kein Listenpreis gepflegt.",
|
|
2554
|
+
bundlePreviewMinimumTermLabel: "Mindestlaufzeit",
|
|
2555
|
+
bundlePreviewMinimumTermMonths: "Monate, bis",
|
|
2556
|
+
bundlePreviewMinimumTermNone: "Keine Mindestlaufzeit",
|
|
2557
|
+
bundlePreviewRedundantTitle: "Bereits enthaltene Features (w\xFCrden doppelt bezahlt)",
|
|
2558
|
+
bundlePreviewRedundantCoveredByPlan: "bereits im Plan",
|
|
2559
|
+
bundlePreviewRedundantCoveredByBundle: "bereits im Bundle",
|
|
2560
|
+
bundlePreviewMissingRequiresTitle: "Fehlende Voraussetzungen",
|
|
2561
|
+
bundlePreviewBlockersTitle: "Buchung nicht m\xF6glich",
|
|
2562
|
+
bundlePreviewWarningsTitle: "Hinweise",
|
|
2563
|
+
bundlePreviewEffectiveAt: "K\xFCndigung wirksam zum",
|
|
2564
|
+
bundlePreviewSavings: "Ersparnis pro Periode ab Wirksamkeit",
|
|
2565
|
+
bundlePreviewConfirmAdd: "Kostenpflichtig buchen",
|
|
2566
|
+
bundlePreviewConfirmCancel: "K\xFCndigung best\xE4tigen",
|
|
2567
|
+
bundlePreviewInProgress: "Wird ausgef\xFChrt \u2026",
|
|
2568
|
+
bundlePreviewClose: "Abbrechen",
|
|
2569
|
+
pendingVersionTitle: "Anstehende Plan-\xC4nderung",
|
|
2570
|
+
pendingVersionChipNonRegressive: "Verbessernd",
|
|
2571
|
+
pendingVersionChipRegressive: "Best\xE4tigung erforderlich",
|
|
2572
|
+
pendingVersionEffectiveAt: "Wirksam ab",
|
|
2573
|
+
pendingVersionAcceptAction: "\xC4nderungen akzeptieren",
|
|
2574
|
+
pendingVersionAcceptInProgress: "Akzeptiere \u2026",
|
|
2575
|
+
pendingVersionAcceptedAt: "Akzeptiert am",
|
|
2576
|
+
wizardTitle: "Paket wechseln",
|
|
2577
|
+
wizardClose: "Schlie\xDFen",
|
|
2578
|
+
wizardCurrent: "Aktuell",
|
|
2579
|
+
wizardBadgeCurrent: "aktiv",
|
|
2580
|
+
wizardBadgePopular: "beliebt",
|
|
2581
|
+
wizardPriceUnitMonthly: "netto/Monat",
|
|
2582
|
+
wizardPriceUnitYearly: "netto/Jahr",
|
|
2583
|
+
wizardPriceOnRequest: "auf Anfrage",
|
|
2584
|
+
wizardStepChoose: "Paket w\xE4hlen",
|
|
2585
|
+
wizardStepChooseIntro: "W\xE4hle dein Ziel-Paket und den Abrechnungszyklus. Im n\xE4chsten Schritt zeigen wir Verbrauchs-Check und Feature-Diff.",
|
|
2586
|
+
wizardStepPreview: "Vorschau",
|
|
2587
|
+
wizardStepConfirm: "Best\xE4tigen",
|
|
2588
|
+
wizardNext: "Weiter",
|
|
2589
|
+
wizardBack: "Zur\xFCck",
|
|
2590
|
+
wizardPreviewLoading: "Vorschau wird berechnet \u2026",
|
|
2591
|
+
wizardEffectiveAtLabel: "Wirksam ab",
|
|
2592
|
+
wizardEffectiveImmediate: "Wirksam sofort",
|
|
2593
|
+
wizardProrationTitle: "Anteilige Abrechnung",
|
|
2594
|
+
wizardProrationLine: "Mehrbetrag bis Periodenende:",
|
|
2595
|
+
wizardProrationDays: "Tage",
|
|
2596
|
+
wizardLimitsTitle: "Limit-Vergleich",
|
|
2597
|
+
wizardLimitsUsed: "Verbrauch",
|
|
2598
|
+
wizardLimitsCurrent: "Aktuell",
|
|
2599
|
+
wizardLimitsTarget: "Ziel",
|
|
2600
|
+
wizardFeaturesGained: "Neu freigeschaltet",
|
|
2601
|
+
wizardFeaturesLost: "Wegfallende Features",
|
|
2602
|
+
wizardBlockersTitle: "Verhinderungs-Gr\xFCnde",
|
|
2603
|
+
wizardConfirmImmediate: "Der Wechsel wird sofort wirksam.",
|
|
2604
|
+
wizardConfirmScheduled: "Der Wechsel wird wirksam zum",
|
|
2605
|
+
wizardConfirmAction: "Wechsel best\xE4tigen",
|
|
2606
|
+
wizardConfirmInProgress: "Wechsel l\xE4uft \u2026",
|
|
2607
|
+
wizardConfirmPriceTitle: "Preis\xFCbersicht",
|
|
2608
|
+
wizardConfirmProratedNow: "Heute anteilig f\xE4llig",
|
|
2609
|
+
wizardConfirmRecurringNext: "Regul\xE4r ab n\xE4chster Periode",
|
|
2610
|
+
wizardConfirmRecurringFrom: "Regul\xE4r f\xE4llig ab",
|
|
2611
|
+
wizardConfirmPerCycleMonthly: "pro Monat",
|
|
2612
|
+
wizardConfirmPerCycleYearly: "pro Jahr",
|
|
2613
|
+
wizardConfirmTrialNote: "W\xE4hrend der Testphase f\xE4llt nichts an.",
|
|
2614
|
+
wizardConfirmRecurringTrialEnd: "Regul\xE4r ab Ende der Testphase",
|
|
2615
|
+
wizardChangeTypeUpgrade: "Upgrade",
|
|
2616
|
+
wizardChangeTypeDowngrade: "Downgrade",
|
|
2617
|
+
wizardChangeTypeCycle: "Zyklus-Wechsel",
|
|
2618
|
+
wizardChangeTypeNoop: "Keine \xC4nderung",
|
|
2619
|
+
packageSnapshotTitle: "Gebuchtes Paket (Snapshot)",
|
|
2620
|
+
packageSnapshotSubtitle: "Schreibgesch\xFCtzte Kopie des Pakets, wie es beim Abschluss vermarktet wurde.",
|
|
2621
|
+
packageSnapshotCapturedAt: "Erfasst am",
|
|
2622
|
+
packageSnapshotOfferRef: "Angebots-Referenz",
|
|
2623
|
+
packageSnapshotPlanLabel: "Plan",
|
|
2624
|
+
packageSnapshotPlanVersionLabel: "Plan-Version",
|
|
2625
|
+
packageSnapshotCycleLabel: "Abrechnungszyklus",
|
|
2626
|
+
packageSnapshotBundlesLabel: "Enthaltene Bundles",
|
|
2627
|
+
packageSnapshotBundlesEmpty: "Keine Bundles im Paket.",
|
|
2628
|
+
packageSnapshotPriceMonthly: "Preis monatlich",
|
|
2629
|
+
packageSnapshotPriceYearly: "Preis j\xE4hrlich",
|
|
2630
|
+
packageSnapshotPriceTotal: "Gesamtpreis",
|
|
2631
|
+
packageSnapshotNone: "Diese Subscription wurde nicht \xFCber ein Webseiten-Angebot abgeschlossen.",
|
|
2632
|
+
packageSnapshotShowRaw: "Rohdaten anzeigen",
|
|
2633
|
+
packageSnapshotHideRaw: "Rohdaten ausblenden",
|
|
2634
|
+
errorLabel: "Fehler",
|
|
2635
|
+
myBundlesTitle: "Meine Bundles",
|
|
2636
|
+
myBundlesSubtitle: "Eigenst\xE4ndig gebuchte Add-On-Pakete zu deinem Plan. Mindestlaufzeit + K\xFCndigungs- Termin pro Bundle.",
|
|
2637
|
+
myBundlesEmptyPrefix: "Du hast noch kein Bundle gebucht. \xDCber",
|
|
2638
|
+
myBundlesEmptySuffix: "kannst du dein Paket um zus\xE4tzliche Features & Quotas erweitern.",
|
|
2639
|
+
myBundlesBookedSince: "Gebucht seit",
|
|
2640
|
+
myBundlesCanceledAt: "Gek\xFCndigt am",
|
|
2641
|
+
myBundlesRunsUntil: "l\xE4uft bis",
|
|
2642
|
+
myBundlesCancelInProgress: "K\xFCndige \u2026",
|
|
2643
|
+
myBundlesBookInProgress: "Buche \u2026",
|
|
2644
|
+
myBundlesCancelConfirm: "Bundle wirklich k\xFCndigen? Die K\xFCndigung wird zum n\xE4chstm\xF6glichen Termin wirksam.",
|
|
2645
|
+
myBundlesStatusCanceledPending: "K\xFCndigung wirksam ab \u2026",
|
|
2646
|
+
myBundlesStatusEnded: "Beendet",
|
|
2647
|
+
myBundlesAddBundleLabel: "Bundle",
|
|
2648
|
+
myBundlesAddSelectPlaceholder: "\u2014 bitte w\xE4hlen \u2014",
|
|
2649
|
+
myBundlesPricePerMonthShort: "\u20AC / Mo",
|
|
2650
|
+
myBundlesHiddenIncompatible: "weitere Bundle(s) ausgeblendet \u2014 nicht kompatibel mit Plan",
|
|
2651
|
+
myBundlesBundleVersionIdLabel: "BundleVersion-ID",
|
|
2652
|
+
myBundlesBundleVersionIdPlaceholder: "UUID der gew\xFCnschten Bundle-Version",
|
|
2653
|
+
myBundlesMinimumTermLabel: "Mindestlaufzeit (Monate, optional)",
|
|
2654
|
+
myBundlesMinimumTermPlaceholder: "Default: 12"
|
|
2655
|
+
};
|
|
2656
|
+
var DEFAULT_I18N_EN = {
|
|
2657
|
+
sectionTitle: "Plan & usage",
|
|
2658
|
+
sectionSubtitle: "Current plan, usage and bundles.",
|
|
2659
|
+
loading: "Loading \u2026",
|
|
2660
|
+
noSubscription: "No subscription found for this tenant.",
|
|
2661
|
+
activePlan: "Current plan",
|
|
2662
|
+
cycleMonthly: "Monthly",
|
|
2663
|
+
cycleYearly: "Yearly",
|
|
2664
|
+
statusActive: "Active",
|
|
2665
|
+
statusTrial: "Trial",
|
|
2666
|
+
statusPastDue: "Payment overdue",
|
|
2667
|
+
statusCanceled: "Canceled",
|
|
2668
|
+
statusPendingSales: "Sales review",
|
|
2669
|
+
trialEndsAt: "Trial ends on",
|
|
2670
|
+
pilotEndsAt: "Pilot ends on",
|
|
2671
|
+
nextBillingDate: "Next billing date",
|
|
2672
|
+
pendingChange: "Pending plan change",
|
|
2673
|
+
changeFromTo: "Change to",
|
|
2674
|
+
changeEffectiveAt: "effective as of",
|
|
2675
|
+
changePlanButton: "Change plan",
|
|
2676
|
+
cancelSubscriptionButton: "Cancel subscription",
|
|
2677
|
+
usageTitle: "Usage",
|
|
2678
|
+
featuresOverviewTitle: "Features",
|
|
2679
|
+
featuresActive: "Included",
|
|
2680
|
+
featuresLocked: "Not included",
|
|
2681
|
+
bundlesStoreTitle: "Bundles",
|
|
2682
|
+
bundlesBookedTitle: "Booked bundles",
|
|
2683
|
+
bundlesAvailableTitle: "Available bundles",
|
|
2684
|
+
bundlesAvailableEmpty: "There are currently no additional bundles available.",
|
|
2685
|
+
bundlesPerMonth: "net/month",
|
|
2686
|
+
bundleBookAction: "Book bundle",
|
|
2687
|
+
bundleBookInProgress: "Booking \u2026",
|
|
2688
|
+
bundleCancelAction: "Cancel",
|
|
2689
|
+
bundleReactivateAction: "Reactivate",
|
|
2690
|
+
bundleReactivateConfirmTitle: "Reactivate bundle",
|
|
2691
|
+
bundleReactivateConfirmBody: "The cancellation will be reverted. The bundle stays booked, continues to run normally and will be billed again.",
|
|
2692
|
+
bundleCanceledAt: "Canceled effective",
|
|
2693
|
+
bundleMinimumTermUntil: "Minimum term until",
|
|
2694
|
+
bundleIncludesLabel: "Includes",
|
|
2695
|
+
bundleAlreadyBooked: "Already booked",
|
|
2696
|
+
bundleIncompatible: "Not compatible with the current plan",
|
|
2697
|
+
bundleMissingRequires: "Requires",
|
|
2698
|
+
bundlePreviewAddTitle: "Book bundle",
|
|
2699
|
+
bundlePreviewCancelTitle: "Cancel bundle",
|
|
2700
|
+
bundlePreviewLoading: "Calculating preview \u2026",
|
|
2701
|
+
bundlePreviewProrationTitle: "Prorated billing",
|
|
2702
|
+
bundlePreviewProratedNow: "Prorated amount due today",
|
|
2703
|
+
bundlePreviewProrationDays: "days",
|
|
2704
|
+
bundlePreviewNextPeriod: "Regular price from the next period",
|
|
2705
|
+
bundlePreviewTrialNote: "Nothing is charged during the trial \u2014 billing starts with the first paid period.",
|
|
2706
|
+
bundlePreviewNoPrice: "No list price is configured for the current billing cycle.",
|
|
2707
|
+
bundlePreviewMinimumTermLabel: "Minimum term",
|
|
2708
|
+
bundlePreviewMinimumTermMonths: "months, until",
|
|
2709
|
+
bundlePreviewMinimumTermNone: "No minimum term",
|
|
2710
|
+
bundlePreviewRedundantTitle: "Features already included (would be paid for twice)",
|
|
2711
|
+
bundlePreviewRedundantCoveredByPlan: "already in the plan",
|
|
2712
|
+
bundlePreviewRedundantCoveredByBundle: "already in bundle",
|
|
2713
|
+
bundlePreviewMissingRequiresTitle: "Missing prerequisites",
|
|
2714
|
+
bundlePreviewBlockersTitle: "Booking not possible",
|
|
2715
|
+
bundlePreviewWarningsTitle: "Notes",
|
|
2716
|
+
bundlePreviewEffectiveAt: "Cancellation effective as of",
|
|
2717
|
+
bundlePreviewSavings: "Savings per period once effective",
|
|
2718
|
+
bundlePreviewConfirmAdd: "Book (chargeable)",
|
|
2719
|
+
bundlePreviewConfirmCancel: "Confirm cancellation",
|
|
2720
|
+
bundlePreviewInProgress: "Processing \u2026",
|
|
2721
|
+
bundlePreviewClose: "Cancel",
|
|
2722
|
+
pendingVersionTitle: "Upcoming plan change",
|
|
2723
|
+
pendingVersionChipNonRegressive: "Improvement",
|
|
2724
|
+
pendingVersionChipRegressive: "Confirmation required",
|
|
2725
|
+
pendingVersionEffectiveAt: "Effective as of",
|
|
2726
|
+
pendingVersionAcceptAction: "Accept changes",
|
|
2727
|
+
pendingVersionAcceptInProgress: "Accepting \u2026",
|
|
2728
|
+
pendingVersionAcceptedAt: "Accepted on",
|
|
2729
|
+
wizardTitle: "Change plan",
|
|
2730
|
+
wizardClose: "Close",
|
|
2731
|
+
wizardCurrent: "Current",
|
|
2732
|
+
wizardBadgeCurrent: "active",
|
|
2733
|
+
wizardBadgePopular: "popular",
|
|
2734
|
+
wizardPriceUnitMonthly: "net/month",
|
|
2735
|
+
wizardPriceUnitYearly: "net/year",
|
|
2736
|
+
wizardPriceOnRequest: "on request",
|
|
2737
|
+
wizardStepChoose: "Choose plan",
|
|
2738
|
+
wizardStepChooseIntro: "Choose your target plan and the billing cycle. In the next step we show the usage check and the feature diff.",
|
|
2739
|
+
wizardStepPreview: "Preview",
|
|
2740
|
+
wizardStepConfirm: "Confirm",
|
|
2741
|
+
wizardNext: "Next",
|
|
2742
|
+
wizardBack: "Back",
|
|
2743
|
+
wizardPreviewLoading: "Calculating preview \u2026",
|
|
2744
|
+
wizardEffectiveAtLabel: "Effective as of",
|
|
2745
|
+
wizardEffectiveImmediate: "Effective immediately",
|
|
2746
|
+
wizardProrationTitle: "Prorated billing",
|
|
2747
|
+
wizardProrationLine: "Additional amount until the end of the period:",
|
|
2748
|
+
wizardProrationDays: "days",
|
|
2749
|
+
wizardLimitsTitle: "Limit comparison",
|
|
2750
|
+
wizardLimitsUsed: "Usage",
|
|
2751
|
+
wizardLimitsCurrent: "Current",
|
|
2752
|
+
wizardLimitsTarget: "Target",
|
|
2753
|
+
wizardFeaturesGained: "Newly unlocked",
|
|
2754
|
+
wizardFeaturesLost: "Features you lose",
|
|
2755
|
+
wizardBlockersTitle: "Blockers",
|
|
2756
|
+
wizardConfirmImmediate: "The change takes effect immediately.",
|
|
2757
|
+
wizardConfirmScheduled: "The change takes effect on",
|
|
2758
|
+
wizardConfirmAction: "Confirm change",
|
|
2759
|
+
wizardConfirmInProgress: "Changing \u2026",
|
|
2760
|
+
wizardConfirmPriceTitle: "Price overview",
|
|
2761
|
+
wizardConfirmProratedNow: "Prorated amount due today",
|
|
2762
|
+
wizardConfirmRecurringNext: "Regular price from the next period",
|
|
2763
|
+
wizardConfirmRecurringFrom: "Regular price due from",
|
|
2764
|
+
wizardConfirmPerCycleMonthly: "per month",
|
|
2765
|
+
wizardConfirmPerCycleYearly: "per year",
|
|
2766
|
+
wizardConfirmTrialNote: "Nothing is charged during the trial.",
|
|
2767
|
+
wizardConfirmRecurringTrialEnd: "Regular price from the end of the trial",
|
|
2768
|
+
wizardChangeTypeUpgrade: "Upgrade",
|
|
2769
|
+
wizardChangeTypeDowngrade: "Downgrade",
|
|
2770
|
+
wizardChangeTypeCycle: "Cycle change",
|
|
2771
|
+
wizardChangeTypeNoop: "No change",
|
|
2772
|
+
packageSnapshotTitle: "Booked plan (snapshot)",
|
|
2773
|
+
packageSnapshotSubtitle: "Read-only copy of the plan as it was marketed at the time of purchase.",
|
|
2774
|
+
packageSnapshotCapturedAt: "Captured on",
|
|
2775
|
+
packageSnapshotOfferRef: "Offer reference",
|
|
2776
|
+
packageSnapshotPlanLabel: "Plan",
|
|
2777
|
+
packageSnapshotPlanVersionLabel: "Plan version",
|
|
2778
|
+
packageSnapshotCycleLabel: "Billing cycle",
|
|
2779
|
+
packageSnapshotBundlesLabel: "Included bundles",
|
|
2780
|
+
packageSnapshotBundlesEmpty: "No bundles in the plan.",
|
|
2781
|
+
packageSnapshotPriceMonthly: "Monthly price",
|
|
2782
|
+
packageSnapshotPriceYearly: "Yearly price",
|
|
2783
|
+
packageSnapshotPriceTotal: "Total price",
|
|
2784
|
+
packageSnapshotNone: "This subscription was not created through a website offer.",
|
|
2785
|
+
packageSnapshotShowRaw: "Show raw data",
|
|
2786
|
+
packageSnapshotHideRaw: "Hide raw data",
|
|
2787
|
+
errorLabel: "Error",
|
|
2788
|
+
myBundlesTitle: "My bundles",
|
|
2789
|
+
myBundlesSubtitle: "Add-on packages booked independently on top of your plan. Minimum term + cancellation date per bundle.",
|
|
2790
|
+
myBundlesEmptyPrefix: "You have not booked a bundle yet. Use",
|
|
2791
|
+
myBundlesEmptySuffix: "to extend your plan with additional features & quotas.",
|
|
2792
|
+
myBundlesBookedSince: "Booked since",
|
|
2793
|
+
myBundlesCanceledAt: "Canceled on",
|
|
2794
|
+
myBundlesRunsUntil: "runs until",
|
|
2795
|
+
myBundlesCancelInProgress: "Canceling \u2026",
|
|
2796
|
+
myBundlesBookInProgress: "Booking \u2026",
|
|
2797
|
+
myBundlesCancelConfirm: "Really cancel this bundle? The cancellation takes effect on the next possible date.",
|
|
2798
|
+
myBundlesStatusCanceledPending: "Cancellation effective as of \u2026",
|
|
2799
|
+
myBundlesStatusEnded: "Ended",
|
|
2800
|
+
myBundlesAddBundleLabel: "Bundle",
|
|
2801
|
+
myBundlesAddSelectPlaceholder: "\u2014 please select \u2014",
|
|
2802
|
+
myBundlesPricePerMonthShort: "\u20AC / mo",
|
|
2803
|
+
myBundlesHiddenIncompatible: "more bundle(s) hidden \u2014 not compatible with plan",
|
|
2804
|
+
myBundlesBundleVersionIdLabel: "BundleVersion ID",
|
|
2805
|
+
myBundlesBundleVersionIdPlaceholder: "UUID of the desired bundle version",
|
|
2806
|
+
myBundlesMinimumTermLabel: "Minimum term (months, optional)",
|
|
2807
|
+
myBundlesMinimumTermPlaceholder: "Default: 12"
|
|
2808
|
+
};
|
|
2809
|
+
function defaultTenantPlanSectionI18n(locale) {
|
|
2810
|
+
return locale === "en" ? DEFAULT_I18N_EN : DEFAULT_I18N_DE;
|
|
2811
|
+
}
|
|
3562
2812
|
export {
|
|
3563
2813
|
ADMIN_UI_VERSION,
|
|
3564
2814
|
ActionDefNotInManifestError,
|
|
@@ -3568,12 +2818,11 @@ export {
|
|
|
3568
2818
|
BootLoadError,
|
|
3569
2819
|
BootLoader,
|
|
3570
2820
|
BundlesApiError,
|
|
3571
|
-
BusinessTypesApiError,
|
|
3572
2821
|
CATALOG_DEFAULT_LOCALE,
|
|
3573
2822
|
CatalogEntriesApiError,
|
|
3574
2823
|
DEFAULT_I18N_DE,
|
|
3575
|
-
|
|
3576
|
-
|
|
2824
|
+
DEFAULT_I18N_EN,
|
|
2825
|
+
DEFAULT_SA_LOCALE,
|
|
3577
2826
|
DEFAULT_STANDARD_PAGE_ROUTES,
|
|
3578
2827
|
DEFAULT_YEARLY_FACTOR,
|
|
3579
2828
|
DiscoveryLoadError,
|
|
@@ -3587,13 +2836,18 @@ export {
|
|
|
3587
2836
|
PlansApiError,
|
|
3588
2837
|
ProjectPageHost,
|
|
3589
2838
|
PromotionsApiError,
|
|
2839
|
+
SA_INTL_LOCALES,
|
|
2840
|
+
SA_LOCALES,
|
|
2841
|
+
SA_MESSAGES,
|
|
3590
2842
|
SUPER_ADMIN_ACTIONS_KEY,
|
|
3591
2843
|
SUPER_ADMIN_BRAND_KEY,
|
|
3592
2844
|
SUPER_ADMIN_ENDPOINTS_KEY,
|
|
3593
2845
|
SUPER_ADMIN_EXTENSIONS_KEY,
|
|
3594
2846
|
SUPER_ADMIN_HTTP_KEY,
|
|
2847
|
+
SUPER_ADMIN_I18N_KEY,
|
|
3595
2848
|
SUPER_ADMIN_LOGIN_ADAPTER_KEY,
|
|
3596
2849
|
SUPER_ADMIN_MANIFEST_KEY,
|
|
2850
|
+
SUPER_ADMIN_NOTIFY_KEY,
|
|
3597
2851
|
TenantSubscriptionBundlesApiError,
|
|
3598
2852
|
buildFeatureRegistry,
|
|
3599
2853
|
buildFeatureRouterGuard,
|
|
@@ -3606,16 +2860,23 @@ export {
|
|
|
3606
2860
|
createManifestStore,
|
|
3607
2861
|
createPlatformLoaders,
|
|
3608
2862
|
createProjectPageHostRoute,
|
|
3609
|
-
|
|
2863
|
+
createSuperAdminI18n,
|
|
3610
2864
|
defaultHttpClient,
|
|
3611
2865
|
defaultIconForActionKey,
|
|
3612
2866
|
defaultKvStore,
|
|
2867
|
+
defaultSectionOrder,
|
|
2868
|
+
defaultTenantPlanSectionI18n,
|
|
3613
2869
|
defaultToneForActionKey,
|
|
2870
|
+
defineMessages,
|
|
2871
|
+
formatCurrency,
|
|
2872
|
+
formatMessage,
|
|
3614
2873
|
getJson,
|
|
3615
2874
|
listOpenDrafts,
|
|
2875
|
+
mergeMessages,
|
|
3616
2876
|
postJson,
|
|
3617
2877
|
provideEntitlement,
|
|
3618
2878
|
resolveExtension,
|
|
2879
|
+
resolveMessages,
|
|
3619
2880
|
trimTrailingSlashes,
|
|
3620
2881
|
useActions,
|
|
3621
2882
|
useApiList,
|
|
@@ -3625,8 +2886,6 @@ export {
|
|
|
3625
2886
|
useBundleVersions,
|
|
3626
2887
|
useBundleVersionsMap,
|
|
3627
2888
|
useBundles,
|
|
3628
|
-
useBusinessTypeVersions,
|
|
3629
|
-
useBusinessTypes,
|
|
3630
2889
|
useCatalogEntries,
|
|
3631
2890
|
useDiscovery,
|
|
3632
2891
|
useEntitlement,
|
|
@@ -3642,12 +2901,14 @@ export {
|
|
|
3642
2901
|
usePlatformTenantActions,
|
|
3643
2902
|
usePromotions,
|
|
3644
2903
|
usePublicBoot,
|
|
2904
|
+
useSaMessages,
|
|
3645
2905
|
useSubscriptionDraft,
|
|
3646
2906
|
useSuperAdminActions,
|
|
3647
2907
|
useSuperAdminBrand,
|
|
3648
2908
|
useSuperAdminEndpoints,
|
|
3649
2909
|
useSuperAdminExtensions,
|
|
3650
2910
|
useSuperAdminHttp,
|
|
2911
|
+
useSuperAdminI18n,
|
|
3651
2912
|
useSuperAdminLoginAdapter,
|
|
3652
2913
|
useSuperAdminManifest,
|
|
3653
2914
|
useTenantActionFlow,
|