@saasicat/ui-vue 0.22.2 → 0.24.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 (119) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-LY52T4N4.js → chunk-TQQMKWN6.js} +58 -1
  3. package/dist/{chunk-XMMFN2WJ.js → chunk-VYMTWCTT.js} +51 -1
  4. package/dist/client/index.cjs +57 -0
  5. package/dist/client/index.d.cts +58 -1
  6. package/dist/client/index.d.ts +58 -1
  7. package/dist/client/index.js +15 -1
  8. package/dist/index.cjs +321 -159
  9. package/dist/index.d.cts +67 -4
  10. package/dist/index.d.ts +67 -4
  11. package/dist/index.js +72 -2
  12. package/dist/quasar/index.cjs +116 -20
  13. package/dist/quasar/index.d.cts +48 -3
  14. package/dist/quasar/index.d.ts +48 -3
  15. package/dist/quasar/index.js +58 -3
  16. package/dist/{use-super-admin-i18n-CgTuNi8h.d.cts → use-sa-theme-B5t7oXph.d.cts} +51 -1
  17. package/dist/{use-super-admin-i18n-CHNdsSlm.d.ts → use-sa-theme-OUBlaqgl.d.ts} +51 -1
  18. package/package.json +5 -2
  19. package/src/client/identity-accents.ts +129 -0
  20. package/src/client/index.ts +1 -0
  21. package/src/components/BundleVersionPublishDialog.vue +17 -17
  22. package/src/components/KvBlock.vue +6 -6
  23. package/src/components/MarketingPromotionsTab.vue +84 -80
  24. package/src/components/MfaPromptDialog.vue +2 -2
  25. package/src/components/TenantActionConfirmDialog.vue +5 -5
  26. package/src/components/VersionDiffPreview.vue +18 -18
  27. package/src/components/bundle-editor/BundleCreatePanel.vue +52 -52
  28. package/src/components/bundle-editor/BundleFeaturesEditor.vue +20 -20
  29. package/src/components/bundle-editor/BundlePlanCompatPicker.vue +33 -33
  30. package/src/components/bundle-editor/BundleQuotasEditor.vue +26 -26
  31. package/src/components/bundle-editor/BundleStatusBanner.vue +21 -21
  32. package/src/components/bundle-editor/BundleVersionInlineEditor.vue +49 -49
  33. package/src/components/bundle-editor/BundleVersionStrip.vue +30 -30
  34. package/src/components/dialogs/PilotCreateDialog.vue +20 -20
  35. package/src/components/dialogs/PilotEditDialog.vue +6 -6
  36. package/src/components/dialogs/PromoCodeCreateDialog.vue +9 -9
  37. package/src/components/dialogs/PromoCodeDialogFields.vue +64 -63
  38. package/src/components/dialogs/PromoCodeEditDialog.vue +11 -11
  39. package/src/components/dialogs/pilot-dialog.css +48 -48
  40. package/src/components/plan/PlanCycleToggle.vue +9 -16
  41. package/src/components/plan/PlanGrid.vue +30 -34
  42. package/src/components/plan/PriceSummary.vue +35 -35
  43. package/src/components/plan/PromoCodeInput.vue +26 -26
  44. package/src/components/plan/PublicBundleGrid.vue +32 -28
  45. package/src/components/plan-create-dialog/PlanCreateDialog.vue +50 -50
  46. package/src/components/plan-detail/PlanDetail.vue +178 -157
  47. package/src/components/plan-detail/PlanTitleEdit.vue +11 -11
  48. package/src/components/plan-list/PlanList.vue +116 -100
  49. package/src/components/plan-matrix/PlanMatrix.vue +97 -114
  50. package/src/components/plan-review/PlanReview.vue +61 -57
  51. package/src/components/plan-version-editor/PlanVersionEditor.vue +246 -203
  52. package/src/components/wizard-stepper/WizardStepper.vue +17 -17
  53. package/src/index.ts +1 -0
  54. package/src/pages-standard/AdminLayout.vue +58 -37
  55. package/src/pages-standard/AdminManifestErrorPage.vue +6 -6
  56. package/src/pages-standard/AuditPage.vue +3 -3
  57. package/src/pages-standard/BundlesPage.vue +37 -37
  58. package/src/pages-standard/DashboardPage.vue +22 -19
  59. package/src/pages-standard/DiscoveryPage.vue +55 -49
  60. package/src/pages-standard/EmailHistoryPage.vue +53 -71
  61. package/src/pages-standard/MarketingCatalogPage.vue +245 -221
  62. package/src/pages-standard/PilotsPage.vue +7 -6
  63. package/src/pages-standard/PlatformEmailPage.vue +18 -18
  64. package/src/pages-standard/PromoCodeDetailPage.vue +3 -3
  65. package/src/pages-standard/PromoCodesPage.vue +10 -7
  66. package/src/pages-standard/SuperAdminLoginPage.vue +39 -25
  67. package/src/pages-standard/SuperAdminSetupWizard.vue +44 -33
  68. package/src/pages-standard/TenantDetailPage.vue +7 -7
  69. package/src/pages-standard/TenantsPage.vue +16 -50
  70. package/src/pages-standard/bundles-page/BundleAccordionList.vue +13 -13
  71. package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +24 -24
  72. package/src/pages-standard/discovery-page/DiscoveryCapList.vue +22 -22
  73. package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +39 -39
  74. package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +26 -26
  75. package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +1 -1
  76. package/src/pages-standard/discovery-page/discovery-ui.ts +10 -7
  77. package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +2 -5
  78. package/src/pages-standard/plan-versions/PlanDiffCard.vue +27 -20
  79. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +24 -16
  80. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +45 -45
  81. package/src/pages-standard/plans-page/PlanBundleOverview.vue +21 -15
  82. package/src/pages-standard/plans-page/PlanPublishDialog.vue +4 -4
  83. package/src/pages-standard/plans-page/PlansPageToast.vue +6 -6
  84. package/src/pages-standard/sa-theme.css +9 -687
  85. package/src/pages-standard/tenants/StatusPill.vue +13 -13
  86. package/src/pages-standard/tenants/format.ts +11 -12
  87. package/src/pages-tenant/LimitsRow.vue +4 -4
  88. package/src/pages-tenant/MySubscriptionBundlesPage.vue +70 -61
  89. package/src/pages-tenant/OnboardingConfigurator.vue +25 -25
  90. package/src/pages-tenant/PackageSnapshotPanel.vue +10 -16
  91. package/src/pages-tenant/PendingVersionBanner.vue +12 -12
  92. package/src/pages-tenant/PlanChangeWizard.vue +22 -76
  93. package/src/pages-tenant/TenantPlanSection.vue +19 -69
  94. package/src/pages-tenant/UsageBar.vue +7 -7
  95. package/src/pages-tenant/default-i18n.ts +113 -0
  96. package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +14 -14
  97. package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +10 -10
  98. package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +2 -2
  99. package/src/quasar/create-super-admin-app.ts +96 -1
  100. package/src/quasar/dark-bridge.ts +66 -0
  101. package/src/quasar/index.ts +1 -0
  102. package/src/ui/theme/_breakpoints.scss +34 -0
  103. package/src/ui/theme/base.css +50 -0
  104. package/src/ui/theme/components/body.css +23 -0
  105. package/src/ui/theme/components/button.css +158 -0
  106. package/src/ui/theme/components/card.css +74 -0
  107. package/src/ui/theme/components/field.css +38 -0
  108. package/src/ui/theme/components/hero.css +46 -0
  109. package/src/ui/theme/components/paginator.css +63 -0
  110. package/src/ui/theme/components/section.css +67 -0
  111. package/src/ui/theme/components/statistics.css +189 -0
  112. package/src/ui/theme/components/table.css +60 -0
  113. package/src/ui/theme/index.css +42 -0
  114. package/src/ui/theme/tokens.legacy.css +147 -0
  115. package/src/ui/theme/tokens.primitive.css +112 -0
  116. package/src/ui/theme/tokens.scale.css +103 -0
  117. package/src/ui/theme/tokens.semantic.dark.css +185 -0
  118. package/src/ui/theme/tokens.semantic.light.css +242 -0
  119. package/src/vue/use-sa-theme.ts +154 -0
@@ -0,0 +1,154 @@
1
+ // The colour-scheme switch. Framework-neutral half: no Quasar import here, so
2
+ // the main entry stays free of an optional peer dependency (`src/quasar/
3
+ // dark-bridge.ts` is the half that talks to Quasar and the DOM).
4
+ //
5
+ // Three schemes, and `'system'` is the default because it is the only one that
6
+ // is right without being asked. `resolved` collapses it to the two the
7
+ // stylesheet knows about, so a caller never has to ask "and what does 'system'
8
+ // mean right now".
9
+ //
10
+ // Following the operating system lives HERE and not in the stylesheet, and
11
+ // that is load-bearing rather than tidy. The stylesheet paints the platform's
12
+ // surfaces; Quasar paints its own cards, dialogs and steppers, and follows only
13
+ // `body--dark`. A media query in the stylesheet moves one of them — measured on
14
+ // an embedded page with the OS set to dark, that meant a white Quasar card
15
+ // carrying near-white platform text. Only the application can move both, so
16
+ // only the application decides, and this is where it does.
17
+
18
+ import {
19
+ computed,
20
+ inject,
21
+ isRef,
22
+ ref,
23
+ watch,
24
+ type ComputedRef,
25
+ type InjectionKey,
26
+ type Ref,
27
+ } from 'vue';
28
+
29
+ import { defaultKvStore, type KvStore } from '../client/types.js';
30
+
31
+ export type SaColorScheme = 'light' | 'dark' | 'system';
32
+ export type SaResolvedScheme = 'light' | 'dark';
33
+
34
+ const SCHEMES: readonly SaColorScheme[] = ['light', 'dark', 'system'];
35
+ const STORAGE_KEY = 'saasicat.theme.scheme';
36
+
37
+ export interface SaTheme {
38
+ /** What the operator picked. Writable — assigning switches the theme. */
39
+ scheme: Ref<SaColorScheme>;
40
+ /** What that means right now: `'system'` resolved against the OS. */
41
+ resolved: ComputedRef<SaResolvedScheme>;
42
+ /**
43
+ * Releases the `prefers-color-scheme` subscription and the persistence
44
+ * watcher. Idempotent.
45
+ *
46
+ * Needed because this context outlives no component — nothing unmounts it.
47
+ * A second shell in the same document (hot reload, a micro-frontend, a
48
+ * second test file) would otherwise leave the first one subscribed, and the
49
+ * next change of the operating system's theme would drive TWO bridges, both
50
+ * writing `data-sa-theme` and Quasar's `Dark` onto the one document. The
51
+ * stale one can win, and then the new shell's explicit scheme is overruled
52
+ * by a context nobody holds a reference to any more.
53
+ */
54
+ dispose: () => void;
55
+ }
56
+
57
+ export interface SaThemeOptions {
58
+ /** Starting scheme. Pass a `Ref` to keep the value in your own store. */
59
+ scheme?: SaColorScheme | Ref<SaColorScheme>;
60
+ /**
61
+ * Remember the operator's pick across reloads, default `true`. Ignored when
62
+ * `scheme` is a `Ref`: the app owns that value and persists it where it
63
+ * belongs. Same rule as the locale in `createSuperAdminI18n`.
64
+ */
65
+ persist?: boolean;
66
+ /** Injection seam for tests. */
67
+ storage?: KvStore;
68
+ }
69
+
70
+ function isScheme(value: unknown): value is SaColorScheme {
71
+ return typeof value === 'string' && SCHEMES.includes(value as SaColorScheme);
72
+ }
73
+
74
+ /**
75
+ * Live `prefers-color-scheme`, as a ref that updates when the OS setting does,
76
+ * plus the handle that unsubscribes it.
77
+ *
78
+ * A one-time read would leave every open tab on the old theme when somebody
79
+ * flips their system to dark at sunset — which is exactly when they flip it.
80
+ * Returns a constant `'light'` where `matchMedia` does not exist (SSR, jsdom
81
+ * without a stub), because guessing dark for a missing API is worse than
82
+ * matching the CSS default.
83
+ */
84
+ function systemScheme(): { value: Ref<SaResolvedScheme>; unsubscribe: () => void } {
85
+ const value = ref<SaResolvedScheme>('light');
86
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
87
+ return { value, unsubscribe: () => {} };
88
+ }
89
+
90
+ const query = window.matchMedia('(prefers-color-scheme: dark)');
91
+ value.value = query.matches ? 'dark' : 'light';
92
+ const onChange = (event: MediaQueryListEvent) => {
93
+ value.value = event.matches ? 'dark' : 'light';
94
+ };
95
+ // `addEventListener` since Safari 14; no fallback to the deprecated
96
+ // `addListener`, because the package's Quasar peer needs a newer browser
97
+ // than that anyway.
98
+ query.addEventListener('change', onChange);
99
+ return { value, unsubscribe: () => query.removeEventListener('change', onChange) };
100
+ }
101
+
102
+ /**
103
+ * Creates the theme context. `createSuperAdminApp()` makes one and the Quasar
104
+ * bridge mirrors it onto the document; call it directly only when bootstrapping
105
+ * by hand.
106
+ */
107
+ export function createSaTheme(options: SaThemeOptions = {}): SaTheme {
108
+ const storage = options.storage ?? defaultKvStore();
109
+ const persist = options.persist !== false;
110
+ // `isRef`, like `createSuperAdminI18n` next door. Branched inline rather
111
+ // than through a boolean, because a boolean does not narrow the union and
112
+ // the version that went through one needed three casts to compile.
113
+ const provided = options.scheme;
114
+ const appOwnsIt = isRef(provided);
115
+
116
+ const stored = persist && !appOwnsIt ? storage.get(STORAGE_KEY) : null;
117
+ const scheme = isRef(provided)
118
+ ? provided
119
+ : ref<SaColorScheme>(isScheme(stored) ? stored : (provided ?? 'system'));
120
+ const { value: system, unsubscribe } = systemScheme();
121
+
122
+ const stopPersisting =
123
+ persist && !appOwnsIt ? watch(scheme, (next) => storage.set(STORAGE_KEY, next)) : null;
124
+
125
+ return {
126
+ scheme,
127
+ resolved: computed(() => (scheme.value === 'system' ? system.value : scheme.value)),
128
+ dispose: () => {
129
+ unsubscribe();
130
+ stopPersisting?.();
131
+ },
132
+ };
133
+ }
134
+
135
+ // `Symbol.for` — see super-admin-context.ts: dist- and src-imported module
136
+ // instances must resolve to the same injection key.
137
+ export const SA_THEME_KEY: InjectionKey<SaTheme> = Symbol.for('@saasicat/ui-vue/SA_THEME');
138
+
139
+ let fallbackTheme: SaTheme | null = null;
140
+
141
+ /**
142
+ * The shell's theme context, or a shared unpersisted one outside a shell.
143
+ *
144
+ * Same shape as `useSuperAdminI18n()`, and for the same reason: a component
145
+ * mounted in isolation must render rather than throw, and the fallback must not
146
+ * write to real storage or two tests in one process would decide each other's
147
+ * outcome.
148
+ */
149
+ export function useSaTheme(): SaTheme {
150
+ const injected = inject(SA_THEME_KEY, null);
151
+ if (injected) return injected;
152
+ fallbackTheme ??= createSaTheme({ persist: false });
153
+ return fallbackTheme;
154
+ }