@saasicat/ui-vue 0.19.0 → 0.22.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/dist/{catalog-JXdKdIqG.d.cts → catalog-DJcfm5N2.d.cts} +21 -7
- package/dist/{catalog-JXdKdIqG.d.ts → catalog-DJcfm5N2.d.ts} +21 -7
- package/dist/{chunk-DPTIOQXX.js → chunk-F7NRM6KI.js} +56 -28
- package/dist/{chunk-RVG6LQHL.js → chunk-LY52T4N4.js} +21 -2
- package/dist/{chunk-ORX3ZG47.js → chunk-XMMFN2WJ.js} +74 -3
- package/dist/client/index.cjs +134 -29
- package/dist/client/index.d.cts +53 -3
- package/dist/client/index.d.ts +53 -3
- package/dist/client/index.js +14 -2
- package/dist/index.cjs +321 -162
- package/dist/index.d.cts +30 -8
- package/dist/index.d.ts +30 -8
- package/dist/index.js +176 -133
- package/dist/quasar/index.cjs +78 -29
- package/dist/quasar/index.d.cts +2 -2
- package/dist/quasar/index.d.ts +2 -2
- package/dist/quasar/index.js +6 -2
- package/dist/testing-e2e/admin-pages-suite.d.cts +37 -0
- package/dist/testing-e2e/admin-pages-suite.d.ts +37 -0
- package/dist/{use-super-admin-i18n-BlqcD0N7.d.ts → use-super-admin-i18n-CHNdsSlm.d.ts} +34 -7
- package/dist/{use-super-admin-i18n-1nCOkvgi.d.cts → use-super-admin-i18n-CgTuNi8h.d.cts} +34 -7
- package/package.json +18 -8
- package/src/client/i18n/messages/bundles.ts +0 -2
- package/src/client/i18n/messages/common.ts +24 -0
- package/src/client/i18n/messages/discovery.ts +2 -2
- package/src/client/i18n/messages/marketing.ts +2 -4
- package/src/client/i18n/messages/plan-detail.ts +0 -4
- package/src/client/i18n/messages/plans.ts +20 -8
- package/src/client/i18n/messages/tenants.ts +0 -2
- package/src/client/index.ts +1 -0
- package/src/client/manifest-loader.ts +1 -1
- package/src/client/resolve-plans.ts +144 -0
- package/src/components/BundleVersionPublishDialog.vue +6 -6
- package/src/components/KvBlock.vue +4 -4
- package/src/components/MarketingPromotionsTab.vue +34 -48
- package/src/components/MfaPromptDialog.vue +8 -2
- package/src/components/TenantActionConfirmDialog.vue +4 -4
- package/src/components/VersionDiffPreview.vue +7 -7
- package/src/components/admin-page/AdminBody.vue +40 -0
- package/src/components/admin-page/AdminFilters.vue +12 -0
- package/src/components/admin-page/AdminHero.vue +49 -0
- package/src/components/admin-page/AdminKpi.vue +84 -0
- package/src/components/admin-page/AdminPage.vue +16 -0
- package/src/components/admin-page/AdminPaginator.vue +195 -0
- package/src/components/admin-page/AdminRefreshBtn.vue +39 -0
- package/src/components/admin-page/AdminSection.vue +53 -0
- package/src/components/admin-page/AdminStatistics.vue +40 -0
- package/src/components/admin-page/AdminTable.vue +175 -0
- package/src/components/bundle-editor/BundleCreatePanel.vue +23 -23
- package/src/components/bundle-editor/BundleFeaturesEditor.vue +7 -7
- package/src/components/bundle-editor/BundlePlanCompatPicker.vue +9 -9
- package/src/components/bundle-editor/BundleQuotasEditor.vue +11 -11
- package/src/components/bundle-editor/BundleStatusBanner.vue +6 -6
- package/src/components/bundle-editor/BundleVersionInlineEditor.vue +18 -18
- package/src/components/bundle-editor/BundleVersionStrip.vue +12 -12
- package/src/components/dialogs/PilotCreateDialog.vue +12 -12
- package/src/components/dialogs/PilotEditDialog.vue +7 -7
- package/src/components/dialogs/PromoCodeCreateDialog.vue +8 -8
- package/src/components/dialogs/PromoCodeDialogFields.vue +33 -19
- package/src/components/dialogs/PromoCodeEditDialog.vue +10 -10
- package/src/components/plan/PlanGrid.vue +1 -1
- package/src/components/plan-create-dialog/PlanCreateDialog.vue +34 -46
- package/src/components/plan-detail/PlanAuditLog.vue +7 -8
- package/src/components/plan-detail/PlanDetail.vue +95 -292
- package/src/components/plan-detail/PlanDetailKpis.vue +22 -27
- package/src/components/plan-detail/PlanTitleEdit.vue +133 -0
- package/src/components/plan-detail/PlanVersionDiffPanel.vue +5 -8
- package/src/components/plan-detail/PlanVersionsPanel.vue +9 -11
- package/src/components/plan-list/PlanList.vue +206 -457
- package/src/components/plan-matrix/PlanMatrix.vue +66 -183
- package/src/components/plan-review/PlanReview.vue +191 -275
- package/src/components/plan-version-editor/PlanCatalogPreview.vue +7 -5
- package/src/components/plan-version-editor/PlanVersionEditor.vue +200 -295
- package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +23 -55
- package/src/index.ts +1 -0
- package/src/pages-standard/AdminLayout.vue +38 -12
- package/src/pages-standard/AdminManifestErrorPage.vue +79 -9
- package/src/pages-standard/AuditPage.vue +65 -109
- package/src/pages-standard/BundlesPage.vue +146 -179
- package/src/pages-standard/DashboardPage.vue +59 -154
- package/src/pages-standard/DiscoveryPage.vue +172 -227
- package/src/pages-standard/EmailHistoryPage.vue +118 -159
- package/src/pages-standard/MarketingCatalogPage.vue +399 -440
- package/src/pages-standard/PilotsPage.vue +69 -117
- package/src/pages-standard/PlansPage.vue +240 -112
- package/src/pages-standard/PlatformEmailPage.vue +56 -91
- package/src/pages-standard/PromoCodeDetailPage.vue +49 -74
- package/src/pages-standard/PromoCodesPage.vue +95 -128
- package/src/pages-standard/SubscriptionsPage.vue +19 -52
- package/src/pages-standard/SuperAdminLoginPage.vue +12 -12
- package/src/pages-standard/SuperAdminSetupWizard.vue +22 -18
- package/src/pages-standard/TenantDetailPage.vue +100 -191
- package/src/pages-standard/TenantsPage.vue +190 -295
- package/src/pages-standard/UsersPage.vue +89 -157
- package/src/pages-standard/bundles-page/BundleAccordionList.vue +20 -17
- package/src/pages-standard/bundles-page/BundlesKpis.vue +13 -22
- package/src/pages-standard/bundles-page/BundlesToolbar.vue +63 -0
- package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +10 -10
- package/src/pages-standard/discovery-page/DiscoveryCapList.vue +5 -5
- package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +16 -16
- package/src/pages-standard/discovery-page/DiscoveryKpis.vue +31 -27
- package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +7 -7
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +111 -73
- package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +43 -62
- package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +53 -35
- package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +7 -7
- package/src/pages-standard/plan-versions/PlanDiffCard.vue +6 -6
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +6 -6
- package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +16 -16
- package/src/pages-standard/plans-page/PlanBundleOverview.vue +14 -29
- package/src/pages-standard/plans-page/PlanPublishDialog.vue +1 -1
- package/src/pages-standard/plans-page/PlansPageToast.vue +6 -6
- package/src/pages-standard/sa-theme.css +552 -86
- package/src/pages-standard/tenant-detail/TenantMasterData.vue +28 -0
- package/src/pages-standard/tenant-detail/TenantUsage.vue +31 -0
- package/src/pages-standard/tenant-detail/TenantUsers.vue +15 -0
- package/src/pages-standard/tenant-detail/types.ts +38 -0
- package/src/pages-standard/tenants/StatusPill.vue +2 -2
- package/src/pages-tenant/MySubscriptionBundlesPage.vue +25 -25
- package/src/pages-tenant/OnboardingConfigurator.vue +1 -5
- package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
- package/src/pages-tenant/TenantPlanSection.vue +12 -20
- package/src/quasar/create-super-admin-app.ts +4 -0
- package/src/vue/entitlement-provider.ts +0 -1
- package/src/vue/feature-router-guard.ts +0 -1
- package/src/vue/super-admin-context.ts +106 -1
- package/src/vue/use-api-list.ts +2 -1
- package/src/vue/use-mfa-prompt.ts +72 -0
- package/src/vue/use-pagination.ts +63 -0
- package/src/vue/use-plans.ts +1 -2
- package/src/vue/use-sign-out.ts +90 -0
- package/src/vue/use-tenant-billing.ts +1 -1
- package/src/vue/use-tenant-manifest.ts +0 -1
- package/src/components/KpiCard.vue +0 -53
- package/src/components/plan-cockpit/PlanCockpit.vue +0 -1012
- package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +0 -72
- package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +0 -100
- package/src/components/plan-cockpit/PlanCockpitHeader.vue +0 -101
- package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +0 -112
- package/src/components/plan-cockpit/PlanCockpitKpis.vue +0 -68
- package/src/components/plan-cockpit/PlanCockpitVersions.vue +0 -251
- package/src/components/plan-cockpit/types.ts +0 -72
- package/src/components/plan-detail/PlanDetailHeader.vue +0 -194
- package/src/pages-standard/bundles-page/BundlesHeader.vue +0 -68
- package/src/pages-standard/discovery-page/DiscoveryHeader.vue +0 -34
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<section v-if="auditRows.length > 0 || loadingAudit" class="pc-card pc-audit-card">
|
|
3
|
-
<div class="pc-card-head pc-card-head--audit">
|
|
4
|
-
<div class="pc-card-head-text">
|
|
5
|
-
<div class="pc-card-title">{{ msg.auditLog.title }}</div>
|
|
6
|
-
<div class="pc-card-sub">{{ msg.auditLog.subtitle }}</div>
|
|
7
|
-
</div>
|
|
8
|
-
</div>
|
|
9
|
-
<div class="pc-audit">
|
|
10
|
-
<div v-if="loadingAudit" class="pc-empty pc-empty--inline">
|
|
11
|
-
{{ msg.auditLog.loading }}
|
|
12
|
-
</div>
|
|
13
|
-
<div v-for="a in auditRows" :key="a.id" class="pc-audit-row">
|
|
14
|
-
<span :class="['pc-audit-dot', `pc-audit-${auditKind(a.action)}`]" />
|
|
15
|
-
<span class="pc-audit-when">{{ formatAuditDate(a.createdAt) }}</span>
|
|
16
|
-
<span class="pc-audit-avatar">{{ auditInitial(a) }}</span>
|
|
17
|
-
<span class="pc-audit-who">{{ auditWho(a) }}</span>
|
|
18
|
-
<span class="pc-audit-what">{{ auditLabel(a) }}</span>
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
</section>
|
|
22
|
-
</template>
|
|
23
|
-
|
|
24
|
-
<script setup lang="ts">
|
|
25
|
-
import { formatMessage } from '../../client/i18n/format.js';
|
|
26
|
-
import { useSaMessages, useSuperAdminI18n } from '../../vue/use-super-admin-i18n.js';
|
|
27
|
-
import type { AuditRow } from './types';
|
|
28
|
-
|
|
29
|
-
defineProps<{
|
|
30
|
-
auditRows: AuditRow[];
|
|
31
|
-
loadingAudit: boolean;
|
|
32
|
-
}>();
|
|
33
|
-
|
|
34
|
-
const msg = useSaMessages('planDetail');
|
|
35
|
-
const { intlLocale } = useSuperAdminI18n();
|
|
36
|
-
|
|
37
|
-
function auditKind(action: string): string {
|
|
38
|
-
const a = action.toLowerCase();
|
|
39
|
-
if (a.includes('publish')) return 'publish';
|
|
40
|
-
if (a.includes('create') && a.includes('draft')) return 'draft';
|
|
41
|
-
if (a.includes('create')) return 'add';
|
|
42
|
-
if (a.includes('update')) return 'change';
|
|
43
|
-
if (a.includes('delete')) return 'remove';
|
|
44
|
-
return 'change';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function auditWho(a: AuditRow): string {
|
|
48
|
-
if (a.user?.firstName || a.user?.lastName) {
|
|
49
|
-
return `${a.user.firstName ?? ''} ${a.user.lastName ?? ''}`.trim();
|
|
50
|
-
}
|
|
51
|
-
return a.user?.email ?? a.userEmail ?? 'system';
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function auditInitial(a: AuditRow): string {
|
|
55
|
-
const who = auditWho(a);
|
|
56
|
-
return who[0]?.toUpperCase() ?? '?';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function auditLabel(a: AuditRow): string {
|
|
60
|
-
return a.action;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function formatAuditDate(iso: string): string {
|
|
64
|
-
const d = new Date(iso);
|
|
65
|
-
if (Number.isNaN(d.getTime())) return iso;
|
|
66
|
-
const today = new Date();
|
|
67
|
-
const isToday = d.toDateString() === today.toDateString();
|
|
68
|
-
const time = d.toLocaleTimeString(intlLocale.value, { hour: '2-digit', minute: '2-digit' });
|
|
69
|
-
if (isToday) return formatMessage(msg.value.auditLog.today, { time });
|
|
70
|
-
return `${d.toLocaleDateString(intlLocale.value, { day: '2-digit', month: 'short' })} · ${time}`;
|
|
71
|
-
}
|
|
72
|
-
</script>
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<section class="pc-card">
|
|
3
|
-
<div class="pc-card-head">
|
|
4
|
-
<div class="pc-card-head-text">
|
|
5
|
-
<div class="pc-card-title">{{ panelTitle }}</div>
|
|
6
|
-
<div class="pc-card-sub">
|
|
7
|
-
{{ diffPair ? msg.diff.subOnPublish : msg.diff.noComparison }}
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
<div v-if="diffPair" class="pc-pillrow">
|
|
11
|
-
<span class="pc-pill" style="background: #ecfdf5; color: #047857">
|
|
12
|
-
+{{ diff.addedFeatures.length }} F
|
|
13
|
-
</span>
|
|
14
|
-
<span class="pc-pill" style="background: #fef2f2; color: #b91c1c">
|
|
15
|
-
−{{ diff.removedFeatures.length }} F
|
|
16
|
-
</span>
|
|
17
|
-
<span class="pc-pill" style="background: #fffbeb; color: #b45309">
|
|
18
|
-
{{
|
|
19
|
-
diff.changedQuotas.length +
|
|
20
|
-
diff.addedQuotas.length +
|
|
21
|
-
diff.removedQuotas.length
|
|
22
|
-
}}
|
|
23
|
-
Q
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
<div class="pc-diff-body">
|
|
28
|
-
<div
|
|
29
|
-
v-for="d in diffRows"
|
|
30
|
-
:key="d.id"
|
|
31
|
-
class="pc-diff-row"
|
|
32
|
-
:style="{ background: d.bg, borderColor: d.border }"
|
|
33
|
-
>
|
|
34
|
-
<span class="pc-diff-sign" :style="{ background: d.color }">{{ d.sign }}</span>
|
|
35
|
-
<div class="pc-diff-main">
|
|
36
|
-
<div class="pc-diff-headline">
|
|
37
|
-
<span class="pc-diff-section" :style="{ color: d.color }">{{
|
|
38
|
-
d.section
|
|
39
|
-
}}</span>
|
|
40
|
-
<span class="pc-diff-label">{{ d.label }}</span>
|
|
41
|
-
<code v-if="d.sub" class="pc-mono pc-mono--xs">{{ d.sub }}</code>
|
|
42
|
-
</div>
|
|
43
|
-
<div v-if="d.from !== undefined" class="pc-diff-change">
|
|
44
|
-
<span class="pc-diff-from">{{ d.from }}</span>
|
|
45
|
-
<span class="pc-diff-arrow" :style="{ color: d.color }">
|
|
46
|
-
<svg
|
|
47
|
-
width="12"
|
|
48
|
-
height="12"
|
|
49
|
-
viewBox="0 0 24 24"
|
|
50
|
-
fill="none"
|
|
51
|
-
stroke="currentColor"
|
|
52
|
-
stroke-width="2"
|
|
53
|
-
>
|
|
54
|
-
<path d="M5 12h14M13 5l7 7-7 7" />
|
|
55
|
-
</svg>
|
|
56
|
-
</span>
|
|
57
|
-
<span class="pc-diff-to">{{ d.to }}</span>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
<span
|
|
61
|
-
class="pc-chip pc-diff-tag"
|
|
62
|
-
:style="{
|
|
63
|
-
background: '#fff',
|
|
64
|
-
color: d.color,
|
|
65
|
-
borderColor: d.border,
|
|
66
|
-
}"
|
|
67
|
-
>
|
|
68
|
-
{{ d.tag }}
|
|
69
|
-
</span>
|
|
70
|
-
</div>
|
|
71
|
-
<div v-if="diffRows.length === 0" class="pc-empty pc-empty--inline">
|
|
72
|
-
{{ msg.diff.noChangesDetected }}
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</section>
|
|
76
|
-
</template>
|
|
77
|
-
|
|
78
|
-
<script setup lang="ts">
|
|
79
|
-
import { computed } from 'vue';
|
|
80
|
-
import { formatMessage } from '../../client/i18n/format.js';
|
|
81
|
-
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
82
|
-
import type { DiffRow, DiffSummary, PlanVersionPair } from './types';
|
|
83
|
-
|
|
84
|
-
const props = defineProps<{
|
|
85
|
-
diffPair: PlanVersionPair | null;
|
|
86
|
-
diff: DiffSummary;
|
|
87
|
-
diffRows: DiffRow[];
|
|
88
|
-
}>();
|
|
89
|
-
|
|
90
|
-
const msg = useSaMessages('planDetail');
|
|
91
|
-
|
|
92
|
-
const panelTitle = computed(() => {
|
|
93
|
-
const pair = props.diffPair;
|
|
94
|
-
if (!pair) return msg.value.diff.title;
|
|
95
|
-
return formatMessage(msg.value.diff.titleCompare, {
|
|
96
|
-
from: pair.from.version,
|
|
97
|
-
to: pair.to.version,
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
</script>
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="pc-backbar">
|
|
3
|
-
<button class="pc-back" type="button" @click="emit('back')">
|
|
4
|
-
<span class="pc-back-ico" aria-hidden="true">
|
|
5
|
-
<svg
|
|
6
|
-
width="14"
|
|
7
|
-
height="14"
|
|
8
|
-
viewBox="0 0 24 24"
|
|
9
|
-
fill="none"
|
|
10
|
-
stroke="currentColor"
|
|
11
|
-
stroke-width="2"
|
|
12
|
-
>
|
|
13
|
-
<path d="M5 12h14M13 5l7 7-7 7" />
|
|
14
|
-
</svg>
|
|
15
|
-
</span>
|
|
16
|
-
<span>{{ msg.header.backToList }}</span>
|
|
17
|
-
</button>
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
<div class="pc-header">
|
|
21
|
-
<div class="pc-header-left">
|
|
22
|
-
<div class="pc-bigchip" :style="{ background: accentBg, color: accentFg }">
|
|
23
|
-
{{ plan.planKey }}
|
|
24
|
-
</div>
|
|
25
|
-
<div class="pc-header-titles">
|
|
26
|
-
<h2 class="pc-h-title">{{ plan.label }}</h2>
|
|
27
|
-
<p class="pc-h-sub">{{ plan.description || msg.header.noDescription }}</p>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
<div class="pc-header-right">
|
|
31
|
-
<button class="pc-btn" type="button" @click="emit('clonePlan')">
|
|
32
|
-
<svg
|
|
33
|
-
width="14"
|
|
34
|
-
height="14"
|
|
35
|
-
viewBox="0 0 24 24"
|
|
36
|
-
fill="none"
|
|
37
|
-
stroke="currentColor"
|
|
38
|
-
stroke-width="2"
|
|
39
|
-
>
|
|
40
|
-
<rect x="9" y="9" width="11" height="11" rx="2" />
|
|
41
|
-
<path d="M5 15V5a2 2 0 012-2h10" />
|
|
42
|
-
</svg>
|
|
43
|
-
<span>{{ msg.header.clonePlan }}</span>
|
|
44
|
-
</button>
|
|
45
|
-
<button class="pc-btn" type="button" @click="emit('viewCatalog')">
|
|
46
|
-
<svg
|
|
47
|
-
width="14"
|
|
48
|
-
height="14"
|
|
49
|
-
viewBox="0 0 24 24"
|
|
50
|
-
fill="none"
|
|
51
|
-
stroke="currentColor"
|
|
52
|
-
stroke-width="2"
|
|
53
|
-
>
|
|
54
|
-
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
55
|
-
<circle cx="12" cy="12" r="3" />
|
|
56
|
-
</svg>
|
|
57
|
-
<span>{{ msg.header.viewInCatalog }}</span>
|
|
58
|
-
</button>
|
|
59
|
-
<button
|
|
60
|
-
class="pc-btn pc-btn--primary"
|
|
61
|
-
type="button"
|
|
62
|
-
:disabled="hasOpenDraft"
|
|
63
|
-
:title="hasOpenDraft ? msg.header.draftAlreadyOpen : undefined"
|
|
64
|
-
@click="emit('createDraft')"
|
|
65
|
-
>
|
|
66
|
-
<svg
|
|
67
|
-
width="14"
|
|
68
|
-
height="14"
|
|
69
|
-
viewBox="0 0 24 24"
|
|
70
|
-
fill="none"
|
|
71
|
-
stroke="currentColor"
|
|
72
|
-
stroke-width="2.5"
|
|
73
|
-
>
|
|
74
|
-
<path d="M12 5v14M5 12h14" />
|
|
75
|
-
</svg>
|
|
76
|
-
<span>{{ msg.header.newDraftVersion }}</span>
|
|
77
|
-
</button>
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
</template>
|
|
81
|
-
|
|
82
|
-
<script setup lang="ts">
|
|
83
|
-
import type { PlanRow } from '@saasicat/types';
|
|
84
|
-
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
85
|
-
|
|
86
|
-
defineProps<{
|
|
87
|
-
plan: PlanRow;
|
|
88
|
-
accentBg: string;
|
|
89
|
-
accentFg: string;
|
|
90
|
-
hasOpenDraft: boolean;
|
|
91
|
-
}>();
|
|
92
|
-
|
|
93
|
-
const emit = defineEmits<{
|
|
94
|
-
(e: 'back'): void;
|
|
95
|
-
(e: 'clonePlan'): void;
|
|
96
|
-
(e: 'viewCatalog'): void;
|
|
97
|
-
(e: 'createDraft'): void;
|
|
98
|
-
}>();
|
|
99
|
-
|
|
100
|
-
const msg = useSaMessages('planDetail');
|
|
101
|
-
</script>
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<section v-if="tenantImpact" class="pc-card">
|
|
3
|
-
<div class="pc-card-head">
|
|
4
|
-
<div class="pc-card-head-text">
|
|
5
|
-
<div class="pc-card-title">{{ msg.kpis.tenantImpact }}</div>
|
|
6
|
-
<div class="pc-card-sub">{{ msg.impact.subtitle }}</div>
|
|
7
|
-
</div>
|
|
8
|
-
</div>
|
|
9
|
-
<div class="pc-impact-strip">
|
|
10
|
-
<div class="pc-impact-num">{{ tenantImpactTotal }}</div>
|
|
11
|
-
<div class="pc-impact-bars">
|
|
12
|
-
<div class="pc-impact-bar">
|
|
13
|
-
<span
|
|
14
|
-
class="pc-impact-bar-fill"
|
|
15
|
-
:style="{
|
|
16
|
-
width: pct(tenantImpact.auto, tenantImpactTotal),
|
|
17
|
-
background: '#10b981',
|
|
18
|
-
}"
|
|
19
|
-
/>
|
|
20
|
-
<span class="pc-impact-bar-label">{{ autoLabel }}</span>
|
|
21
|
-
</div>
|
|
22
|
-
<div class="pc-impact-bar">
|
|
23
|
-
<span
|
|
24
|
-
class="pc-impact-bar-fill"
|
|
25
|
-
:style="{
|
|
26
|
-
width: pct(tenantImpact.review, tenantImpactTotal),
|
|
27
|
-
background: '#f59e0b',
|
|
28
|
-
}"
|
|
29
|
-
/>
|
|
30
|
-
<span class="pc-impact-bar-label">{{ reviewLabel }}</span>
|
|
31
|
-
</div>
|
|
32
|
-
<div class="pc-impact-bar">
|
|
33
|
-
<span
|
|
34
|
-
class="pc-impact-bar-fill"
|
|
35
|
-
:style="{
|
|
36
|
-
width: pct(tenantImpact.conflict, tenantImpactTotal),
|
|
37
|
-
background: '#ef4444',
|
|
38
|
-
}"
|
|
39
|
-
/>
|
|
40
|
-
<span class="pc-impact-bar-label">{{ conflictLabel }}</span>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
<div
|
|
45
|
-
v-if="tenantImpact.examples && tenantImpact.examples.length > 0"
|
|
46
|
-
class="pc-impact-tenants"
|
|
47
|
-
>
|
|
48
|
-
<div v-for="t in tenantImpact.examples" :key="t.name" class="pc-impact-tenant">
|
|
49
|
-
<div class="pc-impact-avatar">{{ initialsOf(t.name) }}</div>
|
|
50
|
-
<div class="pc-impact-body">
|
|
51
|
-
<div class="pc-impact-name">{{ t.name }}</div>
|
|
52
|
-
<div class="pc-impact-sub">{{ t.plan }}</div>
|
|
53
|
-
</div>
|
|
54
|
-
<span :class="['pc-chip', impactStateChip(t.state)]">{{
|
|
55
|
-
impactStateLabel(t.state)
|
|
56
|
-
}}</span>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
</section>
|
|
60
|
-
</template>
|
|
61
|
-
|
|
62
|
-
<script setup lang="ts">
|
|
63
|
-
import { computed } from 'vue';
|
|
64
|
-
import { formatMessage } from '../../client/i18n/format.js';
|
|
65
|
-
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
66
|
-
import type { ImpactTenant, TenantImpact } from './types';
|
|
67
|
-
|
|
68
|
-
const props = defineProps<{
|
|
69
|
-
tenantImpact: TenantImpact | null;
|
|
70
|
-
tenantImpactTotal: number;
|
|
71
|
-
}>();
|
|
72
|
-
|
|
73
|
-
const msg = useSaMessages('planDetail');
|
|
74
|
-
|
|
75
|
-
const autoLabel = computed(() =>
|
|
76
|
-
formatMessage(msg.value.impact.autoMigrate, { count: props.tenantImpact?.auto ?? 0 }),
|
|
77
|
-
);
|
|
78
|
-
const reviewLabel = computed(() =>
|
|
79
|
-
formatMessage(msg.value.impact.needReview, { count: props.tenantImpact?.review ?? 0 }),
|
|
80
|
-
);
|
|
81
|
-
const conflictLabel = computed(() =>
|
|
82
|
-
formatMessage(msg.value.impact.conflict, { count: props.tenantImpact?.conflict ?? 0 }),
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
function pct(part: number, total: number): string {
|
|
86
|
-
if (total <= 0) return '0%';
|
|
87
|
-
return `${Math.max(2, Math.round((part / total) * 100))}%`;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function initialsOf(name: string): string {
|
|
91
|
-
return name
|
|
92
|
-
.split(/\s+/)
|
|
93
|
-
.filter(Boolean)
|
|
94
|
-
.slice(0, 2)
|
|
95
|
-
.map((s) => s[0]?.toUpperCase() ?? '')
|
|
96
|
-
.join('');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function impactStateLabel(state: ImpactTenant['state']): string {
|
|
100
|
-
if (state === 'auto') return msg.value.impact.stateAuto;
|
|
101
|
-
if (state === 'review') return msg.value.impact.stateReview;
|
|
102
|
-
return msg.value.impact.stateConflict;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function impactStateChip(state: ImpactTenant['state']): string {
|
|
106
|
-
return state === 'auto'
|
|
107
|
-
? 'pc-chip--live'
|
|
108
|
-
: state === 'review'
|
|
109
|
-
? 'pc-chip--draft'
|
|
110
|
-
: 'pc-chip--conflict';
|
|
111
|
-
}
|
|
112
|
-
</script>
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="pc-kpis">
|
|
3
|
-
<div class="pc-kpi">
|
|
4
|
-
<div class="pc-kpi-label">{{ msg.kpis.activeVersion }}</div>
|
|
5
|
-
<div class="pc-kpi-val">
|
|
6
|
-
<template v-if="liveVersion">
|
|
7
|
-
v{{ liveVersion.version }}
|
|
8
|
-
<span class="pc-chip pc-chip--live pc-chip--dot pc-chip--mid">live</span>
|
|
9
|
-
</template>
|
|
10
|
-
<template v-else>—</template>
|
|
11
|
-
</div>
|
|
12
|
-
<div class="pc-kpi-sub">{{ activeVersionSub }}</div>
|
|
13
|
-
</div>
|
|
14
|
-
<div class="pc-kpi">
|
|
15
|
-
<div class="pc-kpi-label">{{ msg.kpis.tenantImpact }}</div>
|
|
16
|
-
<div class="pc-kpi-val">{{ tenantImpactTotal }}</div>
|
|
17
|
-
<div class="pc-kpi-sub">{{ msg.kpis.tenantsOnPlan }}</div>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="pc-kpi">
|
|
20
|
-
<div class="pc-kpi-label">{{ msg.kpis.versions }}</div>
|
|
21
|
-
<div class="pc-kpi-val">{{ versionsCount }}</div>
|
|
22
|
-
<div class="pc-kpi-sub">{{ versionsSummary }}</div>
|
|
23
|
-
</div>
|
|
24
|
-
<div :class="['pc-kpi', draftVersion ? 'pc-kpi--draft' : '']">
|
|
25
|
-
<div class="pc-kpi-label" :style="draftVersion ? 'color:#b45309' : ''">
|
|
26
|
-
{{ draftVersion ? msg.kpis.openDraft : msg.kpis.noDraft }}
|
|
27
|
-
</div>
|
|
28
|
-
<div class="pc-kpi-val">
|
|
29
|
-
{{ draftVersion ? `v${draftVersion.version}` : '—' }}
|
|
30
|
-
</div>
|
|
31
|
-
<div class="pc-kpi-sub" :style="draftVersion ? 'color:#b45309' : ''">
|
|
32
|
-
{{ draftVersion ? msg.kpis.readyToEdit : msg.kpis.createNewDraft }}
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</template>
|
|
37
|
-
|
|
38
|
-
<script setup lang="ts">
|
|
39
|
-
import { computed } from 'vue';
|
|
40
|
-
import type { PlanVersionRow } from '@saasicat/types';
|
|
41
|
-
import { formatMessage } from '../../client/i18n/format.js';
|
|
42
|
-
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
43
|
-
|
|
44
|
-
const props = defineProps<{
|
|
45
|
-
liveVersion: PlanVersionRow | null;
|
|
46
|
-
tenantImpactTotal: number;
|
|
47
|
-
versionsCount: number;
|
|
48
|
-
publishedCount: number;
|
|
49
|
-
draftCount: number;
|
|
50
|
-
draftVersion: PlanVersionRow | null;
|
|
51
|
-
}>();
|
|
52
|
-
|
|
53
|
-
const msg = useSaMessages('planDetail');
|
|
54
|
-
|
|
55
|
-
const activeVersionSub = computed(() => {
|
|
56
|
-
const validFrom = props.liveVersion?.validFrom;
|
|
57
|
-
return validFrom
|
|
58
|
-
? formatMessage(msg.value.kpis.activeSince, { date: validFrom.slice(0, 10) })
|
|
59
|
-
: msg.value.kpis.noPublishedVersion;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const versionsSummary = computed(() =>
|
|
63
|
-
formatMessage(msg.value.kpis.versionsSummaryWithDrafts, {
|
|
64
|
-
published: props.publishedCount,
|
|
65
|
-
drafts: props.draftCount,
|
|
66
|
-
}),
|
|
67
|
-
);
|
|
68
|
-
</script>
|