@saasicat/ui-vue 0.21.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.
Files changed (132) hide show
  1. package/dist/{catalog-BY_t9prv.d.cts → catalog-DJcfm5N2.d.cts} +20 -7
  2. package/dist/{catalog-BY_t9prv.d.ts → catalog-DJcfm5N2.d.ts} +20 -7
  3. package/dist/{chunk-JN6HZGLV.js → chunk-F7NRM6KI.js} +54 -28
  4. package/dist/{chunk-7IFKVGZV.js → chunk-LY52T4N4.js} +21 -2
  5. package/dist/{chunk-OTP3OSSW.js → chunk-XMMFN2WJ.js} +74 -3
  6. package/dist/client/index.cjs +132 -29
  7. package/dist/client/index.d.cts +53 -3
  8. package/dist/client/index.d.ts +53 -3
  9. package/dist/client/index.js +14 -2
  10. package/dist/index.cjs +319 -162
  11. package/dist/index.d.cts +30 -8
  12. package/dist/index.d.ts +30 -8
  13. package/dist/index.js +176 -133
  14. package/dist/quasar/index.cjs +76 -29
  15. package/dist/quasar/index.d.cts +2 -2
  16. package/dist/quasar/index.d.ts +2 -2
  17. package/dist/quasar/index.js +6 -2
  18. package/dist/testing-e2e/admin-pages-suite.d.cts +37 -0
  19. package/dist/testing-e2e/admin-pages-suite.d.ts +37 -0
  20. package/dist/{use-super-admin-i18n-c_Eqsu9u.d.ts → use-super-admin-i18n-CHNdsSlm.d.ts} +34 -7
  21. package/dist/{use-super-admin-i18n-DlJRGIVq.d.cts → use-super-admin-i18n-CgTuNi8h.d.cts} +34 -7
  22. package/package.json +18 -8
  23. package/src/client/i18n/messages/bundles.ts +0 -2
  24. package/src/client/i18n/messages/common.ts +22 -0
  25. package/src/client/i18n/messages/discovery.ts +2 -2
  26. package/src/client/i18n/messages/marketing.ts +2 -4
  27. package/src/client/i18n/messages/plan-detail.ts +0 -4
  28. package/src/client/i18n/messages/plans.ts +20 -8
  29. package/src/client/i18n/messages/tenants.ts +0 -2
  30. package/src/client/index.ts +1 -0
  31. package/src/client/manifest-loader.ts +1 -1
  32. package/src/client/resolve-plans.ts +144 -0
  33. package/src/components/BundleVersionPublishDialog.vue +6 -6
  34. package/src/components/KvBlock.vue +4 -4
  35. package/src/components/MarketingPromotionsTab.vue +34 -48
  36. package/src/components/MfaPromptDialog.vue +8 -2
  37. package/src/components/TenantActionConfirmDialog.vue +4 -4
  38. package/src/components/VersionDiffPreview.vue +7 -7
  39. package/src/components/admin-page/AdminFilters.vue +12 -0
  40. package/src/components/admin-page/AdminHero.vue +22 -5
  41. package/src/components/admin-page/AdminKpi.vue +84 -0
  42. package/src/components/admin-page/AdminPaginator.vue +195 -0
  43. package/src/components/admin-page/AdminRefreshBtn.vue +39 -0
  44. package/src/components/admin-page/AdminSection.vue +31 -16
  45. package/src/components/admin-page/AdminStatistics.vue +40 -0
  46. package/src/components/admin-page/AdminTable.vue +175 -0
  47. package/src/components/bundle-editor/BundleCreatePanel.vue +23 -23
  48. package/src/components/bundle-editor/BundleFeaturesEditor.vue +7 -7
  49. package/src/components/bundle-editor/BundlePlanCompatPicker.vue +9 -9
  50. package/src/components/bundle-editor/BundleQuotasEditor.vue +11 -11
  51. package/src/components/bundle-editor/BundleStatusBanner.vue +6 -6
  52. package/src/components/bundle-editor/BundleVersionInlineEditor.vue +18 -18
  53. package/src/components/bundle-editor/BundleVersionStrip.vue +12 -12
  54. package/src/components/dialogs/PilotCreateDialog.vue +12 -12
  55. package/src/components/dialogs/PilotEditDialog.vue +7 -7
  56. package/src/components/dialogs/PromoCodeCreateDialog.vue +8 -8
  57. package/src/components/dialogs/PromoCodeDialogFields.vue +33 -19
  58. package/src/components/dialogs/PromoCodeEditDialog.vue +10 -10
  59. package/src/components/plan/PlanGrid.vue +1 -1
  60. package/src/components/plan-create-dialog/PlanCreateDialog.vue +34 -46
  61. package/src/components/plan-detail/PlanDetail.vue +92 -302
  62. package/src/components/plan-detail/PlanDetailKpis.vue +22 -27
  63. package/src/components/plan-detail/PlanTitleEdit.vue +133 -0
  64. package/src/components/plan-list/PlanList.vue +72 -290
  65. package/src/components/plan-matrix/PlanMatrix.vue +66 -183
  66. package/src/components/plan-review/PlanReview.vue +191 -275
  67. package/src/components/plan-version-editor/PlanCatalogPreview.vue +7 -5
  68. package/src/components/plan-version-editor/PlanVersionEditor.vue +200 -295
  69. package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +23 -55
  70. package/src/index.ts +1 -0
  71. package/src/pages-standard/AdminLayout.vue +34 -10
  72. package/src/pages-standard/AdminManifestErrorPage.vue +79 -9
  73. package/src/pages-standard/AuditPage.vue +53 -72
  74. package/src/pages-standard/BundlesPage.vue +128 -144
  75. package/src/pages-standard/DashboardPage.vue +43 -107
  76. package/src/pages-standard/DiscoveryPage.vue +158 -200
  77. package/src/pages-standard/EmailHistoryPage.vue +98 -88
  78. package/src/pages-standard/MarketingCatalogPage.vue +237 -267
  79. package/src/pages-standard/PilotsPage.vue +44 -60
  80. package/src/pages-standard/PlansPage.vue +230 -105
  81. package/src/pages-standard/PlatformEmailPage.vue +37 -40
  82. package/src/pages-standard/PromoCodeDetailPage.vue +16 -30
  83. package/src/pages-standard/PromoCodesPage.vue +86 -112
  84. package/src/pages-standard/SubscriptionsPage.vue +10 -17
  85. package/src/pages-standard/SuperAdminLoginPage.vue +12 -12
  86. package/src/pages-standard/SuperAdminSetupWizard.vue +20 -17
  87. package/src/pages-standard/TenantDetailPage.vue +45 -41
  88. package/src/pages-standard/TenantsPage.vue +184 -264
  89. package/src/pages-standard/UsersPage.vue +69 -104
  90. package/src/pages-standard/bundles-page/BundleAccordionList.vue +20 -17
  91. package/src/pages-standard/bundles-page/BundlesKpis.vue +13 -22
  92. package/src/pages-standard/bundles-page/BundlesToolbar.vue +6 -10
  93. package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +10 -10
  94. package/src/pages-standard/discovery-page/DiscoveryCapList.vue +5 -5
  95. package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +16 -16
  96. package/src/pages-standard/discovery-page/DiscoveryKpis.vue +31 -27
  97. package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +7 -7
  98. package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +1 -1
  99. package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +2 -13
  100. package/src/pages-standard/plan-versions/PlanDiffCard.vue +6 -6
  101. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +6 -6
  102. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +16 -16
  103. package/src/pages-standard/plans-page/PlanBundleOverview.vue +14 -29
  104. package/src/pages-standard/plans-page/PlanPublishDialog.vue +1 -1
  105. package/src/pages-standard/plans-page/PlansPageToast.vue +1 -1
  106. package/src/pages-standard/sa-theme.css +481 -71
  107. package/src/pages-standard/tenant-detail/TenantUsers.vue +2 -8
  108. package/src/pages-standard/tenants/StatusPill.vue +2 -2
  109. package/src/pages-tenant/MySubscriptionBundlesPage.vue +25 -25
  110. package/src/pages-tenant/OnboardingConfigurator.vue +1 -5
  111. package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
  112. package/src/pages-tenant/TenantPlanSection.vue +12 -20
  113. package/src/quasar/create-super-admin-app.ts +4 -0
  114. package/src/vue/entitlement-provider.ts +0 -1
  115. package/src/vue/feature-router-guard.ts +0 -1
  116. package/src/vue/super-admin-context.ts +106 -1
  117. package/src/vue/use-api-list.ts +2 -1
  118. package/src/vue/use-pagination.ts +63 -0
  119. package/src/vue/use-plans.ts +1 -2
  120. package/src/vue/use-sign-out.ts +90 -0
  121. package/src/vue/use-tenant-billing.ts +1 -1
  122. package/src/vue/use-tenant-manifest.ts +0 -1
  123. package/src/components/KpiCard.vue +0 -53
  124. package/src/components/plan-cockpit/PlanCockpit.vue +0 -1027
  125. package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +0 -72
  126. package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +0 -99
  127. package/src/components/plan-cockpit/PlanCockpitHeader.vue +0 -101
  128. package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +0 -112
  129. package/src/components/plan-cockpit/PlanCockpitKpis.vue +0 -68
  130. package/src/components/plan-cockpit/PlanCockpitVersions.vue +0 -249
  131. package/src/components/plan-cockpit/types.ts +0 -72
  132. package/src/components/plan-detail/PlanDetailHeader.vue +0 -194
