@saasicat/ui-vue 0.3.0 → 0.5.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 (109) hide show
  1. package/README.md +47 -4
  2. package/dist/action-registry-CQ2wL3R-.d.cts +85 -0
  3. package/dist/action-registry-CQ2wL3R-.d.ts +85 -0
  4. package/dist/chunk-3YP2ZAOD.js +63 -0
  5. package/dist/chunk-EH7TOD4H.js +718 -0
  6. package/dist/chunk-LVGUSD3T.js +26 -0
  7. package/dist/client/index.cjs +785 -0
  8. package/dist/client/index.d.cts +295 -0
  9. package/dist/client/index.d.ts +295 -0
  10. package/dist/client/index.js +52 -0
  11. package/dist/index.cjs +787 -834
  12. package/dist/index.d.cts +267 -841
  13. package/dist/index.d.ts +267 -841
  14. package/dist/index.js +284 -1059
  15. package/dist/quasar/index.cjs +159 -0
  16. package/dist/quasar/index.d.cts +86 -0
  17. package/dist/quasar/index.d.ts +86 -0
  18. package/dist/quasar/index.js +91 -0
  19. package/dist/ui-notify-COUzXEQ4.d.ts +178 -0
  20. package/dist/ui-notify-D_eAAwRh.d.cts +178 -0
  21. package/package.json +23 -3
  22. package/src/client/index.ts +25 -0
  23. package/src/components/FeatureGate.vue +1 -1
  24. package/src/components/plan/PlanCycleToggle.vue +1 -1
  25. package/src/components/plan/PlanGrid.vue +2 -2
  26. package/src/components/plan/PriceSummary.vue +1 -1
  27. package/src/components/plan/PromoCodeInput.vue +1 -1
  28. package/src/components/plan/PublicBundleGrid.vue +1 -1
  29. package/src/index.ts +82 -79
  30. package/src/pages-standard/AdminLayout.vue +2 -2
  31. package/src/pages-standard/DashboardPage.vue +2 -2
  32. package/src/pages-standard/EmailHistoryPage.vue +9 -13
  33. package/src/pages-standard/MarketingCatalogPage.vue +5 -5
  34. package/src/pages-standard/PilotsPage.vue +9 -13
  35. package/src/pages-standard/PlanVersionsPage.vue +2 -2
  36. package/src/pages-standard/PlansPage.vue +2 -2
  37. package/src/pages-standard/PlatformEmailPage.vue +7 -5
  38. package/src/pages-standard/PromoCodesPage.vue +8 -22
  39. package/src/pages-standard/SuperAdminLoginPage.vue +3 -3
  40. package/src/pages-standard/SuperAdminSetupWizard.vue +2 -2
  41. package/src/pages-standard/TenantDetailPage.vue +4 -4
  42. package/src/pages-standard/TenantsPage.vue +3 -3
  43. package/src/pages-standard/UsersPage.vue +6 -12
  44. package/src/pages-standard/plan-versions/PlanVersionHeader.vue +1 -1
  45. package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +1 -1
  46. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +1 -1
  47. package/src/pages-standard/plan-versions/PlanVersionsList.vue +1 -1
  48. package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +1 -1
  49. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +1 -1
  50. package/src/pages-tenant/MySubscriptionBundlesPage.vue +1 -1
  51. package/src/pages-tenant/OnboardingConfigurator.vue +3 -3
  52. package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
  53. package/src/pages-tenant/PlanChangeWizard.vue +2 -2
  54. package/src/pages-tenant/TenantPlanSection.vue +3 -3
  55. package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +1 -1
  56. package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +2 -2
  57. package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +1 -1
  58. package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +1 -1
  59. package/src/quasar/create-super-admin-app.ts +173 -0
  60. package/src/quasar/index.ts +11 -0
  61. package/src/quasar/notify.ts +30 -0
  62. package/src/{manifest-store-factory.ts → vue/manifest-store-factory.ts} +1 -1
  63. package/src/{platform-loaders.ts → vue/platform-loaders.ts} +4 -4
  64. package/src/{project-page-host.ts → vue/project-page-host.ts} +1 -1
  65. package/src/{create-super-admin-app.ts → vue/super-admin-context.ts} +20 -139
  66. package/src/vue/ui-notify.ts +30 -0
  67. package/src/{use-actions.ts → vue/use-actions.ts} +1 -1
  68. package/src/{use-api-list.ts → vue/use-api-list.ts} +1 -1
  69. package/src/{use-batch-columns.ts → vue/use-batch-columns.ts} +1 -1
  70. package/src/{use-bulk-publish.ts → vue/use-bulk-publish.ts} +1 -1
  71. package/src/{use-bundle-versions-map.ts → vue/use-bundle-versions-map.ts} +1 -1
  72. package/src/{use-bundles.ts → vue/use-bundles.ts} +1 -1
  73. package/src/{use-business-types.ts → vue/use-business-types.ts} +1 -1
  74. package/src/{use-catalog-entries.ts → vue/use-catalog-entries.ts} +1 -1
  75. package/src/{use-discovery.ts → vue/use-discovery.ts} +1 -1
  76. package/src/{use-entitlement.ts → vue/use-entitlement.ts} +1 -1
  77. package/src/{use-live-plan-versions.ts → vue/use-live-plan-versions.ts} +1 -1
  78. package/src/{use-manifest.ts → vue/use-manifest.ts} +1 -1
  79. package/src/{use-marketing-projections.ts → vue/use-marketing-projections.ts} +1 -1
  80. package/src/{use-nav.ts → vue/use-nav.ts} +1 -1
  81. package/src/{use-plans.ts → vue/use-plans.ts} +1 -1
  82. package/src/{use-promotions.ts → vue/use-promotions.ts} +1 -1
  83. package/src/{use-public-boot.ts → vue/use-public-boot.ts} +1 -1
  84. package/src/{use-super-admin-context.ts → vue/use-super-admin-context.ts} +2 -2
  85. package/src/{use-tenant-action-flow.ts → vue/use-tenant-action-flow.ts} +1 -1
  86. package/src/{use-tenant-billing-catalog.ts → vue/use-tenant-billing-catalog.ts} +2 -2
  87. package/src/{use-tenant-billing.ts → vue/use-tenant-billing.ts} +2 -2
  88. package/src/{use-tenant-manifest.ts → vue/use-tenant-manifest.ts} +1 -1
  89. package/src/{use-tenant-subscription-bundles.ts → vue/use-tenant-subscription-bundles.ts} +1 -1
  90. package/dist/testing-e2e/admin-pages-suite.d.cts +0 -37
  91. package/dist/testing-e2e/admin-pages-suite.d.ts +0 -37
  92. /package/src/{action-registry.ts → client/action-registry.ts} +0 -0
  93. /package/src/{batch-column-fetcher.ts → client/batch-column-fetcher.ts} +0 -0
  94. /package/src/{boot-loader.ts → client/boot-loader.ts} +0 -0
  95. /package/src/{http-json.ts → client/http-json.ts} +0 -0
  96. /package/src/{manifest-loader.ts → client/manifest-loader.ts} +0 -0
  97. /package/src/{nav-builder.ts → client/nav-builder.ts} +0 -0
  98. /package/src/{plan-versions-catalog.ts → client/plan-versions-catalog.ts} +0 -0
  99. /package/src/{types.ts → client/types.ts} +0 -0
  100. /package/src/{version.ts → client/version.ts} +0 -0
  101. /package/src/{create-admin-routes.ts → vue/create-admin-routes.ts} +0 -0
  102. /package/src/{entitlement-provider.ts → vue/entitlement-provider.ts} +0 -0
  103. /package/src/{feature-router-guard.ts → vue/feature-router-guard.ts} +0 -0
  104. /package/src/{use-audit-entries.ts → vue/use-audit-entries.ts} +0 -0
  105. /package/src/{use-plan-editor.ts → vue/use-plan-editor.ts} +0 -0
  106. /package/src/{use-plan-versions.ts → vue/use-plan-versions.ts} +0 -0
  107. /package/src/{use-platform-tenant-actions.ts → vue/use-platform-tenant-actions.ts} +0 -0
  108. /package/src/{use-subscription-draft.ts → vue/use-subscription-draft.ts} +0 -0
  109. /package/src/{use-tenants.ts → vue/use-tenants.ts} +0 -0
package/README.md CHANGED
@@ -6,7 +6,50 @@ standard pages (Dashboard, Tenants, Plans, Discovery, …) and the tenant
6
6
  self-service building blocks (`FeatureGate`, `useTenantManifest`,
7
7
  `PlanChangeWizard`).
8
8
 
9
- Peer dependencies: `vue`, `vue-router`, `pinia`, `quasar`.
9
+ Peer dependencies: `vue` (required); `vue-router`, `pinia`, `quasar`
10
+ (optional — see the layers below for what actually needs them).
11
+
12
+ ## Layers
13
+
14
+ The package is layered so that each entry only loads what it names.
15
+ Lower layers never import upward; ESLint (`no-restricted-imports`, repo
16
+ root config) enforces the boundaries in CI.
17
+
18
+ | Entry | Source | May import | Contents |
19
+ | --- | --- | --- | --- |
20
+ | `@saasicat/ui-vue/client` | `src/client/` | `@saasicat/types` only | Framework-free core: `BootLoader`, `ManifestLoader` (ETag cache), nav builder, action registry, batch column fetcher, `HttpClient`/`KvStore` contract. Usable from any framework or plain Node/TypeScript. |
21
+ | `@saasicat/ui-vue` (main) | `src/vue/` (+ client re-exports) | Vue, `vue-router`, Pinia — **no Quasar** | Composables (`useTenants`, `usePlanEditor`, …), router guards, injection keys + shell contract, notify-port type, optional Pinia store factory. |
22
+ | `@saasicat/ui-vue/quasar` | `src/quasar/` | everything above + Quasar | `createSuperAdminApp()` bootstrap and the Quasar notify-port implementation. |
23
+ | `@saasicat/ui-vue/pages-standard/*`, `/pages-tenant/*`, `/components/*` | SFC directories | everything | The Quasar reference UI, shipped as raw `.vue` from `src/` (compiled by the consumer's Vite). |
24
+
25
+ Rules of thumb when contributing:
26
+
27
+ - New page/business logic starts as a composable in `src/vue/` (or, if
28
+ framework-free, in `src/client/`); the `.vue` file renders it.
29
+ - Quasar imports in `.ts` files are only allowed under `src/quasar/`.
30
+ - A few framework-free type/i18n modules stay co-located with their SFCs
31
+ (`components/dialogs/types.ts`, `components/bundle-editor/catalog-i18n.ts`,
32
+ `pages-standard/platform-email.types.ts`,
33
+ `pages-standard/email-history.types.ts`, `pages-tenant/default-i18n.ts`)
34
+ and are whitelisted in the ESLint rules — they must not grow framework
35
+ imports.
36
+
37
+ ### Notify port
38
+
39
+ Standard pages emit toasts through a single seam instead of calling
40
+ `$q.notify` directly: the `UiNotify` port (`SUPER_ADMIN_NOTIFY_KEY`).
41
+ `createSuperAdminApp()` provides a Quasar-backed default; apps with their
42
+ own notification center override it:
43
+
44
+ ```ts
45
+ createSuperAdminApp({
46
+ // ...
47
+ notify: (kind, message, options) => myToasts.push({ kind, message, ...options }),
48
+ });
49
+ ```
50
+
51
+ Confirm dialogs inside the reference pages intentionally keep using Quasar's
52
+ `Dialog` — the pages *are* the Quasar layer.
10
53
 
11
54
  ## Usage
12
55
 
@@ -15,7 +58,7 @@ pnpm add @saasicat/ui-vue
15
58
  ```
16
59
 
17
60
  ```ts
18
- import { createSuperAdminApp } from '@saasicat/ui-vue';
61
+ import { createSuperAdminApp } from '@saasicat/ui-vue/quasar';
19
62
  import App from './App.vue';
20
63
  import { routes } from './router/routes';
21
64
 
@@ -43,5 +86,5 @@ and the [handbook](https://github.com/uelker70/saasicat/blob/main/docs/handbook.
43
86
  pnpm --filter @saasicat/ui-vue build
44
87
  ```
45
88
 
46
- Produces `dist/index.{js,cjs,d.ts}` via tsup; `vue`, `vue-router`, `pinia`
47
- and `quasar` stay external.
89
+ Produces `dist/{index,client/index,quasar/index}.{js,cjs,d.ts}` via tsup
90
+ (`tsup.config.ts`); `vue`, `vue-router`, `pinia` and `quasar` stay external.
@@ -0,0 +1,85 @@
1
+ import { AdminManifest, TenantActionDef } from '@saasicat/types';
2
+
3
+ /**
4
+ * Minimal abstraction over `fetch`. Consumers may pass their own
5
+ * implementation (e.g. an axios wrapper that already injects auth headers
6
+ * and tenant headers).
7
+ */
8
+ type HttpClient = (url: string, init?: {
9
+ method?: string;
10
+ headers?: Record<string, string>;
11
+ body?: string;
12
+ }) => Promise<HttpResponse>;
13
+ interface HttpResponse {
14
+ status: number;
15
+ headers: {
16
+ get(name: string): string | null;
17
+ };
18
+ json(): Promise<unknown>;
19
+ text(): Promise<string>;
20
+ }
21
+ /** Simple persistence adapter; default is `localStorage`. */
22
+ interface KvStore {
23
+ get(key: string): string | null;
24
+ set(key: string, value: string): void;
25
+ remove(key: string): void;
26
+ }
27
+ /**
28
+ * Returns a `KvStore` wrapper around `localStorage` (or `null` under SSR).
29
+ * Tests may pass an in-memory stub.
30
+ */
31
+ declare function defaultKvStore(): KvStore;
32
+ /**
33
+ * Default `HttpClient` over `fetch`. Consumers pass their own variant
34
+ * through when they need auth headers / tenant headers / retry logic.
35
+ */
36
+ declare function defaultHttpClient(): HttpClient;
37
+
38
+ /** Consumer implementation; receives the action inputs as a generic object. */
39
+ type ActionHandler<TInput = unknown, TResult = unknown> = (input: TInput) => Promise<TResult>;
40
+ interface ResolvedAction<TInput = unknown, TResult = unknown> {
41
+ def: TenantActionDef;
42
+ handler: ActionHandler<TInput, TResult>;
43
+ }
44
+ declare class MissingHandlerError extends Error {
45
+ constructor(actionKey: string);
46
+ }
47
+ declare class ActionDefNotInManifestError extends Error {
48
+ constructor(actionKey: string);
49
+ }
50
+ declare class ActionRegistry {
51
+ private readonly defs;
52
+ private readonly handlers;
53
+ constructor(manifest: AdminManifest, handlers?: Record<string, ActionHandler>);
54
+ /**
55
+ * Registers a handler after the fact (e.g. when consumer code loads
56
+ * lazily). Throws `ActionDefNotInManifestError` if the `actionKey` is not
57
+ * declared in the manifest — prevents dead registrations.
58
+ */
59
+ register<TInput, TResult>(actionKey: string, handler: ActionHandler<TInput, TResult>): void;
60
+ /**
61
+ * Returns the `{def, handler}` pair. Throws if the key is missing from
62
+ * the manifest or no handler is registered. The shell's UI layer calls
63
+ * the method, checks `def.requiresMfa` / `def.confirmType` for the
64
+ * pre-flow, and then calls `handler(input)`.
65
+ */
66
+ get<TInput = unknown, TResult = unknown>(actionKey: string): ResolvedAction<TInput, TResult>;
67
+ /**
68
+ * Convenience: `get(key).handler(input)`. UI convenience for actions
69
+ * that need neither MFA nor confirm.
70
+ */
71
+ dispatch<TInput, TResult>(actionKey: string, input: TInput): Promise<TResult>;
72
+ /**
73
+ * List of actionKeys that are declared in the manifest but have no
74
+ * handler. Consumers use this in a doctor check to detect drift between
75
+ * the manifest and the shell build.
76
+ */
77
+ listOrphanedDefs(): string[];
78
+ /**
79
+ * List of registered handlers that are missing from the manifest. Drift
80
+ * in the other direction.
81
+ */
82
+ listOrphanedHandlers(): string[];
83
+ }
84
+
85
+ export { type ActionHandler as A, type HttpClient as H, type KvStore as K, MissingHandlerError as M, type ResolvedAction as R, ActionRegistry as a, ActionDefNotInManifestError as b, type HttpResponse as c, defaultHttpClient as d, defaultKvStore as e };
@@ -0,0 +1,85 @@
1
+ import { AdminManifest, TenantActionDef } from '@saasicat/types';
2
+
3
+ /**
4
+ * Minimal abstraction over `fetch`. Consumers may pass their own
5
+ * implementation (e.g. an axios wrapper that already injects auth headers
6
+ * and tenant headers).
7
+ */
8
+ type HttpClient = (url: string, init?: {
9
+ method?: string;
10
+ headers?: Record<string, string>;
11
+ body?: string;
12
+ }) => Promise<HttpResponse>;
13
+ interface HttpResponse {
14
+ status: number;
15
+ headers: {
16
+ get(name: string): string | null;
17
+ };
18
+ json(): Promise<unknown>;
19
+ text(): Promise<string>;
20
+ }
21
+ /** Simple persistence adapter; default is `localStorage`. */
22
+ interface KvStore {
23
+ get(key: string): string | null;
24
+ set(key: string, value: string): void;
25
+ remove(key: string): void;
26
+ }
27
+ /**
28
+ * Returns a `KvStore` wrapper around `localStorage` (or `null` under SSR).
29
+ * Tests may pass an in-memory stub.
30
+ */
31
+ declare function defaultKvStore(): KvStore;
32
+ /**
33
+ * Default `HttpClient` over `fetch`. Consumers pass their own variant
34
+ * through when they need auth headers / tenant headers / retry logic.
35
+ */
36
+ declare function defaultHttpClient(): HttpClient;
37
+
38
+ /** Consumer implementation; receives the action inputs as a generic object. */
39
+ type ActionHandler<TInput = unknown, TResult = unknown> = (input: TInput) => Promise<TResult>;
40
+ interface ResolvedAction<TInput = unknown, TResult = unknown> {
41
+ def: TenantActionDef;
42
+ handler: ActionHandler<TInput, TResult>;
43
+ }
44
+ declare class MissingHandlerError extends Error {
45
+ constructor(actionKey: string);
46
+ }
47
+ declare class ActionDefNotInManifestError extends Error {
48
+ constructor(actionKey: string);
49
+ }
50
+ declare class ActionRegistry {
51
+ private readonly defs;
52
+ private readonly handlers;
53
+ constructor(manifest: AdminManifest, handlers?: Record<string, ActionHandler>);
54
+ /**
55
+ * Registers a handler after the fact (e.g. when consumer code loads
56
+ * lazily). Throws `ActionDefNotInManifestError` if the `actionKey` is not
57
+ * declared in the manifest — prevents dead registrations.
58
+ */
59
+ register<TInput, TResult>(actionKey: string, handler: ActionHandler<TInput, TResult>): void;
60
+ /**
61
+ * Returns the `{def, handler}` pair. Throws if the key is missing from
62
+ * the manifest or no handler is registered. The shell's UI layer calls
63
+ * the method, checks `def.requiresMfa` / `def.confirmType` for the
64
+ * pre-flow, and then calls `handler(input)`.
65
+ */
66
+ get<TInput = unknown, TResult = unknown>(actionKey: string): ResolvedAction<TInput, TResult>;
67
+ /**
68
+ * Convenience: `get(key).handler(input)`. UI convenience for actions
69
+ * that need neither MFA nor confirm.
70
+ */
71
+ dispatch<TInput, TResult>(actionKey: string, input: TInput): Promise<TResult>;
72
+ /**
73
+ * List of actionKeys that are declared in the manifest but have no
74
+ * handler. Consumers use this in a doctor check to detect drift between
75
+ * the manifest and the shell build.
76
+ */
77
+ listOrphanedDefs(): string[];
78
+ /**
79
+ * List of registered handlers that are missing from the manifest. Drift
80
+ * in the other direction.
81
+ */
82
+ listOrphanedHandlers(): string[];
83
+ }
84
+
85
+ export { type ActionHandler as A, type HttpClient as H, type KvStore as K, MissingHandlerError as M, type ResolvedAction as R, ActionRegistry as a, ActionDefNotInManifestError as b, type HttpResponse as c, defaultHttpClient as d, defaultKvStore as e };
@@ -0,0 +1,63 @@
1
+ // src/vue/super-admin-context.ts
2
+ var SUPER_ADMIN_BRAND_KEY = /* @__PURE__ */ Symbol.for(
3
+ "@saasicat/ui-vue/SUPER_ADMIN_BRAND"
4
+ );
5
+ var SUPER_ADMIN_ENDPOINTS_KEY = /* @__PURE__ */ Symbol.for(
6
+ "@saasicat/ui-vue/SUPER_ADMIN_ENDPOINTS"
7
+ );
8
+ var SUPER_ADMIN_EXTENSIONS_KEY = /* @__PURE__ */ Symbol.for(
9
+ "@saasicat/ui-vue/SUPER_ADMIN_EXTENSIONS"
10
+ );
11
+ var SUPER_ADMIN_ACTIONS_KEY = /* @__PURE__ */ Symbol.for(
12
+ "@saasicat/ui-vue/SUPER_ADMIN_ACTIONS"
13
+ );
14
+ var SUPER_ADMIN_MANIFEST_KEY = /* @__PURE__ */ Symbol.for(
15
+ "@saasicat/ui-vue/SUPER_ADMIN_MANIFEST"
16
+ );
17
+ var SUPER_ADMIN_LOGIN_ADAPTER_KEY = /* @__PURE__ */ Symbol.for(
18
+ "@saasicat/ui-vue/SUPER_ADMIN_LOGIN_ADAPTER"
19
+ );
20
+ var SUPER_ADMIN_HTTP_KEY = /* @__PURE__ */ Symbol.for(
21
+ "@saasicat/ui-vue/SUPER_ADMIN_HTTP"
22
+ );
23
+ function buildNavigationGuard(options) {
24
+ const { authGuard, manifestGuard } = options;
25
+ if (!authGuard && !manifestGuard) return null;
26
+ return async (to) => {
27
+ if (to.meta?.public === true) return true;
28
+ if (authGuard) {
29
+ if (!authGuard.isAuthenticated()) return authGuard.onUnauthenticated();
30
+ if (authGuard.isSuperAdmin && !authGuard.isSuperAdmin()) {
31
+ return authGuard.onUnauthenticated();
32
+ }
33
+ }
34
+ if (manifestGuard) {
35
+ try {
36
+ await manifestGuard.ensureLoaded();
37
+ } catch (err) {
38
+ if (manifestGuard.errorRoute && to.path !== manifestGuard.errorRoute) {
39
+ return manifestGuard.errorRoute;
40
+ }
41
+ console.error("[SuperAdmin] manifest load failed", err);
42
+ }
43
+ }
44
+ return true;
45
+ };
46
+ }
47
+
48
+ // src/vue/ui-notify.ts
49
+ var SUPER_ADMIN_NOTIFY_KEY = /* @__PURE__ */ Symbol.for(
50
+ "@saasicat/ui-vue/SUPER_ADMIN_NOTIFY"
51
+ );
52
+
53
+ export {
54
+ SUPER_ADMIN_BRAND_KEY,
55
+ SUPER_ADMIN_ENDPOINTS_KEY,
56
+ SUPER_ADMIN_EXTENSIONS_KEY,
57
+ SUPER_ADMIN_ACTIONS_KEY,
58
+ SUPER_ADMIN_MANIFEST_KEY,
59
+ SUPER_ADMIN_LOGIN_ADAPTER_KEY,
60
+ SUPER_ADMIN_HTTP_KEY,
61
+ buildNavigationGuard,
62
+ SUPER_ADMIN_NOTIFY_KEY
63
+ };