@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
|
@@ -9,11 +9,10 @@
|
|
|
9
9
|
// not required at all). The manifest is Discovery, not security — the server
|
|
10
10
|
// enforces the routes independently.
|
|
11
11
|
|
|
12
|
-
import type {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from '@saasicat/types';
|
|
12
|
+
import type { AdminManifest, StandardPageDef, StandardPageKey } from '@saasicat/types';
|
|
13
|
+
|
|
14
|
+
import { DEFAULT_SA_LOCALE, type SaLocale } from './i18n/locale.js';
|
|
15
|
+
import { navMessages } from './i18n/messages/nav.js';
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Default routes for the platform standard pages. Consumers may override this
|
|
@@ -32,7 +31,6 @@ export const DEFAULT_STANDARD_PAGE_ROUTES: Record<StandardPageKey, string> = {
|
|
|
32
31
|
pilots: '/admin/pilots',
|
|
33
32
|
discovery: '/admin/discovery',
|
|
34
33
|
bundles: '/admin/bundles',
|
|
35
|
-
businessTypes: '/admin/business-types',
|
|
36
34
|
marketingCatalog: '/admin/marketing-catalog',
|
|
37
35
|
platformEmail: '/admin/platform-email',
|
|
38
36
|
platformEmailHistory: '/admin/platform-email-history',
|
|
@@ -59,13 +57,19 @@ export interface BuildRouteEntry {
|
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
export interface NavBuilderOptions {
|
|
60
|
+
/**
|
|
61
|
+
* UI locale for the default labels and section names, default `'de'`.
|
|
62
|
+
* Pass the same locale to `buildSidebar()`'s `sectionOrder` (via
|
|
63
|
+
* `defaultSectionOrder(locale)`) — section names are compared as strings.
|
|
64
|
+
*/
|
|
65
|
+
locale?: SaLocale;
|
|
62
66
|
/**
|
|
63
67
|
* Optional: overrides the default routes for certain standard pages.
|
|
64
68
|
* Consumers set this if they want an alternative URL structure.
|
|
65
69
|
*/
|
|
66
70
|
standardPageRoutes?: Partial<Record<StandardPageKey, string>>;
|
|
67
71
|
/**
|
|
68
|
-
*
|
|
72
|
+
* Per-page label overrides layered over the locale defaults.
|
|
69
73
|
*/
|
|
70
74
|
standardPageLabels?: Partial<Record<StandardPageKey, string>>;
|
|
71
75
|
/** Default icons for standard pages. */
|
|
@@ -82,24 +86,6 @@ export interface NavBuilderOptions {
|
|
|
82
86
|
availableExtensions?: Set<string>;
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
const DEFAULT_LABELS: Record<StandardPageKey, string> = {
|
|
86
|
-
dashboard: 'Dashboard',
|
|
87
|
-
tenants: 'Mandanten',
|
|
88
|
-
subscriptions: 'Abonnements',
|
|
89
|
-
promoCodes: 'Promo-Codes',
|
|
90
|
-
plans: 'Pläne & Versionen',
|
|
91
|
-
planVersions: 'Plan-Versionen',
|
|
92
|
-
audit: 'Audit-Log',
|
|
93
|
-
users: 'Benutzer',
|
|
94
|
-
pilots: 'Piloten',
|
|
95
|
-
discovery: 'Discovery',
|
|
96
|
-
bundles: 'Bundles',
|
|
97
|
-
businessTypes: 'Geschäftstypen',
|
|
98
|
-
marketingCatalog: 'Marketing-Catalog',
|
|
99
|
-
platformEmail: 'Plattform-E-Mail',
|
|
100
|
-
platformEmailHistory: 'E-Mail-Verlauf',
|
|
101
|
-
};
|
|
102
|
-
|
|
103
89
|
const DEFAULT_ICONS: Record<StandardPageKey, string> = {
|
|
104
90
|
dashboard: 'dashboard',
|
|
105
91
|
tenants: 'business',
|
|
@@ -112,36 +98,39 @@ const DEFAULT_ICONS: Record<StandardPageKey, string> = {
|
|
|
112
98
|
pilots: 'flight_takeoff',
|
|
113
99
|
discovery: 'travel_explore',
|
|
114
100
|
bundles: 'inventory_2',
|
|
115
|
-
businessTypes: 'category',
|
|
116
101
|
marketingCatalog: 'campaign',
|
|
117
102
|
platformEmail: 'mail',
|
|
118
103
|
platformEmailHistory: 'mark_email_read',
|
|
119
104
|
};
|
|
120
105
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
106
|
+
type SectionKey = keyof (typeof navMessages)['de']['sections'];
|
|
107
|
+
|
|
108
|
+
const PAGE_SECTIONS: Partial<Record<StandardPageKey, SectionKey>> = {
|
|
109
|
+
dashboard: 'overview',
|
|
110
|
+
discovery: 'catalog',
|
|
111
|
+
bundles: 'catalog',
|
|
112
|
+
plans: 'catalog',
|
|
113
|
+
planVersions: 'catalog',
|
|
114
|
+
marketingCatalog: 'catalog',
|
|
115
|
+
promoCodes: 'catalog',
|
|
116
|
+
tenants: 'customers',
|
|
117
|
+
subscriptions: 'customers',
|
|
118
|
+
users: 'customers',
|
|
119
|
+
pilots: 'customers',
|
|
120
|
+
audit: 'system',
|
|
121
|
+
platformEmail: 'system',
|
|
122
|
+
platformEmailHistory: 'system',
|
|
137
123
|
};
|
|
138
124
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
]
|
|
125
|
+
/**
|
|
126
|
+
* Localized default section names in drawer order (Übersicht → Produktkatalog
|
|
127
|
+
* → Kunden → System). Pass the result to `buildSidebar()` when building routes
|
|
128
|
+
* with a non-default locale.
|
|
129
|
+
*/
|
|
130
|
+
export function defaultSectionOrder(locale: SaLocale = DEFAULT_SA_LOCALE): readonly string[] {
|
|
131
|
+
const sections = navMessages[locale].sections;
|
|
132
|
+
return [sections.overview, sections.catalog, sections.customers, sections.system];
|
|
133
|
+
}
|
|
145
134
|
|
|
146
135
|
/**
|
|
147
136
|
* Returns the list of all routes defined by the current manifest —
|
|
@@ -159,10 +148,14 @@ export function buildRoutes(
|
|
|
159
148
|
|
|
160
149
|
// StandardPages
|
|
161
150
|
const standard = manifest.navigation?.standardPages ?? {};
|
|
151
|
+
const nav = navMessages[options.locale ?? DEFAULT_SA_LOCALE];
|
|
152
|
+
const defaultNavSections: Partial<Record<StandardPageKey, string>> = Object.fromEntries(
|
|
153
|
+
Object.entries(PAGE_SECTIONS).map(([page, section]) => [page, nav.sections[section]]),
|
|
154
|
+
);
|
|
162
155
|
const overrideRoutes = { ...DEFAULT_STANDARD_PAGE_ROUTES, ...options.standardPageRoutes };
|
|
163
|
-
const overrideLabels = { ...
|
|
156
|
+
const overrideLabels = { ...nav.pages, ...options.standardPageLabels };
|
|
164
157
|
const overrideIcons = { ...DEFAULT_ICONS, ...options.standardPageIcons };
|
|
165
|
-
const overrideNavSection = { ...
|
|
158
|
+
const overrideNavSection = { ...defaultNavSections, ...options.standardPageNavSection };
|
|
166
159
|
|
|
167
160
|
for (const key of Object.keys(standard) as StandardPageKey[]) {
|
|
168
161
|
const def = standard[key] as StandardPageDef | undefined;
|
|
@@ -230,7 +223,7 @@ export interface SidebarSection {
|
|
|
230
223
|
*/
|
|
231
224
|
export function buildSidebar(
|
|
232
225
|
routes: BuildRouteEntry[],
|
|
233
|
-
sectionOrder: readonly string[] =
|
|
226
|
+
sectionOrder: readonly string[] = defaultSectionOrder(),
|
|
234
227
|
): SidebarSection[] {
|
|
235
228
|
const bySection = new Map<string | null, SidebarItem[]>();
|
|
236
229
|
for (const r of routes) {
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
import type { PlanVersionRow } from '@saasicat/types';
|
|
19
19
|
|
|
20
|
+
import { formatMessage } from './i18n/format.js';
|
|
21
|
+
import { DEFAULT_SA_LOCALE, SA_INTL_LOCALES, type SaLocale } from './i18n/locale.js';
|
|
22
|
+
import { planVersionsMessages } from './i18n/messages/plan-versions.js';
|
|
23
|
+
|
|
20
24
|
export type SnapshotKind = 'drafts' | 'active' | 'historical';
|
|
21
25
|
|
|
22
26
|
export interface CatalogSnapshot<P extends PlanVersionRow = PlanVersionRow> {
|
|
@@ -78,6 +82,8 @@ export interface BuildSnapshotsOptions {
|
|
|
78
82
|
* end up sorted alphabetically at the back.
|
|
79
83
|
*/
|
|
80
84
|
planSortOrder?: readonly string[];
|
|
85
|
+
/** UI locale for the generated snapshot labels/titles/descriptions. */
|
|
86
|
+
locale?: SaLocale;
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
@@ -168,8 +174,16 @@ function resolvePlan<P extends PlanVersionRow>(source: P, liveBase: P | null): R
|
|
|
168
174
|
|
|
169
175
|
// ─── Snapshot constructors ───────────────────────────────────────────────
|
|
170
176
|
|
|
171
|
-
|
|
172
|
-
|
|
177
|
+
type CatalogMessages = (typeof planVersionsMessages)['de']['catalog'];
|
|
178
|
+
|
|
179
|
+
function draftsTitle(draftCount: number, msg: CatalogMessages): string {
|
|
180
|
+
if (draftCount === 0) return msg.draftsTitleEmpty;
|
|
181
|
+
const template = draftCount === 1 ? msg.draftsTitleOne : msg.draftsTitleMany;
|
|
182
|
+
return formatMessage(template, { count: draftCount });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function dateLabel(iso: string, locale: SaLocale): string {
|
|
186
|
+
return new Date(iso).toLocaleDateString(SA_INTL_LOCALES[locale], {
|
|
173
187
|
day: '2-digit',
|
|
174
188
|
month: 'short',
|
|
175
189
|
year: 'numeric',
|
|
@@ -179,7 +193,9 @@ function dateLabelDe(iso: string): string {
|
|
|
179
193
|
function buildDraftsSnapshot<P extends PlanVersionRow>(
|
|
180
194
|
data: RawCatalogData<P>,
|
|
181
195
|
sortPlansFn: (rows: ResolvedPlan<P>[]) => ResolvedPlan<P>[],
|
|
196
|
+
locale: SaLocale,
|
|
182
197
|
): CatalogSnapshot<P> {
|
|
198
|
+
const msg = planVersionsMessages[locale].catalog;
|
|
183
199
|
const planByKey = groupBy(data.planVersions, (r) => r.planId);
|
|
184
200
|
|
|
185
201
|
const plans: ResolvedPlan<P>[] = [];
|
|
@@ -196,15 +212,9 @@ function buildDraftsSnapshot<P extends PlanVersionRow>(
|
|
|
196
212
|
id: 'drafts',
|
|
197
213
|
kind: 'drafts',
|
|
198
214
|
status: 'DRAFT',
|
|
199
|
-
label:
|
|
200
|
-
title:
|
|
201
|
-
|
|
202
|
-
? 'Keine offenen Drafts'
|
|
203
|
-
: `${draftCount} offene${draftCount === 1 ? 'r' : ''} Draft${draftCount === 1 ? '' : 's'} bereit für Publish`,
|
|
204
|
-
description:
|
|
205
|
-
draftCount === 0
|
|
206
|
-
? 'Lege einen Draft an, um Limits, Preise oder Features zu ändern. Drafts sind ohne MFA editierbar und werden erst beim Publish wirksam.'
|
|
207
|
-
: 'Diese Tabelle zeigt, wie der Catalog nach Publish aller Drafts aussehen würde. Bestehende Mandanten bleiben auf der vorherigen Version, bis sie aktiv migriert werden.',
|
|
215
|
+
label: msg.draftsLabel,
|
|
216
|
+
title: draftsTitle(draftCount, msg),
|
|
217
|
+
description: draftCount === 0 ? msg.draftsDescriptionEmpty : msg.draftsDescription,
|
|
208
218
|
asOf: null,
|
|
209
219
|
createdAt: null,
|
|
210
220
|
publishedAt: null,
|
|
@@ -218,7 +228,9 @@ function buildDraftsSnapshot<P extends PlanVersionRow>(
|
|
|
218
228
|
function buildActiveSnapshot<P extends PlanVersionRow>(
|
|
219
229
|
data: RawCatalogData<P>,
|
|
220
230
|
sortPlansFn: (rows: ResolvedPlan<P>[]) => ResolvedPlan<P>[],
|
|
231
|
+
locale: SaLocale,
|
|
221
232
|
): CatalogSnapshot<P> | null {
|
|
233
|
+
const msg = planVersionsMessages[locale].catalog;
|
|
222
234
|
const planByKey = groupBy(data.planVersions, (r) => r.planId);
|
|
223
235
|
|
|
224
236
|
const plans: ResolvedPlan<P>[] = [];
|
|
@@ -238,10 +250,9 @@ function buildActiveSnapshot<P extends PlanVersionRow>(
|
|
|
238
250
|
id: 'active',
|
|
239
251
|
kind: 'active',
|
|
240
252
|
status: 'ACTIVE',
|
|
241
|
-
label:
|
|
242
|
-
title:
|
|
243
|
-
description:
|
|
244
|
-
'Diese Plan-Versionen sind aktuell für neue Onboardings und für Renewals nach Stichtag aktiv.',
|
|
253
|
+
label: msg.activeLabel,
|
|
254
|
+
title: msg.activeTitle,
|
|
255
|
+
description: msg.activeDescription,
|
|
245
256
|
asOf: latestPublished ?? null,
|
|
246
257
|
createdAt: null,
|
|
247
258
|
publishedAt: latestPublished ?? null,
|
|
@@ -255,7 +266,9 @@ function buildActiveSnapshot<P extends PlanVersionRow>(
|
|
|
255
266
|
function buildHistoricalSnapshots<P extends PlanVersionRow>(
|
|
256
267
|
data: RawCatalogData<P>,
|
|
257
268
|
sortPlansFn: (rows: ResolvedPlan<P>[]) => ResolvedPlan<P>[],
|
|
269
|
+
locale: SaLocale,
|
|
258
270
|
): CatalogSnapshot<P>[] {
|
|
271
|
+
const msg = planVersionsMessages[locale].catalog;
|
|
259
272
|
const allPublishedAt = new Set<string>();
|
|
260
273
|
for (const r of data.planVersions) if (r.publishedAt) allPublishedAt.add(r.publishedAt);
|
|
261
274
|
if (allPublishedAt.size === 0) return [];
|
|
@@ -293,9 +306,9 @@ function buildHistoricalSnapshots<P extends PlanVersionRow>(
|
|
|
293
306
|
id: `hist-${ts}`,
|
|
294
307
|
kind: 'historical',
|
|
295
308
|
status: 'ARCHIVED',
|
|
296
|
-
label:
|
|
297
|
-
title: summarizeEvent(publishedAtThisEvent),
|
|
298
|
-
description: detailEvent(publishedAtThisEvent),
|
|
309
|
+
label: dateLabel(ts, locale),
|
|
310
|
+
title: summarizeEvent(publishedAtThisEvent, msg),
|
|
311
|
+
description: detailEvent(publishedAtThisEvent, msg),
|
|
299
312
|
asOf: ts,
|
|
300
313
|
createdAt: ts,
|
|
301
314
|
publishedAt: ts,
|
|
@@ -308,19 +321,19 @@ function buildHistoricalSnapshots<P extends PlanVersionRow>(
|
|
|
308
321
|
return out;
|
|
309
322
|
}
|
|
310
323
|
|
|
311
|
-
function summarizeEvent(rows: PlanVersionRow[]): string {
|
|
312
|
-
if (rows.length === 0) return
|
|
324
|
+
function summarizeEvent(rows: PlanVersionRow[], msg: CatalogMessages): string {
|
|
325
|
+
if (rows.length === 0) return msg.publishEvent;
|
|
313
326
|
if (rows.length === 1) {
|
|
314
327
|
const r = rows[0]!;
|
|
315
|
-
return
|
|
328
|
+
return formatMessage(msg.publishEventSingle, { planId: r.planId, version: r.version });
|
|
316
329
|
}
|
|
317
|
-
return
|
|
330
|
+
return formatMessage(msg.publishEventMultiple, { count: rows.length });
|
|
318
331
|
}
|
|
319
332
|
|
|
320
|
-
function detailEvent(rows: PlanVersionRow[]): string {
|
|
333
|
+
function detailEvent(rows: PlanVersionRow[], msg: CatalogMessages): string {
|
|
321
334
|
if (rows.length === 0) return '';
|
|
322
335
|
const note = rows[0]?.changeNote ?? '';
|
|
323
|
-
return note.length > 0 ? note :
|
|
336
|
+
return note.length > 0 ? note : msg.reconstructedSnapshot;
|
|
324
337
|
}
|
|
325
338
|
|
|
326
339
|
// ─── Sorting ─────────────────────────────────────────────────────────────
|
|
@@ -348,10 +361,11 @@ export function buildSnapshots<P extends PlanVersionRow>(
|
|
|
348
361
|
data: RawCatalogData<P>,
|
|
349
362
|
options: BuildSnapshotsOptions = {},
|
|
350
363
|
): CatalogSnapshot<P>[] {
|
|
364
|
+
const locale = options.locale ?? DEFAULT_SA_LOCALE;
|
|
351
365
|
const sortPlansFn = sortPlansBy<P>(options.planSortOrder);
|
|
352
|
-
const drafts = buildDraftsSnapshot(data, sortPlansFn);
|
|
353
|
-
const active = buildActiveSnapshot(data, sortPlansFn);
|
|
354
|
-
const historical = buildHistoricalSnapshots(data, sortPlansFn);
|
|
366
|
+
const drafts = buildDraftsSnapshot(data, sortPlansFn, locale);
|
|
367
|
+
const active = buildActiveSnapshot(data, sortPlansFn, locale);
|
|
368
|
+
const historical = buildHistoricalSnapshots(data, sortPlansFn, locale);
|
|
355
369
|
|
|
356
370
|
const out: CatalogSnapshot<P>[] = [drafts];
|
|
357
371
|
if (active) out.push(active);
|
|
@@ -359,9 +373,7 @@ export function buildSnapshots<P extends PlanVersionRow>(
|
|
|
359
373
|
return out;
|
|
360
374
|
}
|
|
361
375
|
|
|
362
|
-
export function listOpenDrafts<P extends PlanVersionRow>(
|
|
363
|
-
data: RawCatalogData<P>,
|
|
364
|
-
): { plans: P[] } {
|
|
376
|
+
export function listOpenDrafts<P extends PlanVersionRow>(data: RawCatalogData<P>): { plans: P[] } {
|
|
365
377
|
return {
|
|
366
378
|
plans: sortByPublishedDesc(data.planVersions).filter((r) => r.publishedAt === null),
|
|
367
379
|
};
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
<q-dialog v-model="open" persistent>
|
|
3
3
|
<q-card style="min-width: 560px; max-width: 96vw">
|
|
4
4
|
<q-card-section>
|
|
5
|
-
<div class="text-h6">
|
|
5
|
+
<div class="text-h6">{{ msg.publishDialog.title }}</div>
|
|
6
6
|
<p class="text-caption text-grey-7">
|
|
7
|
-
|
|
7
|
+
{{ msg.publishDialog.bundleLabel }} <code>{{ bundleKey }}</code> · v{{
|
|
8
|
+
draft.version
|
|
9
|
+
}}
|
|
8
10
|
</p>
|
|
9
11
|
</q-card-section>
|
|
10
12
|
|
|
11
13
|
<q-card-section v-if="!previewLoaded" class="bvpd__loading">
|
|
12
14
|
<q-spinner color="primary" size="32px" />
|
|
13
|
-
<span class="q-ml-sm">
|
|
15
|
+
<span class="q-ml-sm">{{ msg.publishDialog.loadingDiff }}</span>
|
|
14
16
|
</q-card-section>
|
|
15
17
|
|
|
16
18
|
<q-card-section v-else class="bvpd__body">
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
<template #avatar>
|
|
20
22
|
<q-icon name="warning" color="warning" />
|
|
21
23
|
</template>
|
|
22
|
-
<strong>{{
|
|
24
|
+
<strong>{{ strictWarningsText }}</strong>
|
|
23
25
|
<ul class="bvpd__list">
|
|
24
26
|
<li v-for="(w, i) in warnings" :key="i">
|
|
25
27
|
<code>{{ w.code }}</code>
|
|
@@ -32,14 +34,14 @@
|
|
|
32
34
|
</q-banner>
|
|
33
35
|
|
|
34
36
|
<div class="bvpd__validity">
|
|
35
|
-
<div class="bvpd__label">
|
|
37
|
+
<div class="bvpd__label">{{ common.validity }}</div>
|
|
36
38
|
<div class="bvpd__validity-grid">
|
|
37
39
|
<label class="bvpd__field">
|
|
38
|
-
<span>
|
|
40
|
+
<span>{{ msg.fields.validFrom }}</span>
|
|
39
41
|
<input v-model="validFromInput" class="bvpd__input" type="date" />
|
|
40
42
|
</label>
|
|
41
43
|
<label class="bvpd__field">
|
|
42
|
-
<span>
|
|
44
|
+
<span>{{ msg.fields.validUntil }}</span>
|
|
43
45
|
<input v-model="validUntilInput" class="bvpd__input" type="date" />
|
|
44
46
|
</label>
|
|
45
47
|
</div>
|
|
@@ -47,22 +49,19 @@
|
|
|
47
49
|
</div>
|
|
48
50
|
|
|
49
51
|
<div>
|
|
50
|
-
<q-toggle
|
|
51
|
-
v-model="allowZeroPrice"
|
|
52
|
-
label="Preis 0,00 bewusst zulassen (kostenloses Bundle)"
|
|
53
|
-
/>
|
|
52
|
+
<q-toggle v-model="allowZeroPrice" :label="msg.publishDialog.allowZeroPrice" />
|
|
54
53
|
<p class="bvpd__label" style="margin-top: 6px">
|
|
55
|
-
|
|
54
|
+
{{ msg.publishDialog.allowZeroPriceHint }}
|
|
56
55
|
</p>
|
|
57
56
|
</div>
|
|
58
57
|
|
|
59
58
|
<!-- Diff against the previous version -->
|
|
60
59
|
<div>
|
|
61
60
|
<div class="bvpd__label">
|
|
62
|
-
|
|
61
|
+
{{ msg.publishDialog.changesVs }}
|
|
63
62
|
<span v-if="previousVersion"> v{{ previousVersion }} </span>
|
|
64
63
|
<span v-else class="text-grey-7">
|
|
65
|
-
|
|
64
|
+
{{ msg.publishDialog.noPrevious }}
|
|
66
65
|
</span>
|
|
67
66
|
</div>
|
|
68
67
|
<q-banner
|
|
@@ -74,7 +73,7 @@
|
|
|
74
73
|
<template #avatar>
|
|
75
74
|
<q-icon name="info" color="grey-7" />
|
|
76
75
|
</template>
|
|
77
|
-
|
|
76
|
+
{{ msg.publishDialog.noChanges }}
|
|
78
77
|
</q-banner>
|
|
79
78
|
<q-list v-else-if="changes.length > 0" bordered separator>
|
|
80
79
|
<q-item v-for="(c, i) in changes" :key="i">
|
|
@@ -106,14 +105,13 @@
|
|
|
106
105
|
<template #avatar>
|
|
107
106
|
<q-icon name="error" color="negative" />
|
|
108
107
|
</template>
|
|
109
|
-
<strong>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<code>force</code>-Confirm.
|
|
108
|
+
<strong>{{ msg.publishDialog.regressionTitle }}</strong>
|
|
109
|
+
{{ msg.publishDialog.regressionBody }}
|
|
110
|
+
<code>force</code>{{ msg.publishDialog.regressionBodySuffix }}
|
|
113
111
|
<template #action>
|
|
114
112
|
<q-toggle
|
|
115
113
|
v-model="forceRegressive"
|
|
116
|
-
label="
|
|
114
|
+
:label="msg.publishDialog.forceRegressive"
|
|
117
115
|
color="negative"
|
|
118
116
|
/>
|
|
119
117
|
</template>
|
|
@@ -121,11 +119,15 @@
|
|
|
121
119
|
</q-card-section>
|
|
122
120
|
|
|
123
121
|
<q-card-actions align="right">
|
|
124
|
-
<q-btn flat label="
|
|
122
|
+
<q-btn flat :label="common.cancel" @click="close" />
|
|
125
123
|
<q-btn
|
|
126
124
|
unelevated
|
|
127
125
|
:color="isRegression ? 'negative' : 'positive'"
|
|
128
|
-
:label="
|
|
126
|
+
:label="
|
|
127
|
+
isRegression
|
|
128
|
+
? msg.publishDialog.confirmRegressive
|
|
129
|
+
: msg.publishDialog.confirm
|
|
130
|
+
"
|
|
129
131
|
:loading="submitting"
|
|
130
132
|
:disable="!canSubmit"
|
|
131
133
|
@click="submit"
|
|
@@ -144,6 +146,9 @@ import type {
|
|
|
144
146
|
VersionChange,
|
|
145
147
|
} from '@saasicat/types';
|
|
146
148
|
|
|
149
|
+
import { formatMessage } from '../client/i18n/format.js';
|
|
150
|
+
import { useSaMessages } from '../vue/use-super-admin-i18n.js';
|
|
151
|
+
|
|
147
152
|
// BundleVersionPublishDialog — confirm modal for publishing a draft version.
|
|
148
153
|
// Shows the diff against the previous version (publishedChanges) and blocks
|
|
149
154
|
// regressive versions without a forceRegressive confirm.
|
|
@@ -178,6 +183,9 @@ const emit = defineEmits<{
|
|
|
178
183
|
(e: 'submitted', result: BundleVersionMutationResult): void;
|
|
179
184
|
}>();
|
|
180
185
|
|
|
186
|
+
const msg = useSaMessages('bundles');
|
|
187
|
+
const common = useSaMessages('common');
|
|
188
|
+
|
|
181
189
|
const open = computed({
|
|
182
190
|
get: () => props.modelValue,
|
|
183
191
|
set: (v) => emit('update:modelValue', v),
|
|
@@ -194,10 +202,13 @@ const validUntilInput = ref('');
|
|
|
194
202
|
|
|
195
203
|
const previousVersion = computed(() => props.previous?.version ?? null);
|
|
196
204
|
const isRegression = computed(() => previewLoaded.value && !nonRegressive.value);
|
|
205
|
+
const strictWarningsText = computed(() =>
|
|
206
|
+
formatMessage(msg.value.publishDialog.strictWarnings, { count: props.warnings.length }),
|
|
207
|
+
);
|
|
197
208
|
const validityError = computed<string | null>(() => {
|
|
198
|
-
if (!validFromInput.value) return
|
|
209
|
+
if (!validFromInput.value) return msg.value.validation.validFromRequired;
|
|
199
210
|
if (validUntilInput.value && validUntilInput.value <= validFromInput.value) {
|
|
200
|
-
return
|
|
211
|
+
return msg.value.validation.validUntilAfterValidFrom;
|
|
201
212
|
}
|
|
202
213
|
return null;
|
|
203
214
|
});
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
// instead of returning `null`. (Strict mode comes later; currently default = lenient.)
|
|
36
36
|
|
|
37
37
|
import { computed } from 'vue';
|
|
38
|
-
import { useInjectedEntitlement } from '../entitlement-provider.js';
|
|
38
|
+
import { useInjectedEntitlement } from '../vue/entitlement-provider.js';
|
|
39
39
|
|
|
40
40
|
interface Props {
|
|
41
41
|
/** A single FeatureKey or several as a logical OR. */
|