@@ -1,72 +0,0 @@
1
- <template>
2
- <AdminSection
3
- v-if="auditRows.length > 0 || loadingAudit"
4
- :title="msg.auditLog.title"
5
- :subtitle="msg.auditLog.subtitle"
6
- class="pc-card pc-audit-card pc-card--audit-head"
7
- >
8
- <div class="pc-audit">
9
- <div v-if="loadingAudit" class="pc-empty pc-empty--inline">
10
- {{ msg.auditLog.loading }}
11
- </div>
12
- <div v-for="a in auditRows" :key="a.id" class="pc-audit-row">
13
- <span :class="['pc-audit-dot', `pc-audit-${auditKind(a.action)}`]" />
14
- <span class="pc-audit-when">{{ formatAuditDate(a.createdAt) }}</span>
15
- <span class="pc-audit-avatar">{{ auditInitial(a) }}</span>
16
- <span class="pc-audit-who">{{ auditWho(a) }}</span>
17
- <span class="pc-audit-what">{{ auditLabel(a) }}</span>
18
- </div>
19
- </div>
20
- </AdminSection>
21
- </template>
22
-
23
- <script setup lang="ts">
24
- import { formatMessage } from '../../client/i18n/format.js';
25
- import AdminSection from '../admin-page/AdminSection.vue';
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,99 +0,0 @@
1
- <template>
2
- <AdminSection
3
- :title="panelTitle"
4
- :subtitle="diffPair ? msg.diff.subOnPublish : msg.diff.noComparison"
5
- class="pc-card"
6
- >
7
- <template v-if="diffPair" #actions>
8
- <div class="pc-pillrow">
9
- <span class="pc-pill" style="background: #ecfdf5; color: #047857">
10
- +{{ diff.addedFeatures.length }} F
11
- </span>
12
- <span class="pc-pill" style="background: #fef2f2; color: #b91c1c">
13
- −{{ diff.removedFeatures.length }} F
14
- </span>
15
- <span class="pc-pill" style="background: #fffbeb; color: #b45309">
16
- {{
17
- diff.changedQuotas.length +
18
- diff.addedQuotas.length +
19
- diff.removedQuotas.length
20
- }}
21
- Q
22
- </span>
23
- </div>
24
- </template>
25
- <div class="pc-diff-body">
26
- <div
27
- v-for="d in diffRows"
28
- :key="d.id"
29
- class="pc-diff-row"
30
- :style="{ background: d.bg, borderColor: d.border }"
31
- >
32
- <span class="pc-diff-sign" :style="{ background: d.color }">{{ d.sign }}</span>
33
- <div class="pc-diff-main">
34
- <div class="pc-diff-headline">
35
- <span class="pc-diff-section" :style="{ color: d.color }">{{
36
- d.section
37
- }}</span>
38
- <span class="pc-diff-label">{{ d.label }}</span>
39
- <code v-if="d.sub" class="pc-mono pc-mono--xs">{{ d.sub }}</code>
40
- </div>
41
- <div v-if="d.from !== undefined" class="pc-diff-change">
42
- <span class="pc-diff-from">{{ d.from }}</span>
43
- <span class="pc-diff-arrow" :style="{ color: d.color }">
44
- <svg
45
- width="12"
46
- height="12"
47
- viewBox="0 0 24 24"
48
- fill="none"
49
- stroke="currentColor"
50
- stroke-width="2"
51
- >
52
- <path d="M5 12h14M13 5l7 7-7 7" />
53
- </svg>
54
- </span>
55
- <span class="pc-diff-to">{{ d.to }}</span>
56
- </div>
57
- </div>
58
- <span
59
- class="pc-chip pc-diff-tag"
60
- :style="{
61
- background: '#fff',
62
- color: d.color,
63
- borderColor: d.border,
64
- }"
65
- >
66
- {{ d.tag }}
67
- </span>
68
- </div>
69
- <div v-if="diffRows.length === 0" class="pc-empty pc-empty--inline">
70
- {{ msg.diff.noChangesDetected }}
71
- </div>
72
- </div>
73
- </AdminSection>
74
- </template>
75
-
76
- <script setup lang="ts">
77
- import { computed } from 'vue';
78
- import AdminSection from '../admin-page/AdminSection.vue';
79
- import { formatMessage } from '../../client/i18n/format.js';
80
- import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
81
- import type { DiffRow, DiffSummary, PlanVersionPair } from './types';
82
-
83
- const props = defineProps<{
84
- diffPair: PlanVersionPair | null;
85
- diff: DiffSummary;
86
- diffRows: DiffRow[];
87
- }>();
88
-
89
- const msg = useSaMessages('planDetail');
90
-
91
- const panelTitle = computed(() => {
92
- const pair = props.diffPair;
93
- if (!pair) return msg.value.diff.title;
94
- return formatMessage(msg.value.diff.titleCompare, {
95
- from: pair.from.version,
96
- to: pair.to.version,
97
- });
98
- });
99
- </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
- <AdminSection
3
- v-if="tenantImpact"
4
- :title="msg.kpis.tenantImpact"
5
- :subtitle="msg.impact.subtitle"
6
- class="pc-card"
7
- >
8
- <div class="pc-impact-strip">
9
- <div class="pc-impact-num">{{ tenantImpactTotal }}</div>
10
- <div class="pc-impact-bars">
11
- <div class="pc-impact-bar">
12
- <span
13
- class="pc-impact-bar-fill"
14
- :style="{
15
- width: pct(tenantImpact.auto, tenantImpactTotal),
16
- background: '#10b981',
17
- }"
18
- />
19
- <span class="pc-impact-bar-label">{{ autoLabel }}</span>
20
- </div>
21
- <div class="pc-impact-bar">
22
- <span
23
- class="pc-impact-bar-fill"
24
- :style="{
25
- width: pct(tenantImpact.review, tenantImpactTotal),
26
- background: '#f59e0b',
27
- }"
28
- />
29
- <span class="pc-impact-bar-label">{{ reviewLabel }}</span>
30
- </div>
31
- <div class="pc-impact-bar">
32
- <span
33
- class="pc-impact-bar-fill"
34
- :style="{
35
- width: pct(tenantImpact.conflict, tenantImpactTotal),
36
- background: '#ef4444',
37
- }"
38
- />
39
- <span class="pc-impact-bar-label">{{ conflictLabel }}</span>
40
- </div>
41
- </div>
42
- </div>
43
- <div
44
- v-if="tenantImpact.examples && tenantImpact.examples.length > 0"
45
- class="pc-impact-tenants"
46
- >
47
- <div v-for="t in tenantImpact.examples" :key="t.name" class="pc-impact-tenant">
48
- <div class="pc-impact-avatar">{{ initialsOf(t.name) }}</div>
49
- <div class="pc-impact-body">
50
- <div class="pc-impact-name">{{ t.name }}</div>
51
- <div class="pc-impact-sub">{{ t.plan }}</div>
52
- </div>
53
- <span :class="['pc-chip', impactStateChip(t.state)]">{{
54
- impactStateLabel(t.state)
55
- }}</span>
56
- </div>
57
- </div>
58
- </AdminSection>
59
- </template>
60
-
61
- <script setup lang="ts">
62
- import { computed } from 'vue';
63
- import AdminSection from '../admin-page/AdminSection.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>