@saasicat/ui-vue 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +47 -0
- package/dist/index.cjs +3776 -0
- package/dist/index.d.cts +2395 -0
- package/dist/index.d.ts +2395 -0
- package/dist/index.js +3659 -0
- package/dist/testing-e2e/admin-pages-suite.cjs +134 -0
- package/dist/testing-e2e/admin-pages-suite.d.cts +37 -0
- package/dist/testing-e2e/admin-pages-suite.d.ts +37 -0
- package/dist/testing-e2e/admin-pages-suite.js +109 -0
- package/package.json +87 -0
- package/src/action-registry.ts +110 -0
- package/src/batch-column-fetcher.ts +152 -0
- package/src/boot-loader.ts +60 -0
- package/src/components/BundleVersionPublishDialog.vue +359 -0
- package/src/components/BusinessTypeVersionEditorDialog.vue +417 -0
- package/src/components/BusinessTypeVersionPublishDialog.vue +258 -0
- package/src/components/FeatureGate.vue +56 -0
- package/src/components/KpiCard.vue +53 -0
- package/src/components/KvBlock.vue +36 -0
- package/src/components/MarketingPromotionsTab.vue +861 -0
- package/src/components/MfaPromptDialog.vue +104 -0
- package/src/components/TenantActionConfirmDialog.vue +252 -0
- package/src/components/VersionDiffPreview.vue +150 -0
- package/src/components/bundle-editor/BundleCreatePanel.vue +681 -0
- package/src/components/bundle-editor/BundleFeaturesEditor.vue +215 -0
- package/src/components/bundle-editor/BundlePlanCompatPicker.vue +316 -0
- package/src/components/bundle-editor/BundleQuotasEditor.vue +219 -0
- package/src/components/bundle-editor/BundleStatusBanner.vue +189 -0
- package/src/components/bundle-editor/BundleVersionInlineEditor.vue +659 -0
- package/src/components/bundle-editor/BundleVersionStrip.vue +207 -0
- package/src/components/bundle-editor/bundle-version-status.ts +164 -0
- package/src/components/bundle-editor/catalog-i18n.ts +85 -0
- package/src/components/dialogs/PilotCreateDialog.vue +780 -0
- package/src/components/dialogs/PilotEditDialog.vue +548 -0
- package/src/components/dialogs/PromoCodeCreateDialog.vue +737 -0
- package/src/components/dialogs/PromoCodeEditDialog.vue +859 -0
- package/src/components/dialogs/types.ts +126 -0
- package/src/components/plan/PlanCycleToggle.vue +99 -0
- package/src/components/plan/PlanGrid.vue +211 -0
- package/src/components/plan/PriceSummary.vue +293 -0
- package/src/components/plan/PromoCodeInput.vue +192 -0
- package/src/components/plan/PublicBundleGrid.vue +287 -0
- package/src/components/plan-cockpit/PlanCockpit.vue +993 -0
- package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +65 -0
- package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +91 -0
- package/src/components/plan-cockpit/PlanCockpitHeader.vue +98 -0
- package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +99 -0
- package/src/components/plan-cockpit/PlanCockpitKpis.vue +57 -0
- package/src/components/plan-cockpit/PlanCockpitVersions.vue +226 -0
- package/src/components/plan-cockpit/types.ts +72 -0
- package/src/components/plan-create-dialog/PlanCreateDialog.vue +508 -0
- package/src/components/plan-detail/PlanAuditLog.vue +59 -0
- package/src/components/plan-detail/PlanDetail.vue +1195 -0
- package/src/components/plan-detail/PlanDetailHeader.vue +185 -0
- package/src/components/plan-detail/PlanDetailKpis.vue +57 -0
- package/src/components/plan-detail/PlanTerminateDialog.vue +76 -0
- package/src/components/plan-detail/PlanVersionDiffPanel.vue +149 -0
- package/src/components/plan-detail/PlanVersionsPanel.vue +255 -0
- package/src/components/plan-detail/types.ts +59 -0
- package/src/components/plan-list/PlanList.vue +1067 -0
- package/src/components/plan-matrix/PlanMatrix.vue +1113 -0
- package/src/components/plan-review/PlanReview.vue +742 -0
- package/src/components/plan-version-editor/PlanCatalogPreview.vue +184 -0
- package/src/components/plan-version-editor/PlanComponentPool.vue +272 -0
- package/src/components/plan-version-editor/PlanVersionBasket.vue +281 -0
- package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +84 -0
- package/src/components/plan-version-editor/PlanVersionEditor.vue +1728 -0
- package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +94 -0
- package/src/components/plan-version-editor/types.ts +96 -0
- package/src/components/wizard-stepper/WizardStepper.vue +107 -0
- package/src/create-admin-routes.ts +68 -0
- package/src/create-super-admin-app.ts +345 -0
- package/src/entitlement-provider.ts +36 -0
- package/src/feature-router-guard.ts +77 -0
- package/src/http-json.ts +62 -0
- package/src/index.ts +98 -0
- package/src/manifest-loader.ts +139 -0
- package/src/manifest-store-factory.ts +114 -0
- package/src/nav-builder.ts +274 -0
- package/src/pages-standard/AdminLayout.vue +411 -0
- package/src/pages-standard/AdminManifestErrorPage.vue +79 -0
- package/src/pages-standard/AuditPage.vue +223 -0
- package/src/pages-standard/BundlesPage.vue +910 -0
- package/src/pages-standard/BusinessTypesPage.vue +662 -0
- package/src/pages-standard/DashboardPage.vue +595 -0
- package/src/pages-standard/DiscoveryPage.vue +663 -0
- package/src/pages-standard/EmailHistoryPage.vue +576 -0
- package/src/pages-standard/MarketingCatalogPage.vue +1771 -0
- package/src/pages-standard/PilotsPage.vue +577 -0
- package/src/pages-standard/PlanVersionsPage.vue +216 -0
- package/src/pages-standard/PlansPage.vue +1222 -0
- package/src/pages-standard/PlatformEmailPage.vue +434 -0
- package/src/pages-standard/PromoCodeDetailPage.vue +331 -0
- package/src/pages-standard/PromoCodesPage.vue +589 -0
- package/src/pages-standard/SubscriptionsPage.vue +159 -0
- package/src/pages-standard/SuperAdminLoginPage.vue +299 -0
- package/src/pages-standard/SuperAdminSetupWizard.vue +405 -0
- package/src/pages-standard/TenantDetailPage.vue +432 -0
- package/src/pages-standard/TenantsPage.vue +732 -0
- package/src/pages-standard/UsersPage.vue +504 -0
- package/src/pages-standard/bundles-page/BundleAccordionList.vue +116 -0
- package/src/pages-standard/bundles-page/BundleDetailPanel.vue +193 -0
- package/src/pages-standard/bundles-page/BundlesFilterBar.vue +48 -0
- package/src/pages-standard/bundles-page/BundlesHeader.vue +67 -0
- package/src/pages-standard/bundles-page/BundlesKpis.vue +39 -0
- package/src/pages-standard/bundles-page/types.ts +15 -0
- package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +246 -0
- package/src/pages-standard/discovery-page/DiscoveryCapList.vue +171 -0
- package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +410 -0
- package/src/pages-standard/discovery-page/DiscoveryHeader.vue +31 -0
- package/src/pages-standard/discovery-page/DiscoveryKpis.vue +47 -0
- package/src/pages-standard/discovery-page/DiscoveryMetaBanner.vue +26 -0
- package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +247 -0
- package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +73 -0
- package/src/pages-standard/discovery-page/discovery-ui.ts +166 -0
- package/src/pages-standard/email-history.types.ts +48 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +448 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +93 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +138 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +58 -0
- package/src/pages-standard/marketing-catalog/types.ts +43 -0
- package/src/pages-standard/plan-versions/PlanDiffCard.vue +132 -0
- package/src/pages-standard/plan-versions/PlanVersionHeader.vue +293 -0
- package/src/pages-standard/plan-versions/PlanVersionStatusBadge.vue +48 -0
- package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +307 -0
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +269 -0
- package/src/pages-standard/plan-versions/PlanVersionsKpi.vue +65 -0
- package/src/pages-standard/plan-versions/PlanVersionsList.vue +292 -0
- package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +261 -0
- package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +300 -0
- package/src/pages-standard/plan-versions/VersionDiffPreview.vue +167 -0
- package/src/pages-standard/plan-versions/format.ts +53 -0
- package/src/pages-standard/plan-versions/types.ts +33 -0
- package/src/pages-standard/plans-page/PlanArchiveDialog.vue +50 -0
- package/src/pages-standard/plans-page/PlanBundleOverview.vue +147 -0
- package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +46 -0
- package/src/pages-standard/plans-page/PlanPublishDialog.vue +106 -0
- package/src/pages-standard/plans-page/PlansPageToast.vue +69 -0
- package/src/pages-standard/plans-page/types.ts +18 -0
- package/src/pages-standard/platform-email.types.ts +41 -0
- package/src/pages-standard/sa-theme.css +223 -0
- package/src/pages-standard/tenants/StatusPill.vue +62 -0
- package/src/pages-standard/tenants/format.ts +38 -0
- package/src/pages-tenant/LimitsRow.vue +67 -0
- package/src/pages-tenant/MySubscriptionBundlesPage.vue +604 -0
- package/src/pages-tenant/OnboardingConfigurator.vue +417 -0
- package/src/pages-tenant/PackageSnapshotPanel.vue +241 -0
- package/src/pages-tenant/PendingVersionBanner.vue +126 -0
- package/src/pages-tenant/PlanChangeWizard.vue +630 -0
- package/src/pages-tenant/TenantPlanSection.vue +729 -0
- package/src/pages-tenant/UsageBar.vue +107 -0
- package/src/pages-tenant/default-i18n.ts +280 -0
- package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +304 -0
- package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +323 -0
- package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +122 -0
- package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +67 -0
- package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +31 -0
- package/src/plan-versions-catalog.ts +368 -0
- package/src/platform-loaders.ts +68 -0
- package/src/project-page-host.ts +108 -0
- package/src/testing-e2e/admin-pages-suite.ts +187 -0
- package/src/types.ts +58 -0
- package/src/use-actions.ts +25 -0
- package/src/use-api-list.ts +151 -0
- package/src/use-audit-entries.ts +48 -0
- package/src/use-batch-columns.ts +59 -0
- package/src/use-bulk-publish.ts +150 -0
- package/src/use-bundle-versions-map.ts +112 -0
- package/src/use-bundles.ts +297 -0
- package/src/use-business-types.ts +269 -0
- package/src/use-catalog-entries.ts +243 -0
- package/src/use-discovery.ts +158 -0
- package/src/use-entitlement.ts +87 -0
- package/src/use-live-plan-versions.ts +126 -0
- package/src/use-manifest.ts +57 -0
- package/src/use-marketing-projections.ts +160 -0
- package/src/use-nav.ts +33 -0
- package/src/use-plan-editor.ts +162 -0
- package/src/use-plan-versions.ts +83 -0
- package/src/use-plans.ts +375 -0
- package/src/use-platform-tenant-actions.ts +286 -0
- package/src/use-promotions.ts +126 -0
- package/src/use-public-boot.ts +40 -0
- package/src/use-subscription-draft.ts +355 -0
- package/src/use-super-admin-context.ts +93 -0
- package/src/use-tenant-action-flow.ts +238 -0
- package/src/use-tenant-billing-catalog.ts +160 -0
- package/src/use-tenant-billing.ts +453 -0
- package/src/use-tenant-manifest.ts +94 -0
- package/src/use-tenant-subscription-bundles.ts +148 -0
- package/src/use-tenants.ts +61 -0
- package/src/version.ts +10 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<header class="pve-bar">
|
|
3
|
+
<div class="pve-bar-left">
|
|
4
|
+
<div class="pve-titlechip">
|
|
5
|
+
<span class="pve-titlechip-kicker">PLAN</span>
|
|
6
|
+
<span class="pve-chip pve-chip--plan">{{ planKey }}</span>
|
|
7
|
+
</div>
|
|
8
|
+
<h2 class="pve-title">
|
|
9
|
+
{{ editingId ? `Draft v${version} bearbeiten` : `Neue Version v${version}` }}
|
|
10
|
+
</h2>
|
|
11
|
+
<span class="pve-chip pve-chip--draft pve-chip--dot">Draft</span>
|
|
12
|
+
<span class="pve-bar-note">
|
|
13
|
+
Vorgänger wird beim Publish auf
|
|
14
|
+
<code class="pve-mono">{{ predecessorValidUntilHint }}</code> superseded
|
|
15
|
+
</span>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="pve-bar-right">
|
|
18
|
+
<button
|
|
19
|
+
class="pve-btn"
|
|
20
|
+
type="button"
|
|
21
|
+
:disabled="!hasPredecessor"
|
|
22
|
+
:title="hasPredecessor ? undefined : 'Keine Vorgänger-Version (v1)'"
|
|
23
|
+
@click="$emit('showDiff')"
|
|
24
|
+
>
|
|
25
|
+
<span class="pve-ico" aria-hidden="true">
|
|
26
|
+
<svg
|
|
27
|
+
width="14"
|
|
28
|
+
height="14"
|
|
29
|
+
viewBox="0 0 24 24"
|
|
30
|
+
fill="none"
|
|
31
|
+
stroke="currentColor"
|
|
32
|
+
stroke-width="2"
|
|
33
|
+
>
|
|
34
|
+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
35
|
+
<circle cx="12" cy="12" r="3" />
|
|
36
|
+
</svg>
|
|
37
|
+
</span>
|
|
38
|
+
<span>Diff vs. Vorgänger</span>
|
|
39
|
+
</button>
|
|
40
|
+
<button class="pve-btn" type="button" @click="$emit('cancel')">
|
|
41
|
+
<span class="pve-ico" style="transform: rotate(180deg)" aria-hidden="true">
|
|
42
|
+
<svg
|
|
43
|
+
width="14"
|
|
44
|
+
height="14"
|
|
45
|
+
viewBox="0 0 24 24"
|
|
46
|
+
fill="none"
|
|
47
|
+
stroke="currentColor"
|
|
48
|
+
stroke-width="2"
|
|
49
|
+
>
|
|
50
|
+
<path d="M5 12h14M13 5l7 7-7 7" />
|
|
51
|
+
</svg>
|
|
52
|
+
</span>
|
|
53
|
+
<span>Zurück</span>
|
|
54
|
+
</button>
|
|
55
|
+
<button
|
|
56
|
+
class="pve-btn pve-btn--primary"
|
|
57
|
+
type="button"
|
|
58
|
+
:disabled="!canSave"
|
|
59
|
+
@click="$emit('save')"
|
|
60
|
+
>
|
|
61
|
+
<span>Weiter · Review</span>
|
|
62
|
+
<span class="pve-ico" aria-hidden="true">
|
|
63
|
+
<svg
|
|
64
|
+
width="14"
|
|
65
|
+
height="14"
|
|
66
|
+
viewBox="0 0 24 24"
|
|
67
|
+
fill="none"
|
|
68
|
+
stroke="currentColor"
|
|
69
|
+
stroke-width="2"
|
|
70
|
+
>
|
|
71
|
+
<path d="M5 12h14M13 5l7 7-7 7" />
|
|
72
|
+
</svg>
|
|
73
|
+
</span>
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
</header>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<script setup lang="ts">
|
|
80
|
+
defineProps<{
|
|
81
|
+
planKey: string;
|
|
82
|
+
editingId: string | null;
|
|
83
|
+
version: number;
|
|
84
|
+
predecessorValidUntilHint: string;
|
|
85
|
+
hasPredecessor: boolean;
|
|
86
|
+
canSave: boolean;
|
|
87
|
+
}>();
|
|
88
|
+
|
|
89
|
+
defineEmits<{
|
|
90
|
+
(e: 'showDiff'): void;
|
|
91
|
+
(e: 'cancel'): void;
|
|
92
|
+
(e: 'save'): void;
|
|
93
|
+
}>();
|
|
94
|
+
</script>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export interface DiscoveryFeature {
|
|
2
|
+
featureKey: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface DiscoveryQuota {
|
|
6
|
+
quotaKey: string;
|
|
7
|
+
label?: string | null;
|
|
8
|
+
unit?: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BundleEntry {
|
|
12
|
+
bundleKey: string;
|
|
13
|
+
label?: string | null;
|
|
14
|
+
features: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface FeatureMeta {
|
|
18
|
+
label?: string;
|
|
19
|
+
/** Bucket in the library pool (e.g. "Core", "Communication"). */
|
|
20
|
+
group?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PredecessorVersion {
|
|
24
|
+
version: number;
|
|
25
|
+
features: string[];
|
|
26
|
+
quotas: Record<string, number>;
|
|
27
|
+
monthlyNet: string;
|
|
28
|
+
yearlyNet: string;
|
|
29
|
+
/** "Valid from" of the predecessor version — the draft must start strictly after it. */
|
|
30
|
+
validFrom: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface EditorDiffRow {
|
|
34
|
+
id: string;
|
|
35
|
+
section: string;
|
|
36
|
+
label: string;
|
|
37
|
+
sub?: string;
|
|
38
|
+
from?: string;
|
|
39
|
+
to?: string;
|
|
40
|
+
bg: string;
|
|
41
|
+
border: string;
|
|
42
|
+
color: string;
|
|
43
|
+
sign: string;
|
|
44
|
+
tag: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface DraftForm {
|
|
48
|
+
version: number;
|
|
49
|
+
features: string[];
|
|
50
|
+
quotas: Record<string, number>;
|
|
51
|
+
monthlyNet: string;
|
|
52
|
+
yearlyNet: string;
|
|
53
|
+
changeNote: string;
|
|
54
|
+
marketed: boolean;
|
|
55
|
+
validFrom: string | null;
|
|
56
|
+
validUntil: string | null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Save payload = DraftForm plus the persisted bundle selection. `bundles`
|
|
61
|
+
* is derived from the final feature list (all fully-active bundles),
|
|
62
|
+
* so that the persisted list is invariantly consistent with `features` —
|
|
63
|
+
* see `PlanVersionRow.bundles`.
|
|
64
|
+
*/
|
|
65
|
+
export interface PlanVersionSavePayload extends DraftForm {
|
|
66
|
+
bundles: string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type PoolTab = 'features' | 'quotas' | 'bundles';
|
|
70
|
+
export type PoolKind = 'feature' | 'quota' | 'bundle';
|
|
71
|
+
export type PreviewMode = 'desktop' | 'mobile';
|
|
72
|
+
|
|
73
|
+
export interface PoolTabItem {
|
|
74
|
+
id: PoolTab;
|
|
75
|
+
label: string;
|
|
76
|
+
count: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface FeatureGroup {
|
|
80
|
+
key: string;
|
|
81
|
+
label: string;
|
|
82
|
+
rows: DiscoveryFeature[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ChecklistItem {
|
|
86
|
+
id: string;
|
|
87
|
+
label: string;
|
|
88
|
+
ok: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface SelectedQuotaRow {
|
|
92
|
+
quotaKey: string;
|
|
93
|
+
label: string;
|
|
94
|
+
unit: string;
|
|
95
|
+
sub: string;
|
|
96
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="wz-stepper">
|
|
3
|
+
<template v-for="(step, i) in steps" :key="i">
|
|
4
|
+
<div
|
|
5
|
+
class="wz-step"
|
|
6
|
+
:class="{
|
|
7
|
+
'wz-step--current': i === currentIndex,
|
|
8
|
+
'wz-step--done': i < currentIndex,
|
|
9
|
+
}"
|
|
10
|
+
>
|
|
11
|
+
<span class="wz-step-num">
|
|
12
|
+
<svg
|
|
13
|
+
v-if="i < currentIndex"
|
|
14
|
+
width="11"
|
|
15
|
+
height="11"
|
|
16
|
+
viewBox="0 0 24 24"
|
|
17
|
+
fill="none"
|
|
18
|
+
stroke="currentColor"
|
|
19
|
+
stroke-width="3.5"
|
|
20
|
+
aria-hidden="true"
|
|
21
|
+
>
|
|
22
|
+
<path d="M5 13l4 4L19 7" />
|
|
23
|
+
</svg>
|
|
24
|
+
<template v-else>{{ i + 1 }}</template>
|
|
25
|
+
</span>
|
|
26
|
+
<span>{{ step.label }}</span>
|
|
27
|
+
</div>
|
|
28
|
+
<span v-if="i < steps.length - 1" class="wz-step-sep" aria-hidden="true" />
|
|
29
|
+
</template>
|
|
30
|
+
<div v-if="hint" class="wz-stepper-hint">{{ hint }}</div>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup lang="ts">
|
|
35
|
+
// WizardStepper — generic step bar for multi-step SuperAdmin
|
|
36
|
+
// wizards (plan creation, bundle creation, …). Purely presentational: the host
|
|
37
|
+
// controls `currentIndex`; the component does not know the flow.
|
|
38
|
+
|
|
39
|
+
export interface WizardStep {
|
|
40
|
+
label: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
defineProps<{
|
|
44
|
+
steps: WizardStep[];
|
|
45
|
+
/** 0-based index of the current step. */
|
|
46
|
+
currentIndex: number;
|
|
47
|
+
/** Optional hint text, right-aligned (e.g. "Jederzeit als Draft speicherbar"). */
|
|
48
|
+
hint?: string;
|
|
49
|
+
}>();
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<style scoped>
|
|
53
|
+
.wz-stepper {
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: 6px;
|
|
57
|
+
padding: 14px 26px;
|
|
58
|
+
background: var(--sa-bg-surface);
|
|
59
|
+
border-bottom: 1px solid var(--sa-border);
|
|
60
|
+
}
|
|
61
|
+
.wz-step {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 8px;
|
|
65
|
+
padding: 6px 12px;
|
|
66
|
+
border-radius: 999px;
|
|
67
|
+
font: 500 12.5px var(--sa-font-body);
|
|
68
|
+
color: var(--sa-muted);
|
|
69
|
+
}
|
|
70
|
+
.wz-step--current {
|
|
71
|
+
background: var(--sa-heading);
|
|
72
|
+
color: #fff;
|
|
73
|
+
}
|
|
74
|
+
.wz-step--done {
|
|
75
|
+
color: var(--sa-positive);
|
|
76
|
+
}
|
|
77
|
+
.wz-step-num {
|
|
78
|
+
width: 20px;
|
|
79
|
+
height: 20px;
|
|
80
|
+
border-radius: 50%;
|
|
81
|
+
background: var(--sa-border);
|
|
82
|
+
color: var(--sa-muted-dark);
|
|
83
|
+
display: grid;
|
|
84
|
+
place-items: center;
|
|
85
|
+
font: 700 11px var(--sa-font-body);
|
|
86
|
+
flex: 0 0 auto;
|
|
87
|
+
}
|
|
88
|
+
.wz-step--current .wz-step-num {
|
|
89
|
+
background: rgba(255, 255, 255, 0.18);
|
|
90
|
+
color: #fff;
|
|
91
|
+
}
|
|
92
|
+
.wz-step--done .wz-step-num {
|
|
93
|
+
background: var(--sa-positive-soft);
|
|
94
|
+
color: var(--sa-positive);
|
|
95
|
+
}
|
|
96
|
+
.wz-step-sep {
|
|
97
|
+
width: 14px;
|
|
98
|
+
height: 1px;
|
|
99
|
+
background: var(--sa-border);
|
|
100
|
+
margin: 0 2px;
|
|
101
|
+
}
|
|
102
|
+
.wz-stepper-hint {
|
|
103
|
+
margin-left: auto;
|
|
104
|
+
font-size: 11px;
|
|
105
|
+
color: var(--sa-muted);
|
|
106
|
+
}
|
|
107
|
+
</style>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// createAdminRoutes — shared route shell for SuperAdmin apps.
|
|
2
|
+
//
|
|
3
|
+
// The structure (public `/login`, fail-closed `/admin-error`, `/admin` layout
|
|
4
|
+
// with `→ dashboard` redirect + `ProjectPageHost` catch-all, `/` and
|
|
5
|
+
// 404 redirect) is identical across all consumers. App-specific are only the
|
|
6
|
+
// layout/error/login components and the concrete child pages — those the app
|
|
7
|
+
// passes through. Replaces the ~25 lines of copied route boilerplate per app.
|
|
8
|
+
|
|
9
|
+
import type { RouteRecordRaw } from 'vue-router';
|
|
10
|
+
|
|
11
|
+
import { createProjectPageHostRoute } from './project-page-host.js';
|
|
12
|
+
|
|
13
|
+
/** Eager or lazy component loader (`() => import(...)`). */
|
|
14
|
+
type RouteComponent = NonNullable<RouteRecordRaw['component']>;
|
|
15
|
+
|
|
16
|
+
export interface CreateAdminRoutesOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Loader for the shared login page. The app passes it through as a
|
|
19
|
+
* package-path import (resolved by the app bundler):
|
|
20
|
+
* `() => import('@saasicat/ui-vue/pages-standard/SuperAdminLoginPage.vue')`.
|
|
21
|
+
*/
|
|
22
|
+
loginPage: RouteComponent;
|
|
23
|
+
/** Layout component for `/admin` (e.g. `() => import('@/layouts/AdminLayout.vue')`). */
|
|
24
|
+
adminLayout: RouteComponent;
|
|
25
|
+
/** Fail-closed error page for `manifestGuard.errorRoute` (`/admin-error`, public). */
|
|
26
|
+
adminErrorPage: RouteComponent;
|
|
27
|
+
/**
|
|
28
|
+
* App-specific pages under `/admin` (without the `→ dashboard` redirect and
|
|
29
|
+
* the `ProjectPageHost` catch-all — those the factory adds). Exactly the
|
|
30
|
+
* `children` entries that are duplicated per app today.
|
|
31
|
+
*/
|
|
32
|
+
children: RouteRecordRaw[];
|
|
33
|
+
/** Default target of the `/admin` index redirect. Default `/admin/dashboard`. */
|
|
34
|
+
dashboardPath?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function createAdminRoutes(options: CreateAdminRoutesOptions): RouteRecordRaw[] {
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
path: '/login',
|
|
41
|
+
name: 'login',
|
|
42
|
+
component: options.loginPage,
|
|
43
|
+
meta: { public: true },
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
// `meta.public: true` MUST be set, otherwise the route runs through
|
|
47
|
+
// the manifest guard again → redirect loop.
|
|
48
|
+
path: '/admin-error',
|
|
49
|
+
name: 'admin-error',
|
|
50
|
+
component: options.adminErrorPage,
|
|
51
|
+
meta: { public: true },
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
path: '/admin',
|
|
55
|
+
component: options.adminLayout,
|
|
56
|
+
children: [
|
|
57
|
+
{ path: '', redirect: options.dashboardPath ?? '/admin/dashboard' },
|
|
58
|
+
...options.children,
|
|
59
|
+
// Catch-all for manifest ProjectPages — Vue Router 4 matches
|
|
60
|
+
// more specific children first; the host only steps in when
|
|
61
|
+
// no static page matches. MUST come last.
|
|
62
|
+
createProjectPageHostRoute(),
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{ path: '/', redirect: '/admin' },
|
|
66
|
+
{ path: '/:pathMatch(.*)*', redirect: '/admin' },
|
|
67
|
+
];
|
|
68
|
+
}
|