@saasicat/ui-vue 0.21.0 → 0.22.1
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-BY_t9prv.d.cts → catalog-DJcfm5N2.d.cts} +20 -7
- package/dist/{catalog-BY_t9prv.d.ts → catalog-DJcfm5N2.d.ts} +20 -7
- package/dist/{chunk-JN6HZGLV.js → chunk-F7NRM6KI.js} +54 -28
- package/dist/{chunk-7IFKVGZV.js → chunk-LY52T4N4.js} +21 -2
- package/dist/{chunk-OTP3OSSW.js → chunk-XMMFN2WJ.js} +74 -3
- package/dist/client/index.cjs +132 -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 +319 -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 +76 -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-c_Eqsu9u.d.ts → use-super-admin-i18n-CHNdsSlm.d.ts} +34 -7
- package/dist/{use-super-admin-i18n-DlJRGIVq.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 +22 -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/AdminFilters.vue +12 -0
- package/src/components/admin-page/AdminHero.vue +22 -5
- package/src/components/admin-page/AdminKpi.vue +84 -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 +31 -16
- 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/PlanDetail.vue +92 -302
- package/src/components/plan-detail/PlanDetailKpis.vue +22 -27
- package/src/components/plan-detail/PlanTitleEdit.vue +133 -0
- package/src/components/plan-list/PlanList.vue +72 -290
- 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 +34 -10
- package/src/pages-standard/AdminManifestErrorPage.vue +79 -9
- package/src/pages-standard/AuditPage.vue +53 -72
- package/src/pages-standard/BundlesPage.vue +128 -144
- package/src/pages-standard/DashboardPage.vue +43 -107
- package/src/pages-standard/DiscoveryPage.vue +158 -200
- package/src/pages-standard/EmailHistoryPage.vue +98 -88
- package/src/pages-standard/MarketingCatalogPage.vue +237 -267
- package/src/pages-standard/PilotsPage.vue +44 -60
- package/src/pages-standard/PlansPage.vue +230 -105
- package/src/pages-standard/PlatformEmailPage.vue +37 -40
- package/src/pages-standard/PromoCodeDetailPage.vue +16 -30
- package/src/pages-standard/PromoCodesPage.vue +86 -112
- package/src/pages-standard/SubscriptionsPage.vue +10 -17
- package/src/pages-standard/SuperAdminLoginPage.vue +12 -12
- package/src/pages-standard/SuperAdminSetupWizard.vue +20 -17
- package/src/pages-standard/TenantDetailPage.vue +45 -41
- package/src/pages-standard/TenantsPage.vue +184 -264
- package/src/pages-standard/UsersPage.vue +69 -104
- 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 +6 -10
- 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 +1 -1
- package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +2 -13
- 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 +1 -1
- package/src/pages-standard/sa-theme.css +481 -71
- package/src/pages-standard/tenant-detail/TenantUsers.vue +2 -8
- 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-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 -1027
- package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +0 -72
- package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +0 -99
- 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 -249
- package/src/components/plan-cockpit/types.ts +0 -72
- package/src/components/plan-detail/PlanDetailHeader.vue +0 -194
|
@@ -13,115 +13,123 @@
|
|
|
13
13
|
</template>
|
|
14
14
|
</AdminHero>
|
|
15
15
|
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
:available-features="snapshot?.features ?? []"
|
|
43
|
-
:available-quotas="snapshot?.quotas ?? []"
|
|
44
|
-
:plans="plans"
|
|
45
|
-
:live-plan-versions="livePlanVersions"
|
|
46
|
-
:feature-registry="featureRegistryResolved"
|
|
47
|
-
:quota-registry="quotaRegistryResolved"
|
|
48
|
-
:existing-bundle-keys="existingBundleKeys"
|
|
49
|
-
:create="create"
|
|
50
|
-
:create-draft="createDraft"
|
|
51
|
-
@cancel="createOpen = false"
|
|
52
|
-
@created="onWizardCreated"
|
|
53
|
-
/>
|
|
16
|
+
<AdminBody>
|
|
17
|
+
<q-banner v-if="error" class="sa-bundles__error" rounded>
|
|
18
|
+
<template #avatar><q-icon name="warning" color="negative" /></template>
|
|
19
|
+
{{ loadErrorText }}
|
|
20
|
+
</q-banner>
|
|
21
|
+
|
|
22
|
+
<AdminSection>
|
|
23
|
+
<BundlesKpis
|
|
24
|
+
:bundles-total="bundles.length"
|
|
25
|
+
:live-count="liveCount"
|
|
26
|
+
:scheduled-bundles-count="scheduledBundlesCount"
|
|
27
|
+
:total-scheduled-versions="totalScheduledVersions"
|
|
28
|
+
:total-draft-versions="totalDraftVersions"
|
|
29
|
+
:draft-bundles-count="draftBundlesCount"
|
|
30
|
+
:translated-count="translatedCount"
|
|
31
|
+
:locales-count="locales.length"
|
|
32
|
+
/>
|
|
33
|
+
</AdminSection>
|
|
34
|
+
|
|
35
|
+
<AdminSection class="sa-bundles__filter">
|
|
36
|
+
<BundlesFilterBar
|
|
37
|
+
v-model:query="query"
|
|
38
|
+
v-model:status-filter="statusFilter"
|
|
39
|
+
class="q-mb-md"
|
|
40
|
+
:status-filter-options="statusFilterOptions"
|
|
41
|
+
/>
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<template #avatar><q-icon name="info" color="info" /></template>
|
|
61
|
-
{{ msg.page.emptyBefore }} <strong>{{ msg.header.newBundle }}</strong>
|
|
62
|
-
{{ msg.page.emptyAfter }}
|
|
63
|
-
</q-banner>
|
|
64
|
-
|
|
65
|
-
<BundleAccordionList
|
|
66
|
-
:filtered-bundles="filteredBundles"
|
|
67
|
-
:bundles-total="bundles.length"
|
|
68
|
-
:open-key="openKey"
|
|
69
|
-
:aggregate-status-of="aggregateStatusOf"
|
|
70
|
-
:i18n-locale-count="i18nLocaleCount"
|
|
71
|
-
@toggle="toggle"
|
|
72
|
-
>
|
|
73
|
-
<template #detail="{ bundle }">
|
|
74
|
-
<BundleDetailPanel
|
|
75
|
-
:bundle="bundle"
|
|
76
|
-
v-model:edit-form="editForm"
|
|
77
|
-
:i18n-draft="i18nDraft"
|
|
78
|
-
:translatable-locales="translatableLocales"
|
|
79
|
-
:edit-submitting="editSubmitting"
|
|
80
|
-
:detail-versions="detailVersions"
|
|
81
|
-
:selected-version-id="selectedVersionIdByBundle[bundle.id] ?? null"
|
|
82
|
-
:selected-version="selectedVersion"
|
|
43
|
+
<!-- Inline creation (collapsible panel, replaces the former dialog modal) -->
|
|
44
|
+
<BundleCreatePanel
|
|
45
|
+
v-if="createOpen"
|
|
46
|
+
class="q-mb-sm"
|
|
47
|
+
:project-key="projectKey"
|
|
83
48
|
:available-features="snapshot?.features ?? []"
|
|
84
49
|
:available-quotas="snapshot?.quotas ?? []"
|
|
85
50
|
:plans="plans"
|
|
86
51
|
:live-plan-versions="livePlanVersions"
|
|
87
52
|
:feature-registry="featureRegistryResolved"
|
|
88
53
|
:quota-registry="quotaRegistryResolved"
|
|
89
|
-
:
|
|
90
|
-
:
|
|
91
|
-
|
|
92
|
-
@
|
|
93
|
-
@
|
|
94
|
-
@add-version="onAddVersion"
|
|
95
|
-
@inline-save="onInlineSave"
|
|
96
|
-
@discard-version="onDiscardVersion"
|
|
97
|
-
@publish-version="openPublish"
|
|
98
|
-
@delete-bundle="confirmDelete"
|
|
54
|
+
:existing-bundle-keys="existingBundleKeys"
|
|
55
|
+
:create="create"
|
|
56
|
+
:create-draft="createDraft"
|
|
57
|
+
@cancel="createOpen = false"
|
|
58
|
+
@created="onWizardCreated"
|
|
99
59
|
/>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
60
|
+
|
|
61
|
+
<q-banner
|
|
62
|
+
v-if="bundles.length === 0 && !loading && !error"
|
|
63
|
+
class="sa-bundles__empty"
|
|
64
|
+
rounded
|
|
65
|
+
>
|
|
66
|
+
<template #avatar><q-icon name="info" color="info" /></template>
|
|
67
|
+
{{ msg.page.emptyBefore }} <strong>{{ msg.header.newBundle }}</strong>
|
|
68
|
+
{{ msg.page.emptyAfter }}
|
|
69
|
+
</q-banner>
|
|
70
|
+
|
|
71
|
+
<BundleAccordionList
|
|
72
|
+
:filtered-bundles="filteredBundles"
|
|
73
|
+
:bundles-total="bundles.length"
|
|
74
|
+
:open-key="openKey"
|
|
75
|
+
:aggregate-status-of="aggregateStatusOf"
|
|
76
|
+
:i18n-locale-count="i18nLocaleCount"
|
|
77
|
+
@toggle="toggle"
|
|
78
|
+
>
|
|
79
|
+
<template #detail="{ bundle }">
|
|
80
|
+
<BundleDetailPanel
|
|
81
|
+
v-model:edit-form="editForm"
|
|
82
|
+
:bundle="bundle"
|
|
83
|
+
:i18n-draft="i18nDraft"
|
|
84
|
+
:translatable-locales="translatableLocales"
|
|
85
|
+
:edit-submitting="editSubmitting"
|
|
86
|
+
:detail-versions="detailVersions"
|
|
87
|
+
:selected-version-id="selectedVersionIdByBundle[bundle.id] ?? null"
|
|
88
|
+
:selected-version="selectedVersion"
|
|
89
|
+
:available-features="snapshot?.features ?? []"
|
|
90
|
+
:available-quotas="snapshot?.quotas ?? []"
|
|
91
|
+
:plans="plans"
|
|
92
|
+
:live-plan-versions="livePlanVersions"
|
|
93
|
+
:feature-registry="featureRegistryResolved"
|
|
94
|
+
:quota-registry="quotaRegistryResolved"
|
|
95
|
+
:inline-editor-saving="inlineEditorSaving"
|
|
96
|
+
:inline-editor-error="inlineEditorError"
|
|
97
|
+
@set-i18n="setI18n"
|
|
98
|
+
@submit-edit="submitEdit"
|
|
99
|
+
@select-version="onSelectVersion"
|
|
100
|
+
@add-version="onAddVersion"
|
|
101
|
+
@inline-save="onInlineSave"
|
|
102
|
+
@discard-version="onDiscardVersion"
|
|
103
|
+
@publish-version="openPublish"
|
|
104
|
+
@delete-bundle="confirmDelete"
|
|
105
|
+
/>
|
|
106
|
+
</template>
|
|
107
|
+
</BundleAccordionList>
|
|
108
|
+
</AdminSection>
|
|
109
|
+
|
|
110
|
+
<!-- Strict mode warnings after the last mutation -->
|
|
111
|
+
<q-banner
|
|
112
|
+
v-if="lastWarnings.length > 0"
|
|
113
|
+
class="sa-bundles__warnings-banner"
|
|
114
|
+
inline-actions
|
|
115
|
+
rounded
|
|
116
|
+
>
|
|
117
|
+
<template #avatar><q-icon name="warning" color="warning" /></template>
|
|
118
|
+
<strong>{{ strictWarningsText }}</strong>
|
|
119
|
+
<ul class="sa-bundles__warnings-list">
|
|
120
|
+
<li v-for="(w, i) in lastWarnings" :key="i">
|
|
121
|
+
<code>{{ w.code }}</code>
|
|
122
|
+
<template v-if="w.value">
|
|
123
|
+
· <code>{{ w.value }}</code></template
|
|
124
|
+
>
|
|
125
|
+
— {{ w.message }}
|
|
126
|
+
</li>
|
|
127
|
+
</ul>
|
|
128
|
+
<template #action>
|
|
129
|
+
<q-btn flat dense :label="common.close" @click="lastWarnings = []" />
|
|
130
|
+
</template>
|
|
131
|
+
</q-banner>
|
|
132
|
+
</AdminBody>
|
|
125
133
|
|
|
126
134
|
<!-- Publish confirmation modal -->
|
|
127
135
|
<BundleVersionPublishDialog
|
|
@@ -178,7 +186,9 @@ import { useSaMessages } from '../vue/use-super-admin-i18n.js';
|
|
|
178
186
|
import BundleAccordionList from './bundles-page/BundleAccordionList.vue';
|
|
179
187
|
import BundleDetailPanel from './bundles-page/BundleDetailPanel.vue';
|
|
180
188
|
import BundlesFilterBar from './bundles-page/BundlesFilterBar.vue';
|
|
189
|
+
import AdminBody from '../components/admin-page/AdminBody.vue';
|
|
181
190
|
import AdminHero from '../components/admin-page/AdminHero.vue';
|
|
191
|
+
import AdminSection from '../components/admin-page/AdminSection.vue';
|
|
182
192
|
import AdminPage from '../components/admin-page/AdminPage.vue';
|
|
183
193
|
import BundlesToolbar from './bundles-page/BundlesToolbar.vue';
|
|
184
194
|
import BundlesKpis from './bundles-page/BundlesKpis.vue';
|
|
@@ -188,7 +198,7 @@ import type {
|
|
|
188
198
|
BundlesStatusFilterOption,
|
|
189
199
|
} from './bundles-page/types.js';
|
|
190
200
|
|
|
191
|
-
// Platform standard page: Bundles (
|
|
201
|
+
// Platform standard page: Bundles (§11.1 M6 Pack 2c +
|
|
192
202
|
// Pack 2d inline editor after plan simulation). Dumb component —
|
|
193
203
|
// the consumer wrapper supplies the composable results + plan-root
|
|
194
204
|
// list + live PlanVersion index as props.
|
|
@@ -659,32 +669,6 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
659
669
|
.sa-bundles__empty {
|
|
660
670
|
border-left: 4px solid #31ccec;
|
|
661
671
|
}
|
|
662
|
-
.sa-bundles__kpis {
|
|
663
|
-
display: grid;
|
|
664
|
-
/* Responsive: as many KPIs as fit (up to 4), then 3 → 2 → 1. */
|
|
665
|
-
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
666
|
-
gap: 10px;
|
|
667
|
-
}
|
|
668
|
-
.sa-bundles__kpi {
|
|
669
|
-
background: #fff;
|
|
670
|
-
border: 1px solid #e2e8f0;
|
|
671
|
-
border-radius: 10px;
|
|
672
|
-
padding: 12px 14px;
|
|
673
|
-
}
|
|
674
|
-
.sa-bundles__kpi-label {
|
|
675
|
-
font-size: 10px;
|
|
676
|
-
text-transform: uppercase;
|
|
677
|
-
letter-spacing: 0.5px;
|
|
678
|
-
color: #64748b;
|
|
679
|
-
}
|
|
680
|
-
.sa-bundles__kpi-value {
|
|
681
|
-
font-size: 26px;
|
|
682
|
-
font-weight: 700;
|
|
683
|
-
}
|
|
684
|
-
.sa-bundles__kpi-sub {
|
|
685
|
-
font-size: 11px;
|
|
686
|
-
color: #94a3b8;
|
|
687
|
-
}
|
|
688
672
|
.sa-bundles__filter-row {
|
|
689
673
|
display: flex;
|
|
690
674
|
align-items: center;
|
|
@@ -705,7 +689,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
705
689
|
}
|
|
706
690
|
.sa-bd-card {
|
|
707
691
|
background: #fff;
|
|
708
|
-
border: 1px solid
|
|
692
|
+
border: 1px solid var(--sa-border);
|
|
709
693
|
border-radius: 10px;
|
|
710
694
|
overflow: hidden;
|
|
711
695
|
}
|
|
@@ -723,8 +707,8 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
723
707
|
width: 34px;
|
|
724
708
|
height: 34px;
|
|
725
709
|
border-radius: 8px;
|
|
726
|
-
background:
|
|
727
|
-
color:
|
|
710
|
+
background: var(--sa-primary-50);
|
|
711
|
+
color: var(--sa-primary-strong);
|
|
728
712
|
display: flex;
|
|
729
713
|
align-items: center;
|
|
730
714
|
justify-content: center;
|
|
@@ -751,19 +735,19 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
751
735
|
}
|
|
752
736
|
.sa-bd-card__desc {
|
|
753
737
|
font-size: 12px;
|
|
754
|
-
color:
|
|
738
|
+
color: var(--sa-muted);
|
|
755
739
|
}
|
|
756
740
|
.sa-bd-card__chev {
|
|
757
741
|
transition: transform 0.15s;
|
|
758
|
-
color:
|
|
742
|
+
color: var(--sa-muted-light);
|
|
759
743
|
}
|
|
760
744
|
.sa-bd-card__chev.open {
|
|
761
745
|
transform: rotate(90deg);
|
|
762
746
|
}
|
|
763
747
|
.sa-bd-card__body {
|
|
764
|
-
border-top: 1px solid
|
|
748
|
+
border-top: 1px solid var(--sa-border);
|
|
765
749
|
padding: 14px;
|
|
766
|
-
background:
|
|
750
|
+
background: var(--sa-bg-surface-2);
|
|
767
751
|
}
|
|
768
752
|
.sa-bd-grid {
|
|
769
753
|
display: grid;
|
|
@@ -794,7 +778,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
794
778
|
font-weight: 700;
|
|
795
779
|
text-transform: uppercase;
|
|
796
780
|
letter-spacing: 0.5px;
|
|
797
|
-
color:
|
|
781
|
+
color: var(--sa-muted-dark);
|
|
798
782
|
display: flex;
|
|
799
783
|
justify-content: space-between;
|
|
800
784
|
}
|
|
@@ -802,7 +786,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
802
786
|
margin-top: 10px;
|
|
803
787
|
}
|
|
804
788
|
.sa-bd-section-count {
|
|
805
|
-
color:
|
|
789
|
+
color: var(--sa-muted-light);
|
|
806
790
|
font-weight: 600;
|
|
807
791
|
}
|
|
808
792
|
.sa-bundles__form {
|
|
@@ -812,14 +796,14 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
812
796
|
}
|
|
813
797
|
.sa-bd-i18n-hint {
|
|
814
798
|
font-size: 11px;
|
|
815
|
-
color:
|
|
799
|
+
color: var(--sa-muted-light);
|
|
816
800
|
}
|
|
817
801
|
.sa-bd-i18n-block {
|
|
818
802
|
display: flex;
|
|
819
803
|
flex-direction: column;
|
|
820
804
|
gap: 6px;
|
|
821
805
|
padding: 8px;
|
|
822
|
-
border: 1px solid
|
|
806
|
+
border: 1px solid var(--sa-border);
|
|
823
807
|
border-radius: 8px;
|
|
824
808
|
background: #fff;
|
|
825
809
|
}
|
|
@@ -831,7 +815,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
831
815
|
.sa-bd-i18n-code {
|
|
832
816
|
font-size: 10px;
|
|
833
817
|
font-weight: 700;
|
|
834
|
-
background:
|
|
818
|
+
background: var(--sa-border-soft);
|
|
835
819
|
padding: 2px 7px;
|
|
836
820
|
border-radius: 5px;
|
|
837
821
|
}
|
|
@@ -852,7 +836,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
852
836
|
gap: 10px;
|
|
853
837
|
align-items: flex-start;
|
|
854
838
|
background: #fff;
|
|
855
|
-
border: 1px solid
|
|
839
|
+
border: 1px solid var(--sa-border);
|
|
856
840
|
border-radius: 8px;
|
|
857
841
|
padding: 10px;
|
|
858
842
|
}
|
|
@@ -867,7 +851,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
867
851
|
}
|
|
868
852
|
.sa-bd-version__sub {
|
|
869
853
|
font-size: 11px;
|
|
870
|
-
color:
|
|
854
|
+
color: var(--sa-muted);
|
|
871
855
|
margin: 2px 0;
|
|
872
856
|
}
|
|
873
857
|
.sa-bd-version__feats {
|
|
@@ -877,7 +861,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
877
861
|
}
|
|
878
862
|
.sa-bd-feat-chip {
|
|
879
863
|
font-size: 10px;
|
|
880
|
-
background:
|
|
864
|
+
background: var(--sa-border-soft);
|
|
881
865
|
padding: 2px 6px;
|
|
882
866
|
border-radius: 5px;
|
|
883
867
|
}
|
|
@@ -888,7 +872,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
888
872
|
.sa-bd-empty-row {
|
|
889
873
|
padding: 20px;
|
|
890
874
|
text-align: center;
|
|
891
|
-
color:
|
|
875
|
+
color: var(--sa-muted-light);
|
|
892
876
|
font-size: 12px;
|
|
893
877
|
border: 1px dashed #cbd5e1;
|
|
894
878
|
border-radius: 8px;
|
|
@@ -2,14 +2,7 @@
|
|
|
2
2
|
<AdminPage class="sa-dashboard">
|
|
3
3
|
<AdminHero :title="msg.title" :subtitle="subtitle">
|
|
4
4
|
<template #actions>
|
|
5
|
-
<
|
|
6
|
-
flat
|
|
7
|
-
dense
|
|
8
|
-
icon="refresh"
|
|
9
|
-
:loading="loading"
|
|
10
|
-
:aria-label="common.reload"
|
|
11
|
-
@click="reload"
|
|
12
|
-
/>
|
|
5
|
+
<AdminRefreshBtn :loading="loading" @refresh="reload" />
|
|
13
6
|
</template>
|
|
14
7
|
</AdminHero>
|
|
15
8
|
|
|
@@ -25,30 +18,28 @@
|
|
|
25
18
|
{{ msg.emptyKpiCards }}
|
|
26
19
|
</div>
|
|
27
20
|
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
<AdminSection v-else>
|
|
22
|
+
<AdminStatistics :label="msg.title">
|
|
23
|
+
<AdminKpi
|
|
24
|
+
v-for="card in cards"
|
|
25
|
+
:key="card.id"
|
|
26
|
+
:data-card-id="card.id"
|
|
27
|
+
:label="card.label"
|
|
28
|
+
:sub="card.sub"
|
|
29
|
+
:icon="card.displayHint?.icon"
|
|
30
|
+
layout="inline"
|
|
31
|
+
>
|
|
32
|
+
<template #value>
|
|
36
33
|
<q-spinner v-if="card.loading" size="18px" />
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
</
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</div>
|
|
34
|
+
<template v-else-if="card.error">—</template>
|
|
35
|
+
<template v-else>{{ formatValue(card) }}</template>
|
|
36
|
+
</template>
|
|
37
|
+
</AdminKpi>
|
|
38
|
+
</AdminStatistics>
|
|
39
|
+
</AdminSection>
|
|
44
40
|
|
|
45
41
|
<div v-if="distributions && distributions.length > 0" class="sa-dashboard__rows">
|
|
46
|
-
<AdminSection
|
|
47
|
-
v-for="dist in distributions"
|
|
48
|
-
:key="dist.id"
|
|
49
|
-
:title="dist.label"
|
|
50
|
-
class="sa-card sa-dashboard__row-card"
|
|
51
|
-
>
|
|
42
|
+
<AdminSection v-for="dist in distributions" :key="dist.id" :title="dist.label">
|
|
52
43
|
<template v-if="dist.total" #actions>
|
|
53
44
|
<span class="sa-dashboard__count">{{ dist.total }}</span>
|
|
54
45
|
</template>
|
|
@@ -77,7 +68,7 @@
|
|
|
77
68
|
<AdminSection
|
|
78
69
|
v-if="resolvedShortcuts.length > 0"
|
|
79
70
|
:title="msg.shortcutsTitle"
|
|
80
|
-
class="sa-
|
|
71
|
+
class="sa-dashboard__shortcuts"
|
|
81
72
|
>
|
|
82
73
|
<div class="sa-dashboard__shortcut-grid">
|
|
83
74
|
<a
|
|
@@ -108,9 +99,12 @@ import { formatMessage } from '../client/i18n/format.js';
|
|
|
108
99
|
import { SUPER_ADMIN_MANIFEST_KEY } from '../vue/super-admin-context.js';
|
|
109
100
|
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
110
101
|
import { useSuperAdminHttp } from '../vue/use-super-admin-context.js';
|
|
102
|
+
import AdminRefreshBtn from '../components/admin-page/AdminRefreshBtn.vue';
|
|
111
103
|
import AdminHero from '../components/admin-page/AdminHero.vue';
|
|
104
|
+
import AdminKpi from '../components/admin-page/AdminKpi.vue';
|
|
112
105
|
import AdminPage from '../components/admin-page/AdminPage.vue';
|
|
113
106
|
import AdminSection from '../components/admin-page/AdminSection.vue';
|
|
107
|
+
import AdminStatistics from '../components/admin-page/AdminStatistics.vue';
|
|
114
108
|
|
|
115
109
|
// Platform standard page: Dashboard.
|
|
116
110
|
//
|
|
@@ -336,17 +330,22 @@ function defaultFormat(card: KpiCardDef, body: unknown): KpiFormatted {
|
|
|
336
330
|
};
|
|
337
331
|
}
|
|
338
332
|
|
|
339
|
-
|
|
333
|
+
// Every KPI request goes through an `HttpClient`. `useSuperAdminHttp()` always
|
|
334
|
+
// resolves — to the client the app registered, or to `defaultHttpClient()` —
|
|
335
|
+
// so there is no path on which a card could reach the network without the
|
|
336
|
+
// app's auth. A bare `fetch()` here used to silently drop it.
|
|
337
|
+
//
|
|
338
|
+
// The token rides along as a header regardless of which client transports it,
|
|
339
|
+
// the same way `useApiList` and the other standard pages do it. The two are
|
|
340
|
+
// orthogonal, and a page that mounted with the documented `getAuthToken` but
|
|
341
|
+
// no `http` of its own would otherwise send every KPI request unauthenticated
|
|
342
|
+
// and render an error in all of its cards.
|
|
343
|
+
function callHttp(url: string): Promise<{ status: number; json: () => Promise<unknown> }> {
|
|
340
344
|
const http = props.http ?? shellHttp;
|
|
341
|
-
if (http) return http(url, { method: 'GET' });
|
|
342
345
|
const headers: Record<string, string> = {};
|
|
343
346
|
const token = props.getAuthToken?.();
|
|
344
347
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
345
|
-
|
|
346
|
-
return {
|
|
347
|
-
status: resp.status,
|
|
348
|
-
json: () => resp.json(),
|
|
349
|
-
};
|
|
348
|
+
return http(url, { method: 'GET', headers });
|
|
350
349
|
}
|
|
351
350
|
|
|
352
351
|
function extractValue(body: Record<string, unknown>): string | number | null {
|
|
@@ -401,73 +400,10 @@ function barWidth(value: number, max?: number): number {
|
|
|
401
400
|
|
|
402
401
|
<style scoped>
|
|
403
402
|
/* Header look comes from the global .sa-page-head — only a margin tweak here. */
|
|
404
|
-
.sa-dashboard__strip {
|
|
405
|
-
display: grid;
|
|
406
|
-
/* Responsive: 4 cards while there is space, then automatically 3 → 2 → 1. */
|
|
407
|
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
408
|
-
gap: 12px;
|
|
409
|
-
margin-bottom: 14px;
|
|
410
|
-
}
|
|
411
403
|
|
|
412
|
-
.sa-kpi {
|
|
413
|
-
background: #fff;
|
|
414
|
-
border: 1px solid var(--sa-border, #e2e8f0);
|
|
415
|
-
border-radius: 12px;
|
|
416
|
-
padding: 14px 16px;
|
|
417
|
-
display: flex;
|
|
418
|
-
align-items: center;
|
|
419
|
-
gap: 12px;
|
|
420
|
-
transition:
|
|
421
|
-
border-color 0.12s,
|
|
422
|
-
box-shadow 0.12s;
|
|
423
|
-
}
|
|
424
|
-
.sa-kpi:hover {
|
|
425
|
-
border-color: #94a3b8;
|
|
426
|
-
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
|
|
427
|
-
}
|
|
428
|
-
.sa-kpi__icon {
|
|
429
|
-
width: 36px;
|
|
430
|
-
height: 36px;
|
|
431
|
-
border-radius: 9px;
|
|
432
|
-
background: var(--sa-primary-soft, rgba(63, 107, 255, 0.08));
|
|
433
|
-
display: flex;
|
|
434
|
-
align-items: center;
|
|
435
|
-
justify-content: center;
|
|
436
|
-
}
|
|
437
404
|
.sa-kpi__icon :deep(.q-icon) {
|
|
438
405
|
color: var(--sa-primary, #3f6bff);
|
|
439
406
|
}
|
|
440
|
-
.sa-kpi__body {
|
|
441
|
-
flex: 1;
|
|
442
|
-
min-width: 0;
|
|
443
|
-
}
|
|
444
|
-
.sa-kpi__label {
|
|
445
|
-
font-size: 11.5px;
|
|
446
|
-
font-weight: 600;
|
|
447
|
-
letter-spacing: 0.06em;
|
|
448
|
-
color: var(--sa-muted, #64748b);
|
|
449
|
-
text-transform: uppercase;
|
|
450
|
-
}
|
|
451
|
-
.sa-kpi__value {
|
|
452
|
-
font-family: var(--sa-font-head, system-ui, sans-serif);
|
|
453
|
-
font-weight: 700;
|
|
454
|
-
font-size: 28px;
|
|
455
|
-
color: var(--sa-heading, #0f172a);
|
|
456
|
-
line-height: 1;
|
|
457
|
-
letter-spacing: -0.02em;
|
|
458
|
-
margin-top: 4px;
|
|
459
|
-
min-height: 30px;
|
|
460
|
-
display: flex;
|
|
461
|
-
align-items: center;
|
|
462
|
-
}
|
|
463
|
-
.sa-kpi__sub {
|
|
464
|
-
font-size: 11.5px;
|
|
465
|
-
color: #94a3b8;
|
|
466
|
-
margin-top: 4px;
|
|
467
|
-
}
|
|
468
|
-
.sa-kpi__error {
|
|
469
|
-
color: var(--sa-negative, #dc2626);
|
|
470
|
-
}
|
|
471
407
|
|
|
472
408
|
.sa-dashboard__rows {
|
|
473
409
|
display: grid;
|
|
@@ -508,10 +444,10 @@ function barWidth(value: number, max?: number): number {
|
|
|
508
444
|
}
|
|
509
445
|
.sa-dashboard__bar-name {
|
|
510
446
|
font-weight: 600;
|
|
511
|
-
color: var(--sa-heading,
|
|
447
|
+
color: var(--sa-heading, var(--sa-heading));
|
|
512
448
|
}
|
|
513
449
|
.sa-dashboard__bar-track {
|
|
514
|
-
background:
|
|
450
|
+
background: var(--sa-border-soft);
|
|
515
451
|
height: 8px;
|
|
516
452
|
border-radius: 999px;
|
|
517
453
|
overflow: hidden;
|
|
@@ -525,7 +461,7 @@ function barWidth(value: number, max?: number): number {
|
|
|
525
461
|
.sa-dashboard__bar-count {
|
|
526
462
|
font-variant-numeric: tabular-nums;
|
|
527
463
|
text-align: right;
|
|
528
|
-
color: var(--sa-muted-dark,
|
|
464
|
+
color: var(--sa-muted-dark, var(--sa-muted-dark));
|
|
529
465
|
}
|
|
530
466
|
|
|
531
467
|
.sa-dashboard__shortcuts {
|
|
@@ -541,7 +477,7 @@ function barWidth(value: number, max?: number): number {
|
|
|
541
477
|
gap: 12px;
|
|
542
478
|
align-items: flex-start;
|
|
543
479
|
padding: 12px 14px;
|
|
544
|
-
border: 1px solid var(--sa-border,
|
|
480
|
+
border: 1px solid var(--sa-border, var(--sa-border));
|
|
545
481
|
border-radius: 10px;
|
|
546
482
|
text-decoration: none;
|
|
547
483
|
color: inherit;
|
|
@@ -558,19 +494,19 @@ function barWidth(value: number, max?: number): number {
|
|
|
558
494
|
}
|
|
559
495
|
.sa-dashboard__shortcut-title {
|
|
560
496
|
font-weight: 700;
|
|
561
|
-
color: var(--sa-heading,
|
|
497
|
+
color: var(--sa-heading, var(--sa-heading));
|
|
562
498
|
font-family: var(--sa-font-head, system-ui, sans-serif);
|
|
563
499
|
}
|
|
564
500
|
.sa-dashboard__shortcut-sub {
|
|
565
501
|
font-size: 12px;
|
|
566
|
-
color: var(--sa-muted,
|
|
502
|
+
color: var(--sa-muted, var(--sa-muted));
|
|
567
503
|
margin-top: 2px;
|
|
568
504
|
}
|
|
569
505
|
|
|
570
506
|
.sa-dashboard__loading,
|
|
571
507
|
.sa-dashboard__empty {
|
|
572
508
|
padding: 32px 28px;
|
|
573
|
-
color: var(--sa-muted,
|
|
509
|
+
color: var(--sa-muted, var(--sa-muted));
|
|
574
510
|
display: flex;
|
|
575
511
|
align-items: center;
|
|
576
512
|
gap: 12px;
|