@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,80 +1,80 @@
1
1
  <template>
2
- <div class="sa-emh">
3
- <header class="sa-emh__head">
4
- <div>
5
- <h1 class="sa-emh__title">{{ resolvedTitle }}</h1>
6
- <p class="sa-emh__sub">{{ msg.history.subtitle }}</p>
7
- </div>
8
- <q-btn flat icon="refresh" :label="common.reload" @click="applyFilter" />
9
- </header>
10
-
11
- <div class="sa-emh__body">
12
- <div class="sa-emh__filter">
13
- <q-input
14
- v-model="filter.search"
15
- outlined
16
- dense
17
- clearable
18
- :label="msg.history.searchLabel"
19
- debounce="350"
20
- @keyup.enter="applyFilter"
21
- @update:model-value="applyFilter"
22
- >
23
- <template #prepend><q-icon name="search" /></template>
24
- </q-input>
25
- <q-select
26
- v-model="filter.status"
27
- outlined
28
- dense
29
- clearable
30
- emit-value
31
- map-options
32
- :label="common.status"
33
- :options="statusOptions"
34
- @update:model-value="applyFilter"
35
- />
36
- <q-input
37
- v-model="filter.from"
38
- outlined
39
- dense
40
- clearable
41
- type="date"
42
- :label="common.from"
43
- @update:model-value="applyFilter"
44
- />
45
- <q-input
46
- v-model="filter.to"
47
- outlined
48
- dense
49
- clearable
50
- type="date"
51
- :label="common.to"
52
- @update:model-value="applyFilter"
53
- />
54
- </div>
55
-
56
- <div class="sa-emh__card">
57
- <q-table
58
- v-model:pagination="pagination"
59
- flat
60
- :rows="rows"
61
- :columns="columns"
62
- row-key="id"
63
- :loading="loading"
64
- :rows-number="pagination.rowsNumber"
65
- @request="onRequest"
66
- @row-click="(_evt, row) => openDetail(row)"
67
- >
68
- <template #body-cell-status="{ row }">
69
- <q-td>
70
- <q-badge
71
- :color="statusColor(row.status)"
72
- :label="statusLabel(row.status)"
73
- />
74
- </q-td>
75
- </template>
76
- <template #body-cell-actions="{ row }">
77
- <q-td class="text-right">
2
+ <AdminPage class="sa-emh">
3
+ <AdminHero :title="resolvedTitle" :subtitle="msg.history.subtitle">
4
+ <template #actions>
5
+ <AdminRefreshBtn :loading="loading" @refresh="applyFilter" />
6
+ </template>
7
+ </AdminHero>
8
+
9
+ <AdminBody>
10
+ <AdminSection class="sa-emh__body">
11
+ <div class="sa-emh__filter">
12
+ <q-input
13
+ v-model="filter.search"
14
+ outlined
15
+ dense
16
+ clearable
17
+ :label="msg.history.searchLabel"
18
+ debounce="350"
19
+ @keyup.enter="applyFilter"
20
+ @update:model-value="applyFilter"
21
+ >
22
+ <template #prepend><q-icon name="search" /></template>
23
+ </q-input>
24
+ <q-select
25
+ v-model="filter.status"
26
+ outlined
27
+ dense
28
+ clearable
29
+ emit-value
30
+ map-options
31
+ :label="common.status"
32
+ :options="statusOptions"
33
+ @update:model-value="applyFilter"
34
+ />
35
+ <q-input
36
+ v-model="filter.from"
37
+ outlined
38
+ dense
39
+ clearable
40
+ type="date"
41
+ :label="common.from"
42
+ @update:model-value="applyFilter"
43
+ />
44
+ <q-input
45
+ v-model="filter.to"
46
+ outlined
47
+ dense
48
+ clearable
49
+ type="date"
50
+ :label="common.to"
51
+ @update:model-value="applyFilter"
52
+ />
53
+ </div>
54
+
55
+ <div class="sa-emh__card">
56
+ <AdminTable
57
+ server-side
58
+ :rows="rows"
59
+ :columns="columns"
60
+ :loading="loading"
61
+ :page="pagination.page"
62
+ :rows-per-page="pagination.rowsPerPage"
63
+ :total="pagination.rowsNumber"
64
+ storage-key="email-history"
65
+ @row-click="(_evt: Event, row: EmailHistoryRow) => openDetail(row)"
66
+ @update:page="onPageChange"
67
+ @update:rows-per-page="onRowsPerPageChange"
68
+ >
69
+ <template #body-cell-status="{ row }">
70
+ <q-td>
71
+ <q-badge
72
+ :color="statusColor(row.status)"
73
+ :label="statusLabel(row.status)"
74
+ />
75
+ </q-td>
76
+ </template>
77
+ <template #row-actions="{ row }">
78
78
  <q-btn
79
79
  flat
80
80
  dense
@@ -91,14 +91,14 @@
91
91
  :title="msg.history.removeFromHistory"
92
92
  @click.stop="askDelete(row.id)"
93
93
  />
94
- </q-td>
95
- </template>
96
- <template #no-data>
97
- <div class="sa-emh__empty">{{ msg.history.empty }}</div>
98
- </template>
99
- </q-table>
100
- </div>
101
- </div>
94
+ </template>
95
+ <template #no-data>
96
+ <div class="sa-emh__empty">{{ msg.history.empty }}</div>
97
+ </template>
98
+ </AdminTable>
99
+ </div>
100
+ </AdminSection>
101
+ </AdminBody>
102
102
 
103
103
  <q-dialog v-model="detailOpen">
104
104
  <q-card class="sa-emh__detail">
@@ -206,18 +206,25 @@
206
206
 
207
207
  <MfaPromptDialog
208
208
  v-if="requireMfaForWrite"
209
- :model-value="showMfa"
210
- :description="mfaDescription"
211
- :error="mfaError"
209
+ :model-value="mfa.show.value"
210
+ :description="mfa.description.value"
211
+ :error="mfa.error.value"
212
212
  :setup-hint="mfaSetupHint"
213
- @update:model-value="onMfaDialogVisibility"
214
- @confirm="onMfaConfirm"
213
+ @update:model-value="mfa.onVisibility"
214
+ @confirm="mfa.onConfirm"
215
215
  />
216
- </div>
216
+ </AdminPage>
217
217
  </template>
218
218
 
219
219
  <script setup lang="ts">
220
+ import AdminTable from '../components/admin-page/AdminTable.vue';
220
221
  import { computed, reactive, ref } from 'vue';
222
+ import { useMfaPrompt } from '../vue/use-mfa-prompt.js';
223
+ import AdminRefreshBtn from '../components/admin-page/AdminRefreshBtn.vue';
224
+ import AdminBody from '../components/admin-page/AdminBody.vue';
225
+ import AdminHero from '../components/admin-page/AdminHero.vue';
226
+ import AdminSection from '../components/admin-page/AdminSection.vue';
227
+ import AdminPage from '../components/admin-page/AdminPage.vue';
221
228
  import { useSuperAdminNotify } from '../quasar/notify.js';
222
229
  import MfaPromptDialog from '../components/MfaPromptDialog.vue';
223
230
  import { formatMessage } from '../client/i18n/format.js';
@@ -306,7 +313,6 @@ const columns = computed(() => [
306
313
  field: (r: EmailHistoryRow) => formatTs(r.sentAt),
307
314
  align: 'left' as const,
308
315
  },
309
- { name: 'actions', label: '', field: 'id' as never, align: 'right' as const },
310
316
  ]);
311
317
 
312
318
  const detailOpen = ref(false);
@@ -317,10 +323,7 @@ const confirmDeleteOpen = ref(false);
317
323
  const pendingDeleteId = ref<string | null>(null);
318
324
 
319
325
  // MFA loop analogous to PlatformEmailPage (promise-resolver pattern).
320
- const showMfa = ref(false);
321
- const mfaError = ref('');
322
- const mfaDescription = ref('');
323
- let pendingMfaResolve: ((code: string | null) => void) | null = null;
326
+ const mfa = useMfaPrompt();
324
327
 
325
328
  // Sequence guard: with rapidly changing filters a stale (out-of-order)
326
329
  // response must not overwrite the newest.
@@ -354,12 +357,17 @@ async function reload(): Promise<void> {
354
357
  void reload();
355
358
  defineExpose({ reload });
356
359
 
357
- async function onRequest(req: {
358
- pagination: { page: number; rowsPerPage: number };
359
- }): Promise<void> {
360
- pagination.value.page = req.pagination.page;
361
- pagination.value.rowsPerPage = req.pagination.rowsPerPage;
362
- await reload();
360
+ // The pager owns page and size; both need a fetch, and a size change starts
361
+ // over at page 1 — the row it would land on otherwise is arbitrary.
362
+ function onPageChange(page: number): void {
363
+ pagination.value.page = page;
364
+ void reload();
365
+ }
366
+
367
+ function onRowsPerPageChange(rows: number): void {
368
+ pagination.value.rowsPerPage = rows;
369
+ pagination.value.page = 1;
370
+ void reload();
363
371
  }
364
372
 
365
373
  function applyFilter(): void {
@@ -428,48 +436,25 @@ async function runWrite<T>(
428
436
  }
429
437
  }
430
438
  for (;;) {
431
- const code = await promptMfa(label);
439
+ const code = await mfa.prompt(label);
432
440
  if (code === null) return { ok: false };
433
441
  try {
434
442
  const result = await invoke(code);
435
- showMfa.value = false;
443
+ mfa.show.value = false;
436
444
  return { ok: true, result };
437
445
  } catch (err) {
438
446
  const status = (err as { response?: { status?: number } })?.response?.status;
439
447
  if (status === 401) {
440
- mfaError.value = shell.value.mfa.invalidCode;
448
+ mfa.error.value = shell.value.mfa.invalidCode;
441
449
  continue;
442
450
  }
443
- showMfa.value = false;
451
+ mfa.show.value = false;
444
452
  notify('negative', errMsg(err));
445
453
  return { ok: false };
446
454
  }
447
455
  }
448
456
  }
449
457
 
450
- function promptMfa(description: string): Promise<string | null> {
451
- return new Promise((resolve) => {
452
- mfaDescription.value = description;
453
- mfaError.value = '';
454
- showMfa.value = true;
455
- pendingMfaResolve = (code) => {
456
- pendingMfaResolve = null;
457
- resolve(code);
458
- };
459
- });
460
- }
461
-
462
- function onMfaConfirm(code: string): void {
463
- pendingMfaResolve?.(code);
464
- }
465
-
466
- function onMfaDialogVisibility(open: boolean): void {
467
- showMfa.value = open;
468
- if (!open && pendingMfaResolve) {
469
- pendingMfaResolve(null);
470
- }
471
- }
472
-
473
458
  function statusColor(status: EmailHistoryStatus): string {
474
459
  switch (status) {
475
460
  case 'SENT':
@@ -521,32 +506,6 @@ function formatTs(iso: string | null | undefined): string {
521
506
  </script>
522
507
 
523
508
  <style scoped>
524
- .sa-emh {
525
- min-height: calc(100vh - 56px);
526
- background: var(--sa-bg-app, #f1f5f9);
527
- }
528
- .sa-emh__head {
529
- padding: 20px 28px 8px;
530
- display: flex;
531
- align-items: flex-start;
532
- justify-content: space-between;
533
- gap: 16px;
534
- }
535
- .sa-emh__title {
536
- margin: 0;
537
- font-family: var(--sa-font-head, system-ui, sans-serif);
538
- font-weight: 700;
539
- font-size: 22px;
540
- color: var(--sa-heading, #0f172a);
541
- }
542
- .sa-emh__sub {
543
- margin: 4px 0 0;
544
- color: var(--sa-muted-dark, #475569);
545
- font-size: 13.5px;
546
- }
547
- .sa-emh__body {
548
- padding: 12px 28px 28px;
549
- }
550
509
  .sa-emh__filter {
551
510
  display: flex;
552
511
  gap: 10px;
@@ -559,7 +518,7 @@ function formatTs(iso: string | null | undefined): string {
559
518
  }
560
519
  .sa-emh__card {
561
520
  background: #fff;
562
- border: 1px solid var(--sa-border, #e2e8f0);
521
+ border: 1px solid var(--sa-border, var(--sa-border));
563
522
  border-radius: 12px;
564
523
  overflow: hidden;
565
524
  padding: 8px 0;
@@ -567,7 +526,7 @@ function formatTs(iso: string | null | undefined): string {
567
526
  .sa-emh__empty {
568
527
  width: 100%;
569
528
  text-align: center;
570
- color: var(--sa-muted-dark, #64748b);
529
+ color: var(--sa-muted-dark, var(--sa-muted));
571
530
  padding: 24px 0;
572
531
  }
573
532
  .sa-emh__detail {
@@ -585,14 +544,14 @@ function formatTs(iso: string | null | undefined): string {
585
544
  width: 100%;
586
545
  min-height: 320px;
587
546
  max-height: 60vh;
588
- border: 1px solid var(--sa-border, #e2e8f0);
547
+ border: 1px solid var(--sa-border, var(--sa-border));
589
548
  border-radius: 8px;
590
549
  background: #fff;
591
550
  }
592
551
  .sa-emh__text,
593
552
  .sa-emh__smtp {
594
- background: #f8fafc;
595
- border: 1px solid var(--sa-border, #e2e8f0);
553
+ background: var(--sa-bg-surface-2);
554
+ border: 1px solid var(--sa-border, var(--sa-border));
596
555
  border-radius: 8px;
597
556
  padding: 12px;
598
557
  font-size: 12px;