@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.
Files changed (145) hide show
  1. package/dist/{catalog-JXdKdIqG.d.cts → catalog-DJcfm5N2.d.cts} +21 -7
  2. package/dist/{catalog-JXdKdIqG.d.ts → catalog-DJcfm5N2.d.ts} +21 -7
  3. package/dist/{chunk-DPTIOQXX.js → chunk-F7NRM6KI.js} +56 -28
  4. package/dist/{chunk-RVG6LQHL.js → chunk-LY52T4N4.js} +21 -2
  5. package/dist/{chunk-ORX3ZG47.js → chunk-XMMFN2WJ.js} +74 -3
  6. package/dist/client/index.cjs +134 -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 +321 -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 +78 -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-BlqcD0N7.d.ts → use-super-admin-i18n-CHNdsSlm.d.ts} +34 -7
  21. package/dist/{use-super-admin-i18n-1nCOkvgi.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 +24 -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/AdminBody.vue +40 -0
  40. package/src/components/admin-page/AdminFilters.vue +12 -0
  41. package/src/components/admin-page/AdminHero.vue +49 -0
  42. package/src/components/admin-page/AdminKpi.vue +84 -0
  43. package/src/components/admin-page/AdminPage.vue +16 -0
  44. package/src/components/admin-page/AdminPaginator.vue +195 -0
  45. package/src/components/admin-page/AdminRefreshBtn.vue +39 -0
  46. package/src/components/admin-page/AdminSection.vue +53 -0
  47. package/src/components/admin-page/AdminStatistics.vue +40 -0
  48. package/src/components/admin-page/AdminTable.vue +175 -0
  49. package/src/components/bundle-editor/BundleCreatePanel.vue +23 -23
  50. package/src/components/bundle-editor/BundleFeaturesEditor.vue +7 -7
  51. package/src/components/bundle-editor/BundlePlanCompatPicker.vue +9 -9
  52. package/src/components/bundle-editor/BundleQuotasEditor.vue +11 -11
  53. package/src/components/bundle-editor/BundleStatusBanner.vue +6 -6
  54. package/src/components/bundle-editor/BundleVersionInlineEditor.vue +18 -18
  55. package/src/components/bundle-editor/BundleVersionStrip.vue +12 -12
  56. package/src/components/dialogs/PilotCreateDialog.vue +12 -12
  57. package/src/components/dialogs/PilotEditDialog.vue +7 -7
  58. package/src/components/dialogs/PromoCodeCreateDialog.vue +8 -8
  59. package/src/components/dialogs/PromoCodeDialogFields.vue +33 -19
  60. package/src/components/dialogs/PromoCodeEditDialog.vue +10 -10
  61. package/src/components/plan/PlanGrid.vue +1 -1
  62. package/src/components/plan-create-dialog/PlanCreateDialog.vue +34 -46
  63. package/src/components/plan-detail/PlanAuditLog.vue +7 -8
  64. package/src/components/plan-detail/PlanDetail.vue +95 -292
  65. package/src/components/plan-detail/PlanDetailKpis.vue +22 -27
  66. package/src/components/plan-detail/PlanTitleEdit.vue +133 -0
  67. package/src/components/plan-detail/PlanVersionDiffPanel.vue +5 -8
  68. package/src/components/plan-detail/PlanVersionsPanel.vue +9 -11
  69. package/src/components/plan-list/PlanList.vue +206 -457
  70. package/src/components/plan-matrix/PlanMatrix.vue +66 -183
  71. package/src/components/plan-review/PlanReview.vue +191 -275
  72. package/src/components/plan-version-editor/PlanCatalogPreview.vue +7 -5
  73. package/src/components/plan-version-editor/PlanVersionEditor.vue +200 -295
  74. package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +23 -55
  75. package/src/index.ts +1 -0
  76. package/src/pages-standard/AdminLayout.vue +38 -12
  77. package/src/pages-standard/AdminManifestErrorPage.vue +79 -9
  78. package/src/pages-standard/AuditPage.vue +65 -109
  79. package/src/pages-standard/BundlesPage.vue +146 -179
  80. package/src/pages-standard/DashboardPage.vue +59 -154
  81. package/src/pages-standard/DiscoveryPage.vue +172 -227
  82. package/src/pages-standard/EmailHistoryPage.vue +118 -159
  83. package/src/pages-standard/MarketingCatalogPage.vue +399 -440
  84. package/src/pages-standard/PilotsPage.vue +69 -117
  85. package/src/pages-standard/PlansPage.vue +240 -112
  86. package/src/pages-standard/PlatformEmailPage.vue +56 -91
  87. package/src/pages-standard/PromoCodeDetailPage.vue +49 -74
  88. package/src/pages-standard/PromoCodesPage.vue +95 -128
  89. package/src/pages-standard/SubscriptionsPage.vue +19 -52
  90. package/src/pages-standard/SuperAdminLoginPage.vue +12 -12
  91. package/src/pages-standard/SuperAdminSetupWizard.vue +22 -18
  92. package/src/pages-standard/TenantDetailPage.vue +100 -191
  93. package/src/pages-standard/TenantsPage.vue +190 -295
  94. package/src/pages-standard/UsersPage.vue +89 -157
  95. package/src/pages-standard/bundles-page/BundleAccordionList.vue +20 -17
  96. package/src/pages-standard/bundles-page/BundlesKpis.vue +13 -22
  97. package/src/pages-standard/bundles-page/BundlesToolbar.vue +63 -0
  98. package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +10 -10
  99. package/src/pages-standard/discovery-page/DiscoveryCapList.vue +5 -5
  100. package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +16 -16
  101. package/src/pages-standard/discovery-page/DiscoveryKpis.vue +31 -27
  102. package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +7 -7
  103. package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +111 -73
  104. package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +43 -62
  105. package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +53 -35
  106. package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +7 -7
  107. package/src/pages-standard/plan-versions/PlanDiffCard.vue +6 -6
  108. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +6 -6
  109. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +16 -16
  110. package/src/pages-standard/plans-page/PlanBundleOverview.vue +14 -29
  111. package/src/pages-standard/plans-page/PlanPublishDialog.vue +1 -1
  112. package/src/pages-standard/plans-page/PlansPageToast.vue +6 -6
  113. package/src/pages-standard/sa-theme.css +552 -86
  114. package/src/pages-standard/tenant-detail/TenantMasterData.vue +28 -0
  115. package/src/pages-standard/tenant-detail/TenantUsage.vue +31 -0
  116. package/src/pages-standard/tenant-detail/TenantUsers.vue +15 -0
  117. package/src/pages-standard/tenant-detail/types.ts +38 -0
  118. package/src/pages-standard/tenants/StatusPill.vue +2 -2
  119. package/src/pages-tenant/MySubscriptionBundlesPage.vue +25 -25
  120. package/src/pages-tenant/OnboardingConfigurator.vue +1 -5
  121. package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
  122. package/src/pages-tenant/TenantPlanSection.vue +12 -20
  123. package/src/quasar/create-super-admin-app.ts +4 -0
  124. package/src/vue/entitlement-provider.ts +0 -1
  125. package/src/vue/feature-router-guard.ts +0 -1
  126. package/src/vue/super-admin-context.ts +106 -1
  127. package/src/vue/use-api-list.ts +2 -1
  128. package/src/vue/use-mfa-prompt.ts +72 -0
  129. package/src/vue/use-pagination.ts +63 -0
  130. package/src/vue/use-plans.ts +1 -2
  131. package/src/vue/use-sign-out.ts +90 -0
  132. package/src/vue/use-tenant-billing.ts +1 -1
  133. package/src/vue/use-tenant-manifest.ts +0 -1
  134. package/src/components/KpiCard.vue +0 -53
  135. package/src/components/plan-cockpit/PlanCockpit.vue +0 -1012
  136. package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +0 -72
  137. package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +0 -100
  138. package/src/components/plan-cockpit/PlanCockpitHeader.vue +0 -101
  139. package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +0 -112
  140. package/src/components/plan-cockpit/PlanCockpitKpis.vue +0 -68
  141. package/src/components/plan-cockpit/PlanCockpitVersions.vue +0 -251
  142. package/src/components/plan-cockpit/types.ts +0 -72
  143. package/src/components/plan-detail/PlanDetailHeader.vue +0 -194
  144. package/src/pages-standard/bundles-page/BundlesHeader.vue +0 -68
  145. package/src/pages-standard/discovery-page/DiscoveryHeader.vue +0 -34
@@ -1,21 +1,10 @@
1
1
  <template>
2
- <div class="sa-dashboard">
3
- <header class="sa-page-head sa-dashboard__head">
4
- <div>
5
- <h1 class="sa-page-head__title">{{ msg.title }}</h1>
6
- <p v-if="subtitle" class="sa-page-head__sub">{{ subtitle }}</p>
7
- </div>
8
- <div class="sa-page-head__actions">
9
- <q-btn
10
- flat
11
- dense
12
- icon="refresh"
13
- :loading="loading"
14
- :aria-label="common.reload"
15
- @click="reload"
16
- />
17
- </div>
18
- </header>
2
+ <AdminPage class="sa-dashboard">
3
+ <AdminHero :title="msg.title" :subtitle="subtitle">
4
+ <template #actions>
5
+ <AdminRefreshBtn :loading="loading" @refresh="reload" />
6
+ </template>
7
+ </AdminHero>
19
8
 
20
9
  <q-banner v-if="error" class="bg-red-1 text-red-9 q-mb-md" rounded>
21
10
  <strong>{{ common.error }}:</strong> {{ error.message }}
@@ -29,33 +18,31 @@
29
18
  {{ msg.emptyKpiCards }}
30
19
  </div>
31
20
 
32
- <div v-else class="sa-dashboard__strip">
33
- <article v-for="card in cards" :key="card.id" class="sa-kpi" :data-card-id="card.id">
34
- <div class="sa-kpi__icon" v-if="card.displayHint?.icon">
35
- <q-icon :name="card.displayHint.icon" size="20px" />
36
- </div>
37
- <div class="sa-kpi__body">
38
- <div class="sa-kpi__label">{{ card.label }}</div>
39
- <div class="sa-kpi__value">
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>
40
33
  <q-spinner v-if="card.loading" size="18px" />
41
- <span v-else-if="card.error" class="sa-kpi__error">—</span>
42
- <span v-else>{{ formatValue(card) }}</span>
43
- </div>
44
- <div v-if="card.sub" class="sa-kpi__sub">{{ card.sub }}</div>
45
- </div>
46
- </article>
47
- </div>
34
+ <template v-else-if="card.error">—</template>
35
+ <template v-else>{{ formatValue(card) }}</template>
36
+ </template>
37
+ </AdminKpi>
38
+ </AdminStatistics>
39
+ </AdminSection>
48
40
 
49
41
  <div v-if="distributions && distributions.length > 0" class="sa-dashboard__rows">
50
- <section
51
- v-for="dist in distributions"
52
- :key="dist.id"
53
- class="sa-dashboard__card sa-dashboard__row-card"
54
- >
55
- <header class="sa-dashboard__row-head">
56
- <h2>{{ dist.label }}</h2>
57
- <span v-if="dist.total" class="sa-dashboard__count">{{ dist.total }}</span>
58
- </header>
42
+ <AdminSection v-for="dist in distributions" :key="dist.id" :title="dist.label">
43
+ <template v-if="dist.total" #actions>
44
+ <span class="sa-dashboard__count">{{ dist.total }}</span>
45
+ </template>
59
46
  <ul class="sa-dashboard__bar-list">
60
47
  <li
61
48
  v-for="entry in dist.entries"
@@ -75,16 +62,14 @@
75
62
  <span class="sa-dashboard__bar-count">{{ entry.value }}</span>
76
63
  </li>
77
64
  </ul>
78
- </section>
65
+ </AdminSection>
79
66
  </div>
80
67
 
81
- <section
68
+ <AdminSection
82
69
  v-if="resolvedShortcuts.length > 0"
83
- class="sa-dashboard__card sa-dashboard__shortcuts"
70
+ :title="msg.shortcutsTitle"
71
+ class="sa-dashboard__shortcuts"
84
72
  >
85
- <header class="sa-dashboard__row-head">
86
- <h2>{{ msg.shortcutsTitle }}</h2>
87
- </header>
88
73
  <div class="sa-dashboard__shortcut-grid">
89
74
  <a
90
75
  v-for="s in resolvedShortcuts"
@@ -99,10 +84,10 @@
99
84
  </div>
100
85
  </a>
101
86
  </div>
102
- </section>
87
+ </AdminSection>
103
88
 
104
89
  <slot name="after-kpis" />
105
- </div>
90
+ </AdminPage>
106
91
  </template>
107
92
 
108
93
  <script setup lang="ts">
@@ -114,6 +99,12 @@ import { formatMessage } from '../client/i18n/format.js';
114
99
  import { SUPER_ADMIN_MANIFEST_KEY } from '../vue/super-admin-context.js';
115
100
  import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
116
101
  import { useSuperAdminHttp } from '../vue/use-super-admin-context.js';
102
+ import AdminRefreshBtn from '../components/admin-page/AdminRefreshBtn.vue';
103
+ import AdminHero from '../components/admin-page/AdminHero.vue';
104
+ import AdminKpi from '../components/admin-page/AdminKpi.vue';
105
+ import AdminPage from '../components/admin-page/AdminPage.vue';
106
+ import AdminSection from '../components/admin-page/AdminSection.vue';
107
+ import AdminStatistics from '../components/admin-page/AdminStatistics.vue';
117
108
 
118
109
  // Platform standard page: Dashboard.
119
110
  //
@@ -339,17 +330,22 @@ function defaultFormat(card: KpiCardDef, body: unknown): KpiFormatted {
339
330
  };
340
331
  }
341
332
 
342
- async function callHttp(url: string): Promise<{ status: number; json: () => Promise<unknown> }> {
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> }> {
343
344
  const http = props.http ?? shellHttp;
344
- if (http) return http(url, { method: 'GET' });
345
345
  const headers: Record<string, string> = {};
346
346
  const token = props.getAuthToken?.();
347
347
  if (token) headers.Authorization = `Bearer ${token}`;
348
- const resp = await fetch(url, { method: 'GET', headers });
349
- return {
350
- status: resp.status,
351
- json: () => resp.json(),
352
- };
348
+ return http(url, { method: 'GET', headers });
353
349
  }
354
350
 
355
351
  function extractValue(body: Record<string, unknown>): string | number | null {
@@ -403,83 +399,11 @@ function barWidth(value: number, max?: number): number {
403
399
  </script>
404
400
 
405
401
  <style scoped>
406
- .sa-dashboard {
407
- min-height: calc(100vh - 56px);
408
- background: var(--sa-bg-app, #f1f5f9);
409
- padding: 20px 28px 28px;
410
- }
411
402
  /* Header look comes from the global .sa-page-head — only a margin tweak here. */
412
- .sa-dashboard__head {
413
- margin-bottom: 4px;
414
- }
415
403
 
416
- .sa-dashboard__strip {
417
- display: grid;
418
- /* Responsive: 4 cards while there is space, then automatically 3 → 2 → 1. */
419
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
420
- gap: 12px;
421
- margin-bottom: 14px;
422
- }
423
-
424
- .sa-kpi {
425
- background: #fff;
426
- border: 1px solid var(--sa-border, #e2e8f0);
427
- border-radius: 12px;
428
- padding: 14px 16px;
429
- display: flex;
430
- align-items: center;
431
- gap: 12px;
432
- transition:
433
- border-color 0.12s,
434
- box-shadow 0.12s;
435
- }
436
- .sa-kpi:hover {
437
- border-color: #94a3b8;
438
- box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
439
- }
440
- .sa-kpi__icon {
441
- width: 36px;
442
- height: 36px;
443
- border-radius: 9px;
444
- background: var(--sa-primary-soft, rgba(63, 107, 255, 0.08));
445
- display: flex;
446
- align-items: center;
447
- justify-content: center;
448
- }
449
404
  .sa-kpi__icon :deep(.q-icon) {
450
405
  color: var(--sa-primary, #3f6bff);
451
406
  }
452
- .sa-kpi__body {
453
- flex: 1;
454
- min-width: 0;
455
- }
456
- .sa-kpi__label {
457
- font-size: 11.5px;
458
- font-weight: 600;
459
- letter-spacing: 0.06em;
460
- color: var(--sa-muted, #64748b);
461
- text-transform: uppercase;
462
- }
463
- .sa-kpi__value {
464
- font-family: var(--sa-font-head, system-ui, sans-serif);
465
- font-weight: 700;
466
- font-size: 28px;
467
- color: var(--sa-heading, #0f172a);
468
- line-height: 1;
469
- letter-spacing: -0.02em;
470
- margin-top: 4px;
471
- min-height: 30px;
472
- display: flex;
473
- align-items: center;
474
- }
475
- .sa-kpi__sub {
476
- font-size: 11.5px;
477
- color: #94a3b8;
478
- margin-top: 4px;
479
- }
480
- .sa-kpi__error {
481
- color: var(--sa-negative, #dc2626);
482
- }
483
407
 
484
408
  .sa-dashboard__rows {
485
409
  display: grid;
@@ -494,25 +418,6 @@ function barWidth(value: number, max?: number): number {
494
418
  }
495
419
  }
496
420
 
497
- .sa-dashboard__card {
498
- background: #fff;
499
- border: 1px solid var(--sa-border, #e2e8f0);
500
- border-radius: 12px;
501
- padding: 16px 18px;
502
- }
503
- .sa-dashboard__row-head {
504
- display: flex;
505
- align-items: center;
506
- gap: 10px;
507
- margin-bottom: 12px;
508
- }
509
- .sa-dashboard__row-head h2 {
510
- margin: 0;
511
- font-family: var(--sa-font-head, system-ui, sans-serif);
512
- font-size: 14px;
513
- font-weight: 700;
514
- color: var(--sa-heading, #0f172a);
515
- }
516
421
  .sa-dashboard__count {
517
422
  background: var(--sa-primary-soft, rgba(63, 107, 255, 0.08));
518
423
  color: var(--sa-primary, #3f6bff);
@@ -539,10 +444,10 @@ function barWidth(value: number, max?: number): number {
539
444
  }
540
445
  .sa-dashboard__bar-name {
541
446
  font-weight: 600;
542
- color: var(--sa-heading, #0f172a);
447
+ color: var(--sa-heading, var(--sa-heading));
543
448
  }
544
449
  .sa-dashboard__bar-track {
545
- background: #f1f5f9;
450
+ background: var(--sa-border-soft);
546
451
  height: 8px;
547
452
  border-radius: 999px;
548
453
  overflow: hidden;
@@ -556,7 +461,7 @@ function barWidth(value: number, max?: number): number {
556
461
  .sa-dashboard__bar-count {
557
462
  font-variant-numeric: tabular-nums;
558
463
  text-align: right;
559
- color: var(--sa-muted-dark, #475569);
464
+ color: var(--sa-muted-dark, var(--sa-muted-dark));
560
465
  }
561
466
 
562
467
  .sa-dashboard__shortcuts {
@@ -572,7 +477,7 @@ function barWidth(value: number, max?: number): number {
572
477
  gap: 12px;
573
478
  align-items: flex-start;
574
479
  padding: 12px 14px;
575
- border: 1px solid var(--sa-border, #e2e8f0);
480
+ border: 1px solid var(--sa-border, var(--sa-border));
576
481
  border-radius: 10px;
577
482
  text-decoration: none;
578
483
  color: inherit;
@@ -589,19 +494,19 @@ function barWidth(value: number, max?: number): number {
589
494
  }
590
495
  .sa-dashboard__shortcut-title {
591
496
  font-weight: 700;
592
- color: var(--sa-heading, #0f172a);
497
+ color: var(--sa-heading, var(--sa-heading));
593
498
  font-family: var(--sa-font-head, system-ui, sans-serif);
594
499
  }
595
500
  .sa-dashboard__shortcut-sub {
596
501
  font-size: 12px;
597
- color: var(--sa-muted, #64748b);
502
+ color: var(--sa-muted, var(--sa-muted));
598
503
  margin-top: 2px;
599
504
  }
600
505
 
601
506
  .sa-dashboard__loading,
602
507
  .sa-dashboard__empty {
603
508
  padding: 32px 28px;
604
- color: var(--sa-muted, #64748b);
509
+ color: var(--sa-muted, var(--sa-muted));
605
510
  display: flex;
606
511
  align-items: center;
607
512
  gap: 12px;