@saasicat/ui-vue 0.2.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.
- package/LICENSE +202 -0
- package/README.md +47 -0
- package/dist/index.cjs +3776 -0
- package/dist/index.d.cts +2395 -0
- package/dist/index.d.ts +2395 -0
- package/dist/index.js +3659 -0
- package/dist/testing-e2e/admin-pages-suite.cjs +134 -0
- package/dist/testing-e2e/admin-pages-suite.d.cts +37 -0
- package/dist/testing-e2e/admin-pages-suite.d.ts +37 -0
- package/dist/testing-e2e/admin-pages-suite.js +109 -0
- package/package.json +87 -0
- package/src/action-registry.ts +110 -0
- package/src/batch-column-fetcher.ts +152 -0
- package/src/boot-loader.ts +60 -0
- package/src/components/BundleVersionPublishDialog.vue +359 -0
- package/src/components/BusinessTypeVersionEditorDialog.vue +417 -0
- package/src/components/BusinessTypeVersionPublishDialog.vue +258 -0
- package/src/components/FeatureGate.vue +56 -0
- package/src/components/KpiCard.vue +53 -0
- package/src/components/KvBlock.vue +36 -0
- package/src/components/MarketingPromotionsTab.vue +861 -0
- package/src/components/MfaPromptDialog.vue +104 -0
- package/src/components/TenantActionConfirmDialog.vue +252 -0
- package/src/components/VersionDiffPreview.vue +150 -0
- package/src/components/bundle-editor/BundleCreatePanel.vue +681 -0
- package/src/components/bundle-editor/BundleFeaturesEditor.vue +215 -0
- package/src/components/bundle-editor/BundlePlanCompatPicker.vue +316 -0
- package/src/components/bundle-editor/BundleQuotasEditor.vue +219 -0
- package/src/components/bundle-editor/BundleStatusBanner.vue +189 -0
- package/src/components/bundle-editor/BundleVersionInlineEditor.vue +659 -0
- package/src/components/bundle-editor/BundleVersionStrip.vue +207 -0
- package/src/components/bundle-editor/bundle-version-status.ts +164 -0
- package/src/components/bundle-editor/catalog-i18n.ts +85 -0
- package/src/components/dialogs/PilotCreateDialog.vue +780 -0
- package/src/components/dialogs/PilotEditDialog.vue +548 -0
- package/src/components/dialogs/PromoCodeCreateDialog.vue +737 -0
- package/src/components/dialogs/PromoCodeEditDialog.vue +859 -0
- package/src/components/dialogs/types.ts +126 -0
- package/src/components/plan/PlanCycleToggle.vue +99 -0
- package/src/components/plan/PlanGrid.vue +211 -0
- package/src/components/plan/PriceSummary.vue +293 -0
- package/src/components/plan/PromoCodeInput.vue +192 -0
- package/src/components/plan/PublicBundleGrid.vue +287 -0
- package/src/components/plan-cockpit/PlanCockpit.vue +993 -0
- package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +65 -0
- package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +91 -0
- package/src/components/plan-cockpit/PlanCockpitHeader.vue +98 -0
- package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +99 -0
- package/src/components/plan-cockpit/PlanCockpitKpis.vue +57 -0
- package/src/components/plan-cockpit/PlanCockpitVersions.vue +226 -0
- package/src/components/plan-cockpit/types.ts +72 -0
- package/src/components/plan-create-dialog/PlanCreateDialog.vue +508 -0
- package/src/components/plan-detail/PlanAuditLog.vue +59 -0
- package/src/components/plan-detail/PlanDetail.vue +1195 -0
- package/src/components/plan-detail/PlanDetailHeader.vue +185 -0
- package/src/components/plan-detail/PlanDetailKpis.vue +57 -0
- package/src/components/plan-detail/PlanTerminateDialog.vue +76 -0
- package/src/components/plan-detail/PlanVersionDiffPanel.vue +149 -0
- package/src/components/plan-detail/PlanVersionsPanel.vue +255 -0
- package/src/components/plan-detail/types.ts +59 -0
- package/src/components/plan-list/PlanList.vue +1067 -0
- package/src/components/plan-matrix/PlanMatrix.vue +1113 -0
- package/src/components/plan-review/PlanReview.vue +742 -0
- package/src/components/plan-version-editor/PlanCatalogPreview.vue +184 -0
- package/src/components/plan-version-editor/PlanComponentPool.vue +272 -0
- package/src/components/plan-version-editor/PlanVersionBasket.vue +281 -0
- package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +84 -0
- package/src/components/plan-version-editor/PlanVersionEditor.vue +1728 -0
- package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +94 -0
- package/src/components/plan-version-editor/types.ts +96 -0
- package/src/components/wizard-stepper/WizardStepper.vue +107 -0
- package/src/create-admin-routes.ts +68 -0
- package/src/create-super-admin-app.ts +345 -0
- package/src/entitlement-provider.ts +36 -0
- package/src/feature-router-guard.ts +77 -0
- package/src/http-json.ts +62 -0
- package/src/index.ts +98 -0
- package/src/manifest-loader.ts +139 -0
- package/src/manifest-store-factory.ts +114 -0
- package/src/nav-builder.ts +274 -0
- package/src/pages-standard/AdminLayout.vue +411 -0
- package/src/pages-standard/AdminManifestErrorPage.vue +79 -0
- package/src/pages-standard/AuditPage.vue +223 -0
- package/src/pages-standard/BundlesPage.vue +910 -0
- package/src/pages-standard/BusinessTypesPage.vue +662 -0
- package/src/pages-standard/DashboardPage.vue +595 -0
- package/src/pages-standard/DiscoveryPage.vue +663 -0
- package/src/pages-standard/EmailHistoryPage.vue +576 -0
- package/src/pages-standard/MarketingCatalogPage.vue +1771 -0
- package/src/pages-standard/PilotsPage.vue +577 -0
- package/src/pages-standard/PlanVersionsPage.vue +216 -0
- package/src/pages-standard/PlansPage.vue +1222 -0
- package/src/pages-standard/PlatformEmailPage.vue +434 -0
- package/src/pages-standard/PromoCodeDetailPage.vue +331 -0
- package/src/pages-standard/PromoCodesPage.vue +589 -0
- package/src/pages-standard/SubscriptionsPage.vue +159 -0
- package/src/pages-standard/SuperAdminLoginPage.vue +299 -0
- package/src/pages-standard/SuperAdminSetupWizard.vue +405 -0
- package/src/pages-standard/TenantDetailPage.vue +432 -0
- package/src/pages-standard/TenantsPage.vue +732 -0
- package/src/pages-standard/UsersPage.vue +504 -0
- package/src/pages-standard/bundles-page/BundleAccordionList.vue +116 -0
- package/src/pages-standard/bundles-page/BundleDetailPanel.vue +193 -0
- package/src/pages-standard/bundles-page/BundlesFilterBar.vue +48 -0
- package/src/pages-standard/bundles-page/BundlesHeader.vue +67 -0
- package/src/pages-standard/bundles-page/BundlesKpis.vue +39 -0
- package/src/pages-standard/bundles-page/types.ts +15 -0
- package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +246 -0
- package/src/pages-standard/discovery-page/DiscoveryCapList.vue +171 -0
- package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +410 -0
- package/src/pages-standard/discovery-page/DiscoveryHeader.vue +31 -0
- package/src/pages-standard/discovery-page/DiscoveryKpis.vue +47 -0
- package/src/pages-standard/discovery-page/DiscoveryMetaBanner.vue +26 -0
- package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +247 -0
- package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +73 -0
- package/src/pages-standard/discovery-page/discovery-ui.ts +166 -0
- package/src/pages-standard/email-history.types.ts +48 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +448 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +93 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +138 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +58 -0
- package/src/pages-standard/marketing-catalog/types.ts +43 -0
- package/src/pages-standard/plan-versions/PlanDiffCard.vue +132 -0
- package/src/pages-standard/plan-versions/PlanVersionHeader.vue +293 -0
- package/src/pages-standard/plan-versions/PlanVersionStatusBadge.vue +48 -0
- package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +307 -0
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +269 -0
- package/src/pages-standard/plan-versions/PlanVersionsKpi.vue +65 -0
- package/src/pages-standard/plan-versions/PlanVersionsList.vue +292 -0
- package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +261 -0
- package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +300 -0
- package/src/pages-standard/plan-versions/VersionDiffPreview.vue +167 -0
- package/src/pages-standard/plan-versions/format.ts +53 -0
- package/src/pages-standard/plan-versions/types.ts +33 -0
- package/src/pages-standard/plans-page/PlanArchiveDialog.vue +50 -0
- package/src/pages-standard/plans-page/PlanBundleOverview.vue +147 -0
- package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +46 -0
- package/src/pages-standard/plans-page/PlanPublishDialog.vue +106 -0
- package/src/pages-standard/plans-page/PlansPageToast.vue +69 -0
- package/src/pages-standard/plans-page/types.ts +18 -0
- package/src/pages-standard/platform-email.types.ts +41 -0
- package/src/pages-standard/sa-theme.css +223 -0
- package/src/pages-standard/tenants/StatusPill.vue +62 -0
- package/src/pages-standard/tenants/format.ts +38 -0
- package/src/pages-tenant/LimitsRow.vue +67 -0
- package/src/pages-tenant/MySubscriptionBundlesPage.vue +604 -0
- package/src/pages-tenant/OnboardingConfigurator.vue +417 -0
- package/src/pages-tenant/PackageSnapshotPanel.vue +241 -0
- package/src/pages-tenant/PendingVersionBanner.vue +126 -0
- package/src/pages-tenant/PlanChangeWizard.vue +630 -0
- package/src/pages-tenant/TenantPlanSection.vue +729 -0
- package/src/pages-tenant/UsageBar.vue +107 -0
- package/src/pages-tenant/default-i18n.ts +280 -0
- package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +304 -0
- package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +323 -0
- package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +122 -0
- package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +67 -0
- package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +31 -0
- package/src/plan-versions-catalog.ts +368 -0
- package/src/platform-loaders.ts +68 -0
- package/src/project-page-host.ts +108 -0
- package/src/testing-e2e/admin-pages-suite.ts +187 -0
- package/src/types.ts +58 -0
- package/src/use-actions.ts +25 -0
- package/src/use-api-list.ts +151 -0
- package/src/use-audit-entries.ts +48 -0
- package/src/use-batch-columns.ts +59 -0
- package/src/use-bulk-publish.ts +150 -0
- package/src/use-bundle-versions-map.ts +112 -0
- package/src/use-bundles.ts +297 -0
- package/src/use-business-types.ts +269 -0
- package/src/use-catalog-entries.ts +243 -0
- package/src/use-discovery.ts +158 -0
- package/src/use-entitlement.ts +87 -0
- package/src/use-live-plan-versions.ts +126 -0
- package/src/use-manifest.ts +57 -0
- package/src/use-marketing-projections.ts +160 -0
- package/src/use-nav.ts +33 -0
- package/src/use-plan-editor.ts +162 -0
- package/src/use-plan-versions.ts +83 -0
- package/src/use-plans.ts +375 -0
- package/src/use-platform-tenant-actions.ts +286 -0
- package/src/use-promotions.ts +126 -0
- package/src/use-public-boot.ts +40 -0
- package/src/use-subscription-draft.ts +355 -0
- package/src/use-super-admin-context.ts +93 -0
- package/src/use-tenant-action-flow.ts +238 -0
- package/src/use-tenant-billing-catalog.ts +160 -0
- package/src/use-tenant-billing.ts +453 -0
- package/src/use-tenant-manifest.ts +94 -0
- package/src/use-tenant-subscription-bundles.ts +148 -0
- package/src/use-tenants.ts +61 -0
- package/src/version.ts +10 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
// createSuperAdminApp — universal bootstrap helper for SuperAdmin Vue apps.
|
|
2
|
+
// Bundles the Quasar/Pinia/Router setup that every consumer app rebuilds itself
|
|
3
|
+
// today, and exposes the platform contract maps (`extensions:`, `actions:`)
|
|
4
|
+
// via `provide()`.
|
|
5
|
+
//
|
|
6
|
+
// What the helper does NOT do:
|
|
7
|
+
// - Auth stores, login flows, JWT refresh — app-specific.
|
|
8
|
+
// - CSS imports (Quasar CSS, Material Icons, app theme) — these stay in the
|
|
9
|
+
// app's own `main.ts`, because tsup does not bundle CSS.
|
|
10
|
+
|
|
11
|
+
import { createApp, type App, type Component, type InjectionKey } from 'vue';
|
|
12
|
+
import { Quasar, Notify, Dialog, Loading, type QuasarPluginOptions } from 'quasar';
|
|
13
|
+
import { createPinia, type Pinia } from 'pinia';
|
|
14
|
+
import {
|
|
15
|
+
createRouter,
|
|
16
|
+
createWebHistory,
|
|
17
|
+
type NavigationGuardWithThis,
|
|
18
|
+
type RouteLocationNormalized,
|
|
19
|
+
type RouteRecordRaw,
|
|
20
|
+
type Router,
|
|
21
|
+
type RouterHistory,
|
|
22
|
+
} from 'vue-router';
|
|
23
|
+
|
|
24
|
+
import type { ActionKey, AdminManifest, ComponentKey } from '@saasicat/types';
|
|
25
|
+
import type { ActionHandler } from './action-registry.js';
|
|
26
|
+
import { defaultHttpClient, type HttpClient } from './types.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* App-specific branding data that the platform `AdminLayout` and other
|
|
30
|
+
* consumers read via `useSuperAdminBrand()`.
|
|
31
|
+
*/
|
|
32
|
+
export interface SuperAdminBrand {
|
|
33
|
+
/** 2-letter abbreviation in the logo badge (`'ma'`, `'da'`, …). */
|
|
34
|
+
logoText: string;
|
|
35
|
+
/** Full display name (`'DemoApp'`, `'ClubApp'`, …). */
|
|
36
|
+
name: string;
|
|
37
|
+
/** Optional: tag to the right of the name, default `'SuperAdmin'`. */
|
|
38
|
+
tag?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Endpoint configuration. `apiBase` is the shared prefix under which
|
|
43
|
+
* `/manifest`, `/boot` and extra routes live.
|
|
44
|
+
*/
|
|
45
|
+
export interface SuperAdminEndpoints {
|
|
46
|
+
/** Shared prefix, e.g. `'/api/admin'` or `'/api/v1/admin'`. */
|
|
47
|
+
apiBase: string;
|
|
48
|
+
/** Pre-login branding endpoint, default `${apiBase}/boot`. */
|
|
49
|
+
publicBootEndpoint?: string;
|
|
50
|
+
/** Post-login full-manifest endpoint, default `${apiBase}/manifest`. */
|
|
51
|
+
manifestEndpoint?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type ExtensionLoader = () => Promise<Component | { default: Component }>;
|
|
55
|
+
export type ExtensionsMap = Record<ComponentKey, ExtensionLoader>;
|
|
56
|
+
export type ActionsMap = Record<ActionKey, ActionHandler>;
|
|
57
|
+
|
|
58
|
+
export interface SuperAdminAuthGuardOptions {
|
|
59
|
+
/** App provides: is the user currently logged in? */
|
|
60
|
+
isAuthenticated: () => boolean;
|
|
61
|
+
/** App provides: does the user have the SuperAdmin role? Default: only check `isAuthenticated`. */
|
|
62
|
+
isSuperAdmin?: () => boolean;
|
|
63
|
+
/** App provides: redirect path for unauthenticated calls (e.g. `'/login'`). */
|
|
64
|
+
onUnauthenticated: () => string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Result of a login attempt. Apps pass this back from their auth store to the
|
|
69
|
+
* platform LoginPage; the page renders an appropriate error message.
|
|
70
|
+
*
|
|
71
|
+
* `ok: true` → login succeeded, page redirects to `redirectAfterLogin`.
|
|
72
|
+
* `ok: false` → page shows `message` or a translation derived from `code`.
|
|
73
|
+
*
|
|
74
|
+
* Known codes:
|
|
75
|
+
* - `BAD_CREDENTIALS` — wrong email/password combination.
|
|
76
|
+
* - `NOT_SUPER_ADMIN` — account does not have the SuperAdmin role.
|
|
77
|
+
* - otherwise — app-specific; `message` is displayed directly.
|
|
78
|
+
*/
|
|
79
|
+
export type SuperAdminLoginResult =
|
|
80
|
+
| { ok: true }
|
|
81
|
+
| { ok: false; code?: 'BAD_CREDENTIALS' | 'NOT_SUPER_ADMIN' | string; message?: string };
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Login adapter. The app passes its auth-store call through here. The platform
|
|
85
|
+
* LoginPage consumes it via `useSuperAdminLoginAdapter()`, without knowledge
|
|
86
|
+
* of app-specific stores (Pinia, auth API routes, MFA hooks).
|
|
87
|
+
*/
|
|
88
|
+
export interface SuperAdminLoginAdapter {
|
|
89
|
+
/**
|
|
90
|
+
* Performs the login. The app store encapsulates the API call, token
|
|
91
|
+
* storage, MFA hops etc.
|
|
92
|
+
*/
|
|
93
|
+
login(email: string, password: string): Promise<SuperAdminLoginResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Target route after a successful login. Default: `/admin/dashboard`
|
|
96
|
+
* (platform convention for the standard pages — apps with a different
|
|
97
|
+
* default mount override this here).
|
|
98
|
+
*/
|
|
99
|
+
redirectAfterLogin?: string;
|
|
100
|
+
/**
|
|
101
|
+
* Optional: dev hint (test account), shown below the form. Deliberately
|
|
102
|
+
* rendered only when `environment !== 'production'`.
|
|
103
|
+
*/
|
|
104
|
+
devHint?: { email: string; password: string };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface SuperAdminManifestGuardOptions {
|
|
108
|
+
/**
|
|
109
|
+
* App provides: loads the manifest into the app store. The router guard
|
|
110
|
+
* `await`s the promise before the route is resolved.
|
|
111
|
+
*
|
|
112
|
+
* **On loader error:** the promise REJECTS. The router guard catches the
|
|
113
|
+
* rejection and decides depending on `errorRoute`:
|
|
114
|
+
* - `errorRoute` set → redirect to this route (fail-closed).
|
|
115
|
+
* - `errorRoute` not set → `console.error` + render allowed
|
|
116
|
+
* (defensive default behavior; the app must render the manifest gap
|
|
117
|
+
* itself).
|
|
118
|
+
*/
|
|
119
|
+
ensureLoaded: () => Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Optional: read accessor on the loaded manifest. When set, it is exposed
|
|
122
|
+
* via `provide(SUPER_ADMIN_MANIFEST_KEY)` — the `<ProjectPageHost>`
|
|
123
|
+
* resolves manifest `projectPages` through it against the
|
|
124
|
+
* `extensions:` map.
|
|
125
|
+
*/
|
|
126
|
+
getManifest?: () => AdminManifest | null;
|
|
127
|
+
/**
|
|
128
|
+
* Optional: path that the router guard redirects to on a manifest load
|
|
129
|
+
* error (fail-closed mode). The app must register the route in `appRoutes`
|
|
130
|
+
* and mark it as `meta.public = true`, otherwise it runs through the
|
|
131
|
+
* manifest guard again and produces a redirect loop.
|
|
132
|
+
*/
|
|
133
|
+
errorRoute?: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface CreateSuperAdminAppOptions {
|
|
137
|
+
/** App root component (`App.vue`). */
|
|
138
|
+
rootComponent: Component;
|
|
139
|
+
/** App branding (logo, name). */
|
|
140
|
+
brand: SuperAdminBrand;
|
|
141
|
+
/** Endpoint configuration. */
|
|
142
|
+
endpoints: SuperAdminEndpoints;
|
|
143
|
+
/** App's own routes (login, standard pages, bundle pages, …). */
|
|
144
|
+
appRoutes: RouteRecordRaw[];
|
|
145
|
+
/**
|
|
146
|
+
* Static `extensions:` map. Manifest `projectPages[].componentKey` is
|
|
147
|
+
* looked up in it (see Spec §4.4).
|
|
148
|
+
*/
|
|
149
|
+
extensions?: ExtensionsMap;
|
|
150
|
+
/**
|
|
151
|
+
* Static `actions:` map. Manifest `tenants.actions[].actionKey` is looked
|
|
152
|
+
* up in it.
|
|
153
|
+
*/
|
|
154
|
+
actions?: ActionsMap;
|
|
155
|
+
/**
|
|
156
|
+
* Optional: auth guard. When set, `router.beforeEach` is wired up
|
|
157
|
+
* automatically — `to.meta.public === true` bypasses the guard.
|
|
158
|
+
*/
|
|
159
|
+
authGuard?: SuperAdminAuthGuardOptions;
|
|
160
|
+
/**
|
|
161
|
+
* Optional: manifest guard. Runs after a successful auth guard, blocks the
|
|
162
|
+
* render until the manifest is loaded (prevents sidebar flicker).
|
|
163
|
+
*/
|
|
164
|
+
manifestGuard?: SuperAdminManifestGuardOptions;
|
|
165
|
+
/**
|
|
166
|
+
* Optional: login adapter. When set, it is exposed via `provide()` for the
|
|
167
|
+
* shared `<SuperAdminLoginPage>` (from `pages-standard/`) — the app no
|
|
168
|
+
* longer needs to hold its own LoginPage Vue component.
|
|
169
|
+
*/
|
|
170
|
+
loginAdapter?: SuperAdminLoginAdapter;
|
|
171
|
+
/**
|
|
172
|
+
* Optional: router history variant, default `createWebHistory()`.
|
|
173
|
+
* Apps with a subpath mount override this.
|
|
174
|
+
*/
|
|
175
|
+
routerHistory?: RouterHistory;
|
|
176
|
+
/**
|
|
177
|
+
* Optional: Quasar configuration. Default loads `Notify`/`Dialog`/`Loading`
|
|
178
|
+
* with the established consumer convention (`top-right`, 3 s).
|
|
179
|
+
*/
|
|
180
|
+
quasarOptions?: Partial<QuasarPluginOptions>;
|
|
181
|
+
/**
|
|
182
|
+
* Optional: additional Vue plugins (e.g. an app's own NotificationCenter)
|
|
183
|
+
* that are installed after the platform setup, before the mount.
|
|
184
|
+
*/
|
|
185
|
+
installPlugins?: Array<(app: App) => void>;
|
|
186
|
+
/**
|
|
187
|
+
* Optional: `HttpClient` for all pre-login calls (boot, first-run setup).
|
|
188
|
+
* Default `defaultHttpClient()` (= `fetch`). Consumers pass their own
|
|
189
|
+
* variant through (auth header, baseURL, retry) — it then applies uniformly,
|
|
190
|
+
* including for the setup wizard. Consumed via `useSuperAdminHttp()`.
|
|
191
|
+
*/
|
|
192
|
+
http?: HttpClient;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface SuperAdminAppHandle {
|
|
196
|
+
app: App;
|
|
197
|
+
router: Router;
|
|
198
|
+
pinia: Pinia;
|
|
199
|
+
/** Mounts the app on a selector. Returns the root component instance. */
|
|
200
|
+
mount: (selector: string | Element) => ReturnType<App['mount']>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Vue inject keys are registered globally via `Symbol.for(...)`. Reason:
|
|
204
|
+
// apps import `createSuperAdminApp` (and thus `app.provide(KEY, ...)`) from
|
|
205
|
+
// the built `dist/index.js` bundle, but shared `.vue` pages in the
|
|
206
|
+
// `pages-standard/` directory directly from `src/`. Both paths produce two
|
|
207
|
+
// independent module instances — with local `Symbol(...)` the identity would
|
|
208
|
+
// differ, and `inject()` would always throw "not found". `Symbol.for(...)`
|
|
209
|
+
// resolves both imports to the same symbol.
|
|
210
|
+
|
|
211
|
+
/** Vue inject key for `useSuperAdminBrand()`. */
|
|
212
|
+
export const SUPER_ADMIN_BRAND_KEY: InjectionKey<SuperAdminBrand> = Symbol.for(
|
|
213
|
+
'@saasicat/ui-vue/SUPER_ADMIN_BRAND',
|
|
214
|
+
);
|
|
215
|
+
/** Vue inject key for `useSuperAdminEndpoints()`. */
|
|
216
|
+
export const SUPER_ADMIN_ENDPOINTS_KEY: InjectionKey<Required<SuperAdminEndpoints>> = Symbol.for(
|
|
217
|
+
'@saasicat/ui-vue/SUPER_ADMIN_ENDPOINTS',
|
|
218
|
+
);
|
|
219
|
+
/** Vue inject key for `useSuperAdminExtensions()`. */
|
|
220
|
+
export const SUPER_ADMIN_EXTENSIONS_KEY: InjectionKey<ExtensionsMap> = Symbol.for(
|
|
221
|
+
'@saasicat/ui-vue/SUPER_ADMIN_EXTENSIONS',
|
|
222
|
+
);
|
|
223
|
+
/** Vue inject key for `useSuperAdminActions()`. */
|
|
224
|
+
export const SUPER_ADMIN_ACTIONS_KEY: InjectionKey<ActionsMap> = Symbol.for(
|
|
225
|
+
'@saasicat/ui-vue/SUPER_ADMIN_ACTIONS',
|
|
226
|
+
);
|
|
227
|
+
/**
|
|
228
|
+
* Vue inject key for the manifest accessor. Only provided when
|
|
229
|
+
* `manifestGuard.getManifest` was passed to `createSuperAdminApp()` — the
|
|
230
|
+
* `<ProjectPageHost>` needs it to resolve project pages.
|
|
231
|
+
*/
|
|
232
|
+
export const SUPER_ADMIN_MANIFEST_KEY: InjectionKey<() => AdminManifest | null> = Symbol.for(
|
|
233
|
+
'@saasicat/ui-vue/SUPER_ADMIN_MANIFEST',
|
|
234
|
+
);
|
|
235
|
+
/** Vue inject key for `useSuperAdminLoginAdapter()`. */
|
|
236
|
+
export const SUPER_ADMIN_LOGIN_ADAPTER_KEY: InjectionKey<SuperAdminLoginAdapter> = Symbol.for(
|
|
237
|
+
'@saasicat/ui-vue/SUPER_ADMIN_LOGIN_ADAPTER',
|
|
238
|
+
);
|
|
239
|
+
/** Vue inject key for `useSuperAdminHttp()` (pre-login HttpClient). */
|
|
240
|
+
export const SUPER_ADMIN_HTTP_KEY: InjectionKey<HttpClient> = Symbol.for(
|
|
241
|
+
'@saasicat/ui-vue/SUPER_ADMIN_HTTP',
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const DEFAULT_QUASAR_OPTIONS: QuasarPluginOptions = {
|
|
245
|
+
plugins: { Notify, Dialog, Loading },
|
|
246
|
+
config: { notify: { position: 'top-right', timeout: 3000 } },
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Universal bootstrap function for SuperAdmin apps. Replaces the `main.ts`
|
|
251
|
+
* boilerplate duplicated per app today (Quasar + Pinia + Router + manifest
|
|
252
|
+
* guard) and exposes the platform maps via `provide()` for downstream
|
|
253
|
+
* components.
|
|
254
|
+
*/
|
|
255
|
+
export function createSuperAdminApp(options: CreateSuperAdminAppOptions): SuperAdminAppHandle {
|
|
256
|
+
const app = createApp(options.rootComponent);
|
|
257
|
+
|
|
258
|
+
app.use(Quasar, options.quasarOptions ?? DEFAULT_QUASAR_OPTIONS);
|
|
259
|
+
|
|
260
|
+
const pinia = createPinia();
|
|
261
|
+
app.use(pinia);
|
|
262
|
+
|
|
263
|
+
const router = createRouter({
|
|
264
|
+
history: options.routerHistory ?? createWebHistory(),
|
|
265
|
+
routes: options.appRoutes,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const navGuard = buildNavigationGuard(options);
|
|
269
|
+
if (navGuard) {
|
|
270
|
+
router.beforeEach(navGuard);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
app.use(router);
|
|
274
|
+
|
|
275
|
+
const endpoints: Required<SuperAdminEndpoints> = {
|
|
276
|
+
apiBase: options.endpoints.apiBase,
|
|
277
|
+
publicBootEndpoint:
|
|
278
|
+
options.endpoints.publicBootEndpoint ?? `${options.endpoints.apiBase}/boot`,
|
|
279
|
+
manifestEndpoint:
|
|
280
|
+
options.endpoints.manifestEndpoint ?? `${options.endpoints.apiBase}/manifest`,
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
app.provide(SUPER_ADMIN_BRAND_KEY, { tag: 'SuperAdmin', ...options.brand });
|
|
284
|
+
app.provide(SUPER_ADMIN_ENDPOINTS_KEY, endpoints);
|
|
285
|
+
app.provide(SUPER_ADMIN_EXTENSIONS_KEY, options.extensions ?? {});
|
|
286
|
+
app.provide(SUPER_ADMIN_ACTIONS_KEY, options.actions ?? {});
|
|
287
|
+
if (options.manifestGuard?.getManifest) {
|
|
288
|
+
app.provide(SUPER_ADMIN_MANIFEST_KEY, options.manifestGuard.getManifest);
|
|
289
|
+
}
|
|
290
|
+
if (options.loginAdapter) {
|
|
291
|
+
app.provide(SUPER_ADMIN_LOGIN_ADAPTER_KEY, options.loginAdapter);
|
|
292
|
+
}
|
|
293
|
+
app.provide(SUPER_ADMIN_HTTP_KEY, options.http ?? defaultHttpClient());
|
|
294
|
+
|
|
295
|
+
for (const plugin of options.installPlugins ?? []) {
|
|
296
|
+
plugin(app);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
app,
|
|
301
|
+
router,
|
|
302
|
+
pinia,
|
|
303
|
+
mount: (selector) => app.mount(selector),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Internal helper, exported for isolated unit tests of the navigation
|
|
309
|
+
* behavior (auth redirect, manifest fail-closed path). Consumers should call
|
|
310
|
+
* `createSuperAdminApp()`, not this helper directly.
|
|
311
|
+
*/
|
|
312
|
+
export function buildNavigationGuard(
|
|
313
|
+
options: Pick<CreateSuperAdminAppOptions, 'authGuard' | 'manifestGuard'>,
|
|
314
|
+
): NavigationGuardWithThis<undefined> | null {
|
|
315
|
+
const { authGuard, manifestGuard } = options;
|
|
316
|
+
if (!authGuard && !manifestGuard) return null;
|
|
317
|
+
|
|
318
|
+
return async (to: RouteLocationNormalized) => {
|
|
319
|
+
if (to.meta?.public === true) return true;
|
|
320
|
+
|
|
321
|
+
if (authGuard) {
|
|
322
|
+
if (!authGuard.isAuthenticated()) return authGuard.onUnauthenticated();
|
|
323
|
+
if (authGuard.isSuperAdmin && !authGuard.isSuperAdmin()) {
|
|
324
|
+
return authGuard.onUnauthenticated();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (manifestGuard) {
|
|
329
|
+
try {
|
|
330
|
+
await manifestGuard.ensureLoaded();
|
|
331
|
+
} catch (err) {
|
|
332
|
+
if (manifestGuard.errorRoute && to.path !== manifestGuard.errorRoute) {
|
|
333
|
+
return manifestGuard.errorRoute;
|
|
334
|
+
}
|
|
335
|
+
// No errorRoute configured — defensive fallback: warn, allow
|
|
336
|
+
// render. The app can detect the error via the store
|
|
337
|
+
// (`error.value`) and render a banner/stub.
|
|
338
|
+
|
|
339
|
+
console.error('[SuperAdmin] manifest load failed', err);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return true;
|
|
344
|
+
};
|
|
345
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Entitlement provider — app-wide inject key for FeatureGate / router guard.
|
|
2
|
+
//
|
|
3
|
+
// Consumers call `provideEntitlement(app, ent)` once at app bootstrap,
|
|
4
|
+
// where `ent` is the result of `useEntitlement({...})`. Afterwards
|
|
5
|
+
// <FeatureGate> components and the router guard can access it without
|
|
6
|
+
// reconfiguring the endpoint.
|
|
7
|
+
//
|
|
8
|
+
// `Symbol.for(...)` so that multi-bundle setups (app bundle + pages bundle)
|
|
9
|
+
// resolve to the same identity — analogous to the inject keys in
|
|
10
|
+
// create-super-admin-app.
|
|
11
|
+
//
|
|
12
|
+
// Spec: handoff/superadmin/QUICKSTART_SIMPLIFICATIONS.md §P8.
|
|
13
|
+
|
|
14
|
+
import { inject, type App, type InjectionKey } from 'vue';
|
|
15
|
+
import type { UseEntitlementResult } from './use-entitlement.js';
|
|
16
|
+
|
|
17
|
+
export const ENTITLEMENT_INJECTION_KEY: InjectionKey<UseEntitlementResult> = Symbol.for(
|
|
18
|
+
'@saasicat/ui-vue/ENTITLEMENT',
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Binds a `useEntitlement(...)` result for the whole app so that
|
|
23
|
+
* `<FeatureGate>` and router guards can inject it.
|
|
24
|
+
*/
|
|
25
|
+
export function provideEntitlement(app: App, entitlement: UseEntitlementResult): void {
|
|
26
|
+
app.provide(ENTITLEMENT_INJECTION_KEY, entitlement);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Fetches the bound entitlement result. Returns `null` when the
|
|
31
|
+
* consumer did not call `provideEntitlement(...)` — `<FeatureGate>`
|
|
32
|
+
* then falls back to "everything allowed" (with a dev warning).
|
|
33
|
+
*/
|
|
34
|
+
export function useInjectedEntitlement(): UseEntitlementResult | null {
|
|
35
|
+
return inject(ENTITLEMENT_INJECTION_KEY, null);
|
|
36
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Feature router guard — Vue Router `beforeEach` that checks routes with
|
|
2
|
+
// `meta.requiresFeature` against the entitlement source of truth.
|
|
3
|
+
//
|
|
4
|
+
// Usage (in the app router setup, alternatively via createSuperAdminApp):
|
|
5
|
+
//
|
|
6
|
+
// ```ts
|
|
7
|
+
// import { useEntitlement, buildFeatureRouterGuard } from '@saasicat/ui-vue';
|
|
8
|
+
// const ent = useEntitlement({ endpoint: '/api/billing/entitlement' });
|
|
9
|
+
// router.beforeEach(buildFeatureRouterGuard({
|
|
10
|
+
// getEntitlement: () => ent,
|
|
11
|
+
// redirectTo: '/upgrade',
|
|
12
|
+
// }));
|
|
13
|
+
// ```
|
|
14
|
+
//
|
|
15
|
+
// Route meta:
|
|
16
|
+
//
|
|
17
|
+
// ```ts
|
|
18
|
+
// { path: '/dms', component: DmsPage, meta: { requiresFeature: 'DMS' } }
|
|
19
|
+
// { path: '/x', component: XPage, meta: { requiresFeature: ['DMS', 'STORAGE_PRO'] } }
|
|
20
|
+
// ```
|
|
21
|
+
//
|
|
22
|
+
// Spec: handoff/superadmin/QUICKSTART_SIMPLIFICATIONS.md §P8.
|
|
23
|
+
|
|
24
|
+
import type { NavigationGuardWithThis, RouteLocationNormalized } from 'vue-router';
|
|
25
|
+
import type { UseEntitlementResult } from './use-entitlement.js';
|
|
26
|
+
|
|
27
|
+
export interface FeatureRouterGuardOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Returns the current entitlement. Deliberately a factory (instead of
|
|
30
|
+
* passing a ref directly), so the app can lazy-load the entitlement and
|
|
31
|
+
* re-bind it — e.g. after a plan change or logout/re-login.
|
|
32
|
+
*/
|
|
33
|
+
getEntitlement: () => UseEntitlementResult | null;
|
|
34
|
+
/**
|
|
35
|
+
* Where to redirect when a feature is missing. Default: no redirect, but
|
|
36
|
+
* `next(false)` (route blocks). Apps with an upgrade page set
|
|
37
|
+
* `'/upgrade'`.
|
|
38
|
+
*/
|
|
39
|
+
redirectTo?: string;
|
|
40
|
+
/**
|
|
41
|
+
* `true` (default): entitlement not yet loaded → let through,
|
|
42
|
+
* so the first render doesn't hang. `false`: blocks until the entitlement
|
|
43
|
+
* is there (user sees a white screen if the endpoint is slow — only
|
|
44
|
+
* sensible in apps with pre-login load).
|
|
45
|
+
*/
|
|
46
|
+
allowWhileLoading?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function extractRequired(to: RouteLocationNormalized): string[] {
|
|
50
|
+
const raw = (to.meta as { requiresFeature?: string | string[] } | undefined)?.requiresFeature;
|
|
51
|
+
if (!raw) return [];
|
|
52
|
+
return Array.isArray(raw) ? raw : [raw];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function buildFeatureRouterGuard(
|
|
56
|
+
options: FeatureRouterGuardOptions,
|
|
57
|
+
): NavigationGuardWithThis<undefined> {
|
|
58
|
+
return function featureGuard(to, _from, next) {
|
|
59
|
+
const required = extractRequired(to);
|
|
60
|
+
if (required.length === 0) return next();
|
|
61
|
+
const ent = options.getEntitlement();
|
|
62
|
+
if (!ent) {
|
|
63
|
+
// No provider — let through (this prevents the whole app
|
|
64
|
+
// from stalling if the app registered the guard but the
|
|
65
|
+
// entitlement isn't there yet).
|
|
66
|
+
return next();
|
|
67
|
+
}
|
|
68
|
+
const allowLoading = options.allowWhileLoading !== false;
|
|
69
|
+
if (allowLoading && ent.loading.value && ent.entitlement.value === null) {
|
|
70
|
+
return next();
|
|
71
|
+
}
|
|
72
|
+
const allowed = required.some((f) => ent.hasFeature(f));
|
|
73
|
+
if (allowed) return next();
|
|
74
|
+
if (options.redirectTo) return next(options.redirectTo);
|
|
75
|
+
return next(false);
|
|
76
|
+
};
|
|
77
|
+
}
|
package/src/http-json.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Shared JSON helpers over the injectable `HttpClient`. A single
|
|
2
|
+
// request/error path for all pre-login calls (boot, setup status, setup),
|
|
3
|
+
// instead of raw `fetch` with divergent error handling per component. A
|
|
4
|
+
// consumer `HttpClient` (auth header, baseURL, retry) then applies everywhere.
|
|
5
|
+
|
|
6
|
+
import type { HttpClient } from './types.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error for non-2xx responses. `code` is the machine-readable error code
|
|
10
|
+
* from the JSON body (`{ code }`), if present — callers map it to
|
|
11
|
+
* a message (e.g. via `SETUP_ERROR_CODES`).
|
|
12
|
+
*/
|
|
13
|
+
export class HttpJsonError extends Error {
|
|
14
|
+
constructor(
|
|
15
|
+
readonly status: number,
|
|
16
|
+
readonly code?: string,
|
|
17
|
+
) {
|
|
18
|
+
super(code ?? `HTTP ${status}`);
|
|
19
|
+
this.name = 'HttpJsonError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function extractCode(res: { json(): Promise<unknown> }): Promise<string | undefined> {
|
|
24
|
+
try {
|
|
25
|
+
const body = (await res.json()) as { code?: unknown } | null;
|
|
26
|
+
return typeof body?.code === 'string' ? body.code : undefined;
|
|
27
|
+
} catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Removes trailing slashes so an API prefix can be concatenated with paths
|
|
34
|
+
* that start with `/`. Deliberately index-based instead of
|
|
35
|
+
* `replace(/\/+$/, '')`: the regex backtracks quadratically on inputs that
|
|
36
|
+
* end in many slashes.
|
|
37
|
+
*/
|
|
38
|
+
export function trimTrailingSlashes(url: string): string {
|
|
39
|
+
let end = url.length;
|
|
40
|
+
while (end > 0 && url[end - 1] === '/') end--;
|
|
41
|
+
return url.slice(0, end);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function getJson<T>(http: HttpClient, url: string): Promise<T> {
|
|
45
|
+
const res = await http(url);
|
|
46
|
+
if (res.status < 200 || res.status >= 300) {
|
|
47
|
+
throw new HttpJsonError(res.status, await extractCode(res));
|
|
48
|
+
}
|
|
49
|
+
return (await res.json()) as T;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function postJson<T>(http: HttpClient, url: string, body: unknown): Promise<T> {
|
|
53
|
+
const res = await http(url, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify(body),
|
|
57
|
+
});
|
|
58
|
+
if (res.status < 200 || res.status >= 300) {
|
|
59
|
+
throw new HttpJsonError(res.status, await extractCode(res));
|
|
60
|
+
}
|
|
61
|
+
return (await res.json()) as T;
|
|
62
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// @saasicat/ui-vue — Vue 3 components + composables for the
|
|
2
|
+
// SuperAdmin UI shell.
|
|
3
|
+
//
|
|
4
|
+
// P4.1 (Phase 30): public boot loader + manifest loader (ETag cache).
|
|
5
|
+
// P4.2 (Phase 31): nav builder + extension host + action registry.
|
|
6
|
+
// P4.3 (Phase 32): batch column fetcher.
|
|
7
|
+
// P4.4 (Phase 33): composables for standard pages (Tenants, Audit, Entitlement).
|
|
8
|
+
// P4.5 (Phase 34): plan-versions list composables + bulk-publish orchestration.
|
|
9
|
+
//
|
|
10
|
+
// Contents:
|
|
11
|
+
// - types: HttpClient, KvStore, defaultHttpClient, defaultKvStore
|
|
12
|
+
// - boot-loader: BootLoader (framework-agnostic) + BootLoadError
|
|
13
|
+
// - manifest-loader: ManifestLoader with ETag cache + ManifestLoadError
|
|
14
|
+
// - nav-builder: buildRoutes, buildSidebar, resolveExtension
|
|
15
|
+
// - action-registry: ActionRegistry, MissingHandlerError, ActionDefNotInManifestError
|
|
16
|
+
// - batch-column-fetcher: BatchColumnFetcher + BatchColumnDriftError
|
|
17
|
+
// - use-api-list: useApiList<T> generic list composable
|
|
18
|
+
// - use-tenants: useTenants typed wrapper
|
|
19
|
+
// - use-audit-entries: useAuditEntries typed wrapper
|
|
20
|
+
// - use-entitlement: useEntitlement composable
|
|
21
|
+
// - use-plan-versions: usePlanVersionsCatalog
|
|
22
|
+
// - use-bulk-publish: useBulkPublish (parallel publishes with status tracking)
|
|
23
|
+
// - use-plan-editor: usePlanEditor composable (feature discovery, plannedOnly filter)
|
|
24
|
+
// - use-public-boot: usePublicBoot composable
|
|
25
|
+
// - use-manifest: useManifest composable
|
|
26
|
+
// - use-nav: useNav composable
|
|
27
|
+
// - use-actions: useActions composable
|
|
28
|
+
// - use-batch-columns: useBatchColumns composable
|
|
29
|
+
|
|
30
|
+
export * from './version.js';
|
|
31
|
+
export * from './types.js';
|
|
32
|
+
export * from './http-json.js';
|
|
33
|
+
export * from './create-admin-routes.js';
|
|
34
|
+
export * from './boot-loader.js';
|
|
35
|
+
export * from './manifest-loader.js';
|
|
36
|
+
export * from './nav-builder.js';
|
|
37
|
+
export * from './action-registry.js';
|
|
38
|
+
export * from './batch-column-fetcher.js';
|
|
39
|
+
export * from './use-api-list.js';
|
|
40
|
+
export * from './use-tenants.js';
|
|
41
|
+
export * from './use-audit-entries.js';
|
|
42
|
+
export * from './use-entitlement.js';
|
|
43
|
+
export * from './use-tenant-manifest.js';
|
|
44
|
+
export * from './entitlement-provider.js';
|
|
45
|
+
export * from './feature-router-guard.js';
|
|
46
|
+
// FeatureGate.vue is not bundled — consumers import it directly:
|
|
47
|
+
// import FeatureGate from '@saasicat/ui-vue/components/FeatureGate.vue';
|
|
48
|
+
export * from './use-tenant-billing-catalog.js';
|
|
49
|
+
export * from './use-tenant-billing.js';
|
|
50
|
+
export * from './use-subscription-draft.js';
|
|
51
|
+
export * from './pages-tenant/default-i18n.js';
|
|
52
|
+
export * from './use-plan-versions.js';
|
|
53
|
+
export * from './use-bulk-publish.js';
|
|
54
|
+
export * from './use-plan-editor.js';
|
|
55
|
+
export * from './use-public-boot.js';
|
|
56
|
+
export * from './use-discovery.js';
|
|
57
|
+
export * from './use-catalog-entries.js';
|
|
58
|
+
export * from './use-bundles.js';
|
|
59
|
+
export * from './use-bundle-versions-map.js';
|
|
60
|
+
export * from './use-tenant-subscription-bundles.js';
|
|
61
|
+
export * from './use-business-types.js';
|
|
62
|
+
export * from './use-marketing-projections.js';
|
|
63
|
+
export * from './use-promotions.js';
|
|
64
|
+
export * from './use-plans.js';
|
|
65
|
+
export * from './use-live-plan-versions.js';
|
|
66
|
+
export * from './use-manifest.js';
|
|
67
|
+
export * from './use-nav.js';
|
|
68
|
+
export * from './use-actions.js';
|
|
69
|
+
export * from './use-tenant-action-flow.js';
|
|
70
|
+
export * from './use-platform-tenant-actions.js';
|
|
71
|
+
export * from './use-batch-columns.js';
|
|
72
|
+
export * from './plan-versions-catalog.js';
|
|
73
|
+
// Platform dialog types for app wrappers that type submit handlers.
|
|
74
|
+
export * from './components/dialogs/types.js';
|
|
75
|
+
// Pure resolvers for translated feature/quota labels in the bundle editors.
|
|
76
|
+
export * from './components/bundle-editor/catalog-i18n.js';
|
|
77
|
+
// Types of the shared PlatformEmailPage (platform email sender).
|
|
78
|
+
export * from './pages-standard/platform-email.types.js';
|
|
79
|
+
// Types of the shared EmailHistoryPage (platform email history).
|
|
80
|
+
export * from './pages-standard/email-history.types.js';
|
|
81
|
+
// Universal bootstrap helper for SuperAdmin apps + associated composables.
|
|
82
|
+
export * from './create-super-admin-app.js';
|
|
83
|
+
export * from './use-super-admin-context.js';
|
|
84
|
+
// Loader factory that builds BootLoader + ManifestLoader from the same
|
|
85
|
+
// endpoint configuration as createSuperAdminApp() — so that endpoints live
|
|
86
|
+
// in a single place per app.
|
|
87
|
+
export * from './platform-loaders.js';
|
|
88
|
+
// ProjectPageHost: resolves manifest projectPages dynamically against the
|
|
89
|
+
// extensions: map — replaces static route duplication in the apps.
|
|
90
|
+
export * from './project-page-host.js';
|
|
91
|
+
// Manifest Pinia store factory — standardizes ensureLoaded/reload/clearCache.
|
|
92
|
+
export * from './manifest-store-factory.js';
|
|
93
|
+
|
|
94
|
+
// The shared SuperAdmin LoginPage lives at
|
|
95
|
+
// `@saasicat/ui-vue/pages-standard/SuperAdminLoginPage.vue` —
|
|
96
|
+
// not via bundle (tsup ignores .vue), but directly from src/ through the
|
|
97
|
+
// subpath export. Apps pass a `loginAdapter` via createSuperAdminApp()
|
|
98
|
+
// and route `/login` to this component.
|