@saasicat/ui-vue 0.4.0 → 0.6.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/README.md +64 -4
- package/dist/chunk-A3W2N26K.js +714 -0
- package/dist/chunk-QZOSDCNP.js +98 -0
- package/dist/chunk-X5SQ5HMB.js +3231 -0
- package/dist/client/index.cjs +3988 -0
- package/dist/client/index.d.cts +321 -0
- package/dist/client/index.d.ts +321 -0
- package/dist/client/index.js +70 -0
- package/dist/index.cjs +4446 -1249
- package/dist/index.d.cts +297 -888
- package/dist/index.d.ts +297 -888
- package/dist/index.js +609 -1348
- package/dist/messages-7b0P8W75.d.cts +1652 -0
- package/dist/messages-7b0P8W75.d.ts +1652 -0
- package/dist/quasar/index.cjs +3340 -0
- package/dist/quasar/index.d.cts +95 -0
- package/dist/quasar/index.d.ts +95 -0
- package/dist/quasar/index.js +96 -0
- package/dist/testing-e2e/admin-pages-suite.cjs +14 -11
- package/dist/testing-e2e/admin-pages-suite.js +14 -11
- package/dist/use-super-admin-i18n-B3v3-SWZ.d.ts +210 -0
- package/dist/use-super-admin-i18n-DMktRVEt.d.cts +210 -0
- package/package.json +23 -3
- package/src/{action-registry.ts → client/action-registry.ts} +4 -4
- package/src/{batch-column-fetcher.ts → client/batch-column-fetcher.ts} +4 -4
- package/src/{boot-loader.ts → client/boot-loader.ts} +3 -3
- package/src/client/i18n/currency.ts +42 -0
- package/src/client/i18n/define.ts +59 -0
- package/src/client/i18n/format.ts +12 -0
- package/src/client/i18n/index.ts +14 -0
- package/src/client/i18n/locale.ts +15 -0
- package/src/client/i18n/messages/audit.ts +42 -0
- package/src/client/i18n/messages/bundles.ts +459 -0
- package/src/client/i18n/messages/common.ts +147 -0
- package/src/client/i18n/messages/dashboard.ts +16 -0
- package/src/client/i18n/messages/discovery.ts +229 -0
- package/src/client/i18n/messages/email.ts +125 -0
- package/src/client/i18n/messages/marketing.ts +270 -0
- package/src/client/i18n/messages/nav.ts +72 -0
- package/src/client/i18n/messages/pilots.ts +192 -0
- package/src/client/i18n/messages/plan-detail.ts +266 -0
- package/src/client/i18n/messages/plan-editor.ts +177 -0
- package/src/client/i18n/messages/plan-versions.ts +251 -0
- package/src/client/i18n/messages/plans.ts +503 -0
- package/src/client/i18n/messages/promos.ts +202 -0
- package/src/client/i18n/messages/shell.ts +145 -0
- package/src/client/i18n/messages/tenants.ts +104 -0
- package/src/client/i18n/messages/users.ts +78 -0
- package/src/client/i18n/messages.ts +84 -0
- package/src/client/index.ts +27 -0
- package/src/{manifest-loader.ts → client/manifest-loader.ts} +4 -4
- package/src/{nav-builder.ts → client/nav-builder.ts} +44 -51
- package/src/{plan-versions-catalog.ts → client/plan-versions-catalog.ts} +42 -30
- package/src/components/BundleVersionPublishDialog.vue +35 -24
- package/src/components/FeatureGate.vue +1 -1
- package/src/components/MarketingPromotionsTab.vue +112 -59
- package/src/components/MfaPromptDialog.vue +11 -11
- package/src/components/TenantActionConfirmDialog.vue +19 -17
- package/src/components/VersionDiffPreview.vue +72 -43
- package/src/components/bundle-editor/BundleCreatePanel.vue +76 -63
- package/src/components/bundle-editor/BundleFeaturesEditor.vue +13 -6
- package/src/components/bundle-editor/BundlePlanCompatPicker.vue +19 -14
- package/src/components/bundle-editor/BundleQuotasEditor.vue +7 -4
- package/src/components/bundle-editor/BundleStatusBanner.vue +36 -19
- package/src/components/bundle-editor/BundleVersionInlineEditor.vue +51 -35
- package/src/components/bundle-editor/BundleVersionStrip.vue +24 -12
- package/src/components/bundle-editor/bundle-version-status.ts +29 -29
- package/src/components/dialogs/PilotCreateDialog.vue +58 -63
- package/src/components/dialogs/PilotEditDialog.vue +40 -34
- package/src/components/dialogs/PromoCodeCreateDialog.vue +89 -63
- package/src/components/dialogs/PromoCodeEditDialog.vue +98 -66
- package/src/components/dialogs/types.ts +3 -13
- package/src/components/plan/PlanCycleToggle.vue +21 -10
- package/src/components/plan/PlanGrid.vue +2 -2
- package/src/components/plan/PriceSummary.vue +1 -1
- package/src/components/plan/PromoCodeInput.vue +1 -1
- package/src/components/plan/PublicBundleGrid.vue +1 -1
- package/src/components/plan-cockpit/PlanCockpit.vue +34 -15
- package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +13 -6
- package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +18 -9
- package/src/components/plan-cockpit/PlanCockpitHeader.vue +9 -6
- package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +24 -11
- package/src/components/plan-cockpit/PlanCockpitKpis.vue +28 -17
- package/src/components/plan-cockpit/PlanCockpitVersions.vue +50 -25
- package/src/components/plan-create-dialog/PlanCreateDialog.vue +45 -35
- package/src/components/plan-detail/PlanAuditLog.vue +11 -6
- package/src/components/plan-detail/PlanDetail.vue +21 -23
- package/src/components/plan-detail/PlanDetailHeader.vue +20 -11
- package/src/components/plan-detail/PlanDetailKpis.vue +30 -17
- package/src/components/plan-detail/PlanTerminateDialog.vue +22 -9
- package/src/components/plan-detail/PlanVersionDiffPanel.vue +61 -27
- package/src/components/plan-detail/PlanVersionsPanel.vue +71 -36
- package/src/components/plan-list/PlanList.vue +99 -57
- package/src/components/plan-matrix/PlanMatrix.vue +85 -38
- package/src/components/plan-review/PlanReview.vue +105 -80
- package/src/components/plan-version-editor/PlanCatalogPreview.vue +43 -20
- package/src/components/plan-version-editor/PlanComponentPool.vue +15 -7
- package/src/components/plan-version-editor/PlanVersionBasket.vue +45 -24
- package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +8 -7
- package/src/components/plan-version-editor/PlanVersionEditor.vue +74 -40
- package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +24 -12
- package/src/index.ts +82 -79
- package/src/pages-standard/AdminLayout.vue +43 -18
- package/src/pages-standard/AdminManifestErrorPage.vue +10 -10
- package/src/pages-standard/AuditPage.vue +58 -19
- package/src/pages-standard/BundlesPage.vue +29 -18
- package/src/pages-standard/DashboardPage.vue +31 -17
- package/src/pages-standard/DiscoveryPage.vue +40 -28
- package/src/pages-standard/EmailHistoryPage.vue +98 -69
- package/src/pages-standard/MarketingCatalogPage.vue +40 -21
- package/src/pages-standard/PilotsPage.vue +64 -52
- package/src/pages-standard/PlanVersionsPage.vue +9 -5
- package/src/pages-standard/PlansPage.vue +91 -59
- package/src/pages-standard/PlatformEmailPage.vue +120 -53
- package/src/pages-standard/PromoCodeDetailPage.vue +66 -63
- package/src/pages-standard/PromoCodesPage.vue +51 -54
- package/src/pages-standard/SubscriptionsPage.vue +14 -9
- package/src/pages-standard/SuperAdminLoginPage.vue +19 -16
- package/src/pages-standard/SuperAdminSetupWizard.vue +53 -49
- package/src/pages-standard/TenantDetailPage.vue +54 -40
- package/src/pages-standard/TenantsPage.vue +57 -44
- package/src/pages-standard/UsersPage.vue +70 -52
- package/src/pages-standard/bundles-page/BundleAccordionList.vue +20 -8
- package/src/pages-standard/bundles-page/BundleDetailPanel.vue +28 -18
- package/src/pages-standard/bundles-page/BundlesFilterBar.vue +4 -1
- package/src/pages-standard/bundles-page/BundlesHeader.vue +9 -8
- package/src/pages-standard/bundles-page/BundlesKpis.vue +29 -11
- package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +20 -8
- package/src/pages-standard/discovery-page/DiscoveryCapList.vue +5 -3
- package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +43 -24
- package/src/pages-standard/discovery-page/DiscoveryHeader.vue +7 -4
- package/src/pages-standard/discovery-page/DiscoveryKpis.vue +28 -15
- package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +20 -10
- package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +9 -6
- package/src/pages-standard/discovery-page/discovery-ui.ts +40 -52
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +81 -49
- package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +13 -10
- package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +24 -20
- package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +6 -3
- package/src/pages-standard/plan-versions/PlanDiffCard.vue +16 -6
- package/src/pages-standard/plan-versions/PlanVersionHeader.vue +42 -26
- package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +46 -27
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +8 -6
- package/src/pages-standard/plan-versions/PlanVersionsList.vue +40 -17
- package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +1 -1
- package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +25 -13
- package/src/pages-standard/plan-versions/format.ts +33 -17
- package/src/pages-standard/plans-page/PlanArchiveDialog.vue +12 -8
- package/src/pages-standard/plans-page/PlanBundleOverview.vue +15 -6
- package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +18 -7
- package/src/pages-standard/plans-page/PlanPublishDialog.vue +23 -12
- package/src/pages-standard/tenants/format.ts +3 -2
- package/src/pages-tenant/MySubscriptionBundlesPage.vue +73 -38
- package/src/pages-tenant/OnboardingConfigurator.vue +3 -3
- package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
- package/src/pages-tenant/PlanChangeWizard.vue +10 -6
- package/src/pages-tenant/TenantPlanSection.vue +20 -16
- package/src/pages-tenant/default-i18n.ts +217 -1
- package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +9 -7
- package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +11 -12
- package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +4 -1
- package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +1 -1
- package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +1 -1
- package/src/quasar/create-super-admin-app.ts +192 -0
- package/src/quasar/index.ts +11 -0
- package/src/quasar/notify.ts +30 -0
- package/src/testing-e2e/admin-pages-suite.ts +14 -11
- package/src/{manifest-store-factory.ts → vue/manifest-store-factory.ts} +1 -1
- package/src/{platform-loaders.ts → vue/platform-loaders.ts} +4 -4
- package/src/{project-page-host.ts → vue/project-page-host.ts} +6 -4
- package/src/{create-super-admin-app.ts → vue/super-admin-context.ts} +20 -139
- package/src/vue/ui-notify.ts +30 -0
- package/src/{use-actions.ts → vue/use-actions.ts} +1 -1
- package/src/{use-api-list.ts → vue/use-api-list.ts} +1 -1
- package/src/{use-audit-entries.ts → vue/use-audit-entries.ts} +3 -3
- package/src/{use-batch-columns.ts → vue/use-batch-columns.ts} +1 -1
- package/src/{use-bulk-publish.ts → vue/use-bulk-publish.ts} +7 -5
- package/src/{use-bundle-versions-map.ts → vue/use-bundle-versions-map.ts} +7 -5
- package/src/{use-bundles.ts → vue/use-bundles.ts} +14 -14
- package/src/{use-catalog-entries.ts → vue/use-catalog-entries.ts} +14 -11
- package/src/{use-discovery.ts → vue/use-discovery.ts} +9 -6
- package/src/{use-entitlement.ts → vue/use-entitlement.ts} +4 -4
- package/src/{use-live-plan-versions.ts → vue/use-live-plan-versions.ts} +5 -5
- package/src/{use-manifest.ts → vue/use-manifest.ts} +1 -1
- package/src/{use-marketing-projections.ts → vue/use-marketing-projections.ts} +10 -7
- package/src/{use-nav.ts → vue/use-nav.ts} +9 -3
- package/src/{use-plan-editor.ts → vue/use-plan-editor.ts} +2 -2
- package/src/{use-plan-versions.ts → vue/use-plan-versions.ts} +3 -3
- package/src/{use-plans.ts → vue/use-plans.ts} +13 -13
- package/src/{use-platform-tenant-actions.ts → vue/use-platform-tenant-actions.ts} +9 -6
- package/src/{use-promotions.ts → vue/use-promotions.ts} +10 -11
- package/src/{use-public-boot.ts → vue/use-public-boot.ts} +1 -1
- package/src/{use-subscription-draft.ts → vue/use-subscription-draft.ts} +2 -3
- package/src/{use-super-admin-context.ts → vue/use-super-admin-context.ts} +5 -5
- package/src/vue/use-super-admin-i18n.ts +82 -0
- package/src/{use-tenant-action-flow.ts → vue/use-tenant-action-flow.ts} +12 -7
- package/src/{use-tenant-billing-catalog.ts → vue/use-tenant-billing-catalog.ts} +2 -2
- package/src/{use-tenant-billing.ts → vue/use-tenant-billing.ts} +2 -2
- package/src/{use-tenant-manifest.ts → vue/use-tenant-manifest.ts} +2 -4
- package/src/{use-tenant-subscription-bundles.ts → vue/use-tenant-subscription-bundles.ts} +5 -5
- package/src/{use-tenants.ts → vue/use-tenants.ts} +3 -3
- package/dist/testing-e2e/admin-pages-suite.d.cts +0 -37
- package/dist/testing-e2e/admin-pages-suite.d.ts +0 -37
- package/src/components/BusinessTypeVersionEditorDialog.vue +0 -417
- package/src/components/BusinessTypeVersionPublishDialog.vue +0 -258
- package/src/pages-standard/BusinessTypesPage.vue +0 -662
- package/src/pages-standard/plan-versions/VersionDiffPreview.vue +0 -167
- package/src/use-business-types.ts +0 -269
- /package/src/{http-json.ts → client/http-json.ts} +0 -0
- /package/src/{types.ts → client/types.ts} +0 -0
- /package/src/{version.ts → client/version.ts} +0 -0
- /package/src/{create-admin-routes.ts → vue/create-admin-routes.ts} +0 -0
- /package/src/{entitlement-provider.ts → vue/entitlement-provider.ts} +0 -0
- /package/src/{feature-router-guard.ts → vue/feature-router-guard.ts} +0 -0
|
@@ -109,7 +109,7 @@ export function runAdminPagesSuite(
|
|
|
109
109
|
|
|
110
110
|
test.describe(`${config.appName}: SuperAdmin-Pages Smoke`, () => {
|
|
111
111
|
for (const pageDef of config.pages) {
|
|
112
|
-
test(`${pageDef.name}
|
|
112
|
+
test(`${pageDef.name} renders without errors`, async ({ page }) => {
|
|
113
113
|
const errors = attachListeners(
|
|
114
114
|
page,
|
|
115
115
|
config.consoleErrorAllowlist,
|
|
@@ -127,19 +127,19 @@ export function runAdminPagesSuite(
|
|
|
127
127
|
timeout: 5_000,
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
|
-
expect(errors.pageErrors, `pageerror
|
|
130
|
+
expect(errors.pageErrors, `pageerror events on ${pageDef.path}`).toHaveLength(0);
|
|
131
131
|
expect(
|
|
132
132
|
errors.consoleErrors,
|
|
133
|
-
`console.error
|
|
133
|
+
`console.error events on ${pageDef.path}`,
|
|
134
134
|
).toHaveLength(0);
|
|
135
135
|
expect(
|
|
136
136
|
errors.networkErrors,
|
|
137
|
-
`HTTP
|
|
137
|
+
`HTTP errors on /api/ during ${pageDef.path}`,
|
|
138
138
|
).toHaveLength(0);
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
test('Dashboard
|
|
142
|
+
test('Dashboard shows the expected KPIs, distributions and shortcuts', async ({ page }) => {
|
|
143
143
|
attachListeners(page, config.consoleErrorAllowlist);
|
|
144
144
|
await loginIfNeeded(page, config);
|
|
145
145
|
const origin = new URL(config.loginUrl).origin;
|
|
@@ -151,7 +151,9 @@ export function runAdminPagesSuite(
|
|
|
151
151
|
const kpiLabels = await page.locator('.sa-kpi__label').allTextContents();
|
|
152
152
|
const normalizedKpis = kpiLabels.map((s) => s.trim());
|
|
153
153
|
for (const expected of config.expectedKpiLabels) {
|
|
154
|
-
expect(normalizedKpis, `KPI '${expected}'
|
|
154
|
+
expect(normalizedKpis, `KPI '${expected}' missing on the dashboard`).toContain(
|
|
155
|
+
expected,
|
|
156
|
+
);
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
if (config.expectedDistributionTitles?.length) {
|
|
@@ -162,7 +164,7 @@ export function runAdminPagesSuite(
|
|
|
162
164
|
for (const expected of config.expectedDistributionTitles) {
|
|
163
165
|
expect(
|
|
164
166
|
normalizedDist,
|
|
165
|
-
`Distribution '${expected}'
|
|
167
|
+
`Distribution '${expected}' missing on the dashboard`,
|
|
166
168
|
).toContain(expected);
|
|
167
169
|
}
|
|
168
170
|
}
|
|
@@ -172,14 +174,15 @@ export function runAdminPagesSuite(
|
|
|
172
174
|
.allTextContents();
|
|
173
175
|
const normalizedShortcuts = shortcutTitles.map((s) => s.trim());
|
|
174
176
|
for (const expected of config.expectedShortcutTitles) {
|
|
175
|
-
expect(
|
|
176
|
-
|
|
177
|
-
|
|
177
|
+
expect(
|
|
178
|
+
normalizedShortcuts,
|
|
179
|
+
`Shortcut '${expected}' missing on the dashboard`,
|
|
180
|
+
).toContain(expected);
|
|
178
181
|
}
|
|
179
182
|
for (const forbidden of config.forbiddenShortcutTitles ?? []) {
|
|
180
183
|
expect(
|
|
181
184
|
normalizedShortcuts,
|
|
182
|
-
`Shortcut '${forbidden}'
|
|
185
|
+
`Shortcut '${forbidden}' must not be on the dashboard`,
|
|
183
186
|
).not.toContain(forbidden);
|
|
184
187
|
}
|
|
185
188
|
});
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { defineStore, type StoreDefinition } from 'pinia';
|
|
14
14
|
import { ref, type Ref } from 'vue';
|
|
15
15
|
import type { AdminManifest } from '@saasicat/types';
|
|
16
|
-
import type { ManifestLoader } from '
|
|
16
|
+
import type { ManifestLoader } from '../client/manifest-loader.js';
|
|
17
17
|
|
|
18
18
|
export interface ManifestStoreState {
|
|
19
19
|
manifest: Ref<AdminManifest | null>;
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
// a single constant that `main.ts` passes to `createSuperAdminApp()` and that
|
|
6
6
|
// the loader wiring from this helper consumes.
|
|
7
7
|
|
|
8
|
-
import { BootLoader } from '
|
|
9
|
-
import { ManifestLoader } from '
|
|
10
|
-
import type { SuperAdminEndpoints } from './
|
|
11
|
-
import type { HttpClient, KvStore } from '
|
|
8
|
+
import { BootLoader } from '../client/boot-loader.js';
|
|
9
|
+
import { ManifestLoader } from '../client/manifest-loader.js';
|
|
10
|
+
import type { SuperAdminEndpoints } from './super-admin-context.js';
|
|
11
|
+
import type { HttpClient, KvStore } from '../client/types.js';
|
|
12
12
|
|
|
13
13
|
export interface CreatePlatformLoadersOptions {
|
|
14
14
|
/**
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
SUPER_ADMIN_EXTENSIONS_KEY,
|
|
15
15
|
SUPER_ADMIN_MANIFEST_KEY,
|
|
16
16
|
type ExtensionsMap,
|
|
17
|
-
} from './
|
|
17
|
+
} from './super-admin-context.js';
|
|
18
18
|
|
|
19
19
|
// `defineAsyncComponent` gets its own wrapper component per loader
|
|
20
20
|
// function — without a cache, re-renders would recreate the same component
|
|
@@ -60,13 +60,15 @@ export const ProjectPageHost = defineComponent({
|
|
|
60
60
|
const page = matchingPage.value;
|
|
61
61
|
const cmp = resolvedComponent.value;
|
|
62
62
|
if (page && cmp) return h(cmp);
|
|
63
|
+
// Misconfiguration diagnostics — developer-facing, therefore not
|
|
64
|
+
// part of the localized catalog.
|
|
63
65
|
return h('div', { class: 'sa-project-page-host__missing', role: 'alert' }, [
|
|
64
|
-
h('h2', '
|
|
66
|
+
h('h2', 'Page not available'),
|
|
65
67
|
h(
|
|
66
68
|
'p',
|
|
67
69
|
page
|
|
68
|
-
? `componentKey "${page.componentKey}"
|
|
69
|
-
: `
|
|
70
|
+
? `componentKey "${page.componentKey}" is not registered in the extensions: map. Apps must register the component in createSuperAdminApp({ extensions }).`
|
|
71
|
+
: `No manifest definition for ${route.path}. Either the ProjectPage is missing from the manifest, or the capability is not active.`,
|
|
70
72
|
),
|
|
71
73
|
]);
|
|
72
74
|
};
|
|
@@ -1,29 +1,17 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// SuperAdmin shell contract — injection keys, option types and the
|
|
2
|
+
// navigation guard shared between the Quasar bootstrap
|
|
3
|
+
// (`createSuperAdminApp()`, `@saasicat/ui-vue/quasar`) and the framework
|
|
4
|
+
// components/composables in this package.
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
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.
|
|
6
|
+
// Lives in the Vue layer (no Quasar import) so that the main entry and all
|
|
7
|
+
// composables stay consumable without Quasar being installed.
|
|
10
8
|
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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';
|
|
9
|
+
import type { App, Component, InjectionKey } from 'vue';
|
|
10
|
+
import type { NavigationGuardWithThis, RouteLocationNormalized } from 'vue-router';
|
|
23
11
|
|
|
24
12
|
import type { ActionKey, AdminManifest, ComponentKey } from '@saasicat/types';
|
|
25
|
-
import type { ActionHandler } from '
|
|
26
|
-
import {
|
|
13
|
+
import type { ActionHandler } from '../client/action-registry.js';
|
|
14
|
+
import type { HttpClient } from '../client/types.js';
|
|
27
15
|
|
|
28
16
|
/**
|
|
29
17
|
* App-specific branding data that the platform `AdminLayout` and other
|
|
@@ -133,25 +121,11 @@ export interface SuperAdminManifestGuardOptions {
|
|
|
133
121
|
errorRoute?: string;
|
|
134
122
|
}
|
|
135
123
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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;
|
|
124
|
+
/**
|
|
125
|
+
* Guard configuration shared by `buildNavigationGuard()` and
|
|
126
|
+
* `createSuperAdminApp()` (which embeds both fields in its options).
|
|
127
|
+
*/
|
|
128
|
+
export interface SuperAdminGuardOptions {
|
|
155
129
|
/**
|
|
156
130
|
* Optional: auth guard. When set, `router.beforeEach` is wired up
|
|
157
131
|
* automatically — `to.meta.public === true` bypasses the guard.
|
|
@@ -162,47 +136,11 @@ export interface CreateSuperAdminAppOptions {
|
|
|
162
136
|
* render until the manifest is loaded (prevents sidebar flicker).
|
|
163
137
|
*/
|
|
164
138
|
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
139
|
}
|
|
202
140
|
|
|
203
141
|
// Vue inject keys are registered globally via `Symbol.for(...)`. Reason:
|
|
204
142
|
// apps import `createSuperAdminApp` (and thus `app.provide(KEY, ...)`) from
|
|
205
|
-
// the built `dist
|
|
143
|
+
// the built `dist/` bundles, but shared `.vue` pages in the
|
|
206
144
|
// `pages-standard/` directory directly from `src/`. Both paths produce two
|
|
207
145
|
// independent module instances — with local `Symbol(...)` the identity would
|
|
208
146
|
// differ, and `inject()` would always throw "not found". `Symbol.for(...)`
|
|
@@ -241,68 +179,11 @@ export const SUPER_ADMIN_HTTP_KEY: InjectionKey<HttpClient> = Symbol.for(
|
|
|
241
179
|
'@saasicat/ui-vue/SUPER_ADMIN_HTTP',
|
|
242
180
|
);
|
|
243
181
|
|
|
244
|
-
const DEFAULT_QUASAR_OPTIONS: QuasarPluginOptions = {
|
|
245
|
-
plugins: { Notify, Dialog, Loading },
|
|
246
|
-
config: { notify: { position: 'top-right', timeout: 3000 } },
|
|
247
|
-
};
|
|
248
|
-
|
|
249
182
|
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* guard) and exposes the platform maps via `provide()` for downstream
|
|
253
|
-
* components.
|
|
183
|
+
* Additional Vue plugins (e.g. an app's own NotificationCenter) that a
|
|
184
|
+
* bootstrap installs after the platform setup, before the mount.
|
|
254
185
|
*/
|
|
255
|
-
export
|
|
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
|
-
}
|
|
186
|
+
export type InstallPlugin = (app: App) => void;
|
|
306
187
|
|
|
307
188
|
/**
|
|
308
189
|
* Internal helper, exported for isolated unit tests of the navigation
|
|
@@ -310,7 +191,7 @@ export function createSuperAdminApp(options: CreateSuperAdminAppOptions): SuperA
|
|
|
310
191
|
* `createSuperAdminApp()`, not this helper directly.
|
|
311
192
|
*/
|
|
312
193
|
export function buildNavigationGuard(
|
|
313
|
-
options:
|
|
194
|
+
options: SuperAdminGuardOptions,
|
|
314
195
|
): NavigationGuardWithThis<undefined> | null {
|
|
315
196
|
const { authGuard, manifestGuard } = options;
|
|
316
197
|
if (!authGuard && !manifestGuard) return null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// UI notify port — the single toast/snackbar seam between platform pages and
|
|
2
|
+
// the host app. Standard pages emit user feedback exclusively through this
|
|
3
|
+
// port; which component library renders it is the app's choice.
|
|
4
|
+
//
|
|
5
|
+
// `createSuperAdminApp()` (`@saasicat/ui-vue/quasar`) provides a
|
|
6
|
+
// Quasar-backed default; apps override it via
|
|
7
|
+
// `createSuperAdminApp({ notify })` or by providing the key themselves.
|
|
8
|
+
|
|
9
|
+
import type { InjectionKey } from 'vue';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Feedback kind, aligned with the established page conventions
|
|
13
|
+
* (`positive` = success, `negative` = error).
|
|
14
|
+
*/
|
|
15
|
+
export type UiNotifyKind = 'positive' | 'negative' | 'warning' | 'info';
|
|
16
|
+
|
|
17
|
+
export interface UiNotifyOptions {
|
|
18
|
+
/** Secondary line below the message (e.g. changed fields, one-time password). */
|
|
19
|
+
caption?: string;
|
|
20
|
+
/** Display duration in ms; implementations fall back to their default when unset. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Notify port. Compatible with the `(kind, message)` callbacks pages already accept. */
|
|
25
|
+
export type UiNotify = (kind: UiNotifyKind, message: string, options?: UiNotifyOptions) => void;
|
|
26
|
+
|
|
27
|
+
/** Vue inject key for the notify port (see `Symbol.for` note in super-admin-context.ts). */
|
|
28
|
+
export const SUPER_ADMIN_NOTIFY_KEY: InjectionKey<UiNotify> = Symbol.for(
|
|
29
|
+
'@saasicat/ui-vue/SUPER_ADMIN_NOTIFY',
|
|
30
|
+
);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { computed, type ComputedRef, type Ref } from 'vue';
|
|
9
9
|
import type { AdminManifest } from '@saasicat/types';
|
|
10
|
-
import { ActionRegistry, type ActionHandler } from '
|
|
10
|
+
import { ActionRegistry, type ActionHandler } from '../client/action-registry.js';
|
|
11
11
|
|
|
12
12
|
export interface UseActionsResult {
|
|
13
13
|
registry: ComputedRef<ActionRegistry | null>;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Direct use is also allowed for custom endpoints.
|
|
6
6
|
|
|
7
7
|
import { ref, watch, type Ref } from 'vue';
|
|
8
|
-
import { defaultHttpClient, type HttpClient } from '
|
|
8
|
+
import { defaultHttpClient, type HttpClient } from '../client/types.js';
|
|
9
9
|
|
|
10
10
|
export interface ApiListResponse<T> {
|
|
11
11
|
items: T[];
|
|
@@ -29,9 +29,9 @@ export interface UseAuditEntriesResult extends UseApiListResult<AuditEntry> {
|
|
|
29
29
|
export function useAuditEntries(options: UseAuditEntriesOptions): UseAuditEntriesResult {
|
|
30
30
|
if (!options?.endpoint) {
|
|
31
31
|
throw new Error(
|
|
32
|
-
'useAuditEntries: `endpoint`
|
|
33
|
-
'
|
|
34
|
-
'
|
|
32
|
+
'useAuditEntries: `endpoint` is required (e.g. "/api/admin/audit" ' +
|
|
33
|
+
'or "/api/v1/admin/audit"). The platform has no default because ' +
|
|
34
|
+
'apps use different globalPrefix conventions.',
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
const filter = options.filter ?? ref<AuditQuery>({});
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
BatchColumnFetcher,
|
|
13
13
|
type BatchColumnData,
|
|
14
14
|
type BatchColumnFetcherOptions,
|
|
15
|
-
} from '
|
|
15
|
+
} from '../client/batch-column-fetcher.js';
|
|
16
16
|
|
|
17
17
|
export interface UseBatchColumnsResult {
|
|
18
18
|
/** Data per column key (`columnKey → tenantId → value`). */
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
// `mfaCode` parameter through.
|
|
12
12
|
|
|
13
13
|
import { computed, ref, type ComputedRef, type Ref } from 'vue';
|
|
14
|
-
import { defaultHttpClient, type HttpClient } from '
|
|
14
|
+
import { defaultHttpClient, type HttpClient } from '../client/types.js';
|
|
15
|
+
import { useSaMessages } from './use-super-admin-i18n.js';
|
|
15
16
|
|
|
16
17
|
export type BulkItemKind = 'plan';
|
|
17
18
|
|
|
@@ -62,11 +63,12 @@ export interface UseBulkPublishResult {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export function useBulkPublish(options: UseBulkPublishOptions): UseBulkPublishResult {
|
|
66
|
+
const msg = useSaMessages('planVersions');
|
|
65
67
|
if (!options?.endpoints) {
|
|
66
68
|
throw new Error(
|
|
67
|
-
'useBulkPublish: `endpoints`
|
|
68
|
-
'
|
|
69
|
-
'globalPrefix
|
|
69
|
+
'useBulkPublish: `endpoints` is required (mapping plan → URL builder). ' +
|
|
70
|
+
'The platform has no default because apps use different ' +
|
|
71
|
+
'globalPrefix conventions.',
|
|
70
72
|
);
|
|
71
73
|
}
|
|
72
74
|
const http = options.http ?? defaultHttpClient();
|
|
@@ -128,7 +130,7 @@ export function useBulkPublish(options: UseBulkPublishOptions): UseBulkPublishRe
|
|
|
128
130
|
// Mark all items as failed with a clear reason.
|
|
129
131
|
for (const item of items.value) {
|
|
130
132
|
item.status = 'failed';
|
|
131
|
-
item.error =
|
|
133
|
+
item.error = msg.value.bulkPublish.changeNoteRequired;
|
|
132
134
|
}
|
|
133
135
|
return;
|
|
134
136
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { ref, watch, type Ref } from 'vue';
|
|
13
13
|
import type { BundleRow, BundleVersionRow } from '@saasicat/types';
|
|
14
|
-
import { defaultHttpClient, type HttpClient } from '
|
|
14
|
+
import { defaultHttpClient, type HttpClient } from '../client/types.js';
|
|
15
15
|
|
|
16
16
|
export interface UseBundleVersionsMapOptions {
|
|
17
17
|
adminEndpoint: string;
|
|
@@ -36,10 +36,10 @@ export function useBundleVersionsMap(
|
|
|
36
36
|
options: UseBundleVersionsMapOptions,
|
|
37
37
|
): UseBundleVersionsMapResult {
|
|
38
38
|
if (!options?.adminEndpoint) {
|
|
39
|
-
throw new Error('useBundleVersionsMap: `adminEndpoint`
|
|
39
|
+
throw new Error('useBundleVersionsMap: `adminEndpoint` is required.');
|
|
40
40
|
}
|
|
41
41
|
if (!options?.bundles) {
|
|
42
|
-
throw new Error('useBundleVersionsMap: `bundles`
|
|
42
|
+
throw new Error('useBundleVersionsMap: `bundles` is required.');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const http = options.http ?? defaultHttpClient();
|
|
@@ -58,7 +58,9 @@ export function useBundleVersionsMap(
|
|
|
58
58
|
headers: { 'content-type': 'application/json', ...authHeaders() },
|
|
59
59
|
});
|
|
60
60
|
if (res.status >= 400) {
|
|
61
|
-
throw new Error(
|
|
61
|
+
throw new Error(
|
|
62
|
+
`HTTP ${res.status} while loading the versions for bundle '${bundleId}'`,
|
|
63
|
+
);
|
|
62
64
|
}
|
|
63
65
|
return ((await res.json().catch(() => null)) as BundleVersionRow[] | null) ?? [];
|
|
64
66
|
}
|
|
@@ -74,7 +76,7 @@ export function useBundleVersionsMap(
|
|
|
74
76
|
return [b.id, versions] as const;
|
|
75
77
|
} catch (err) {
|
|
76
78
|
console.warn(
|
|
77
|
-
`useBundleVersionsMap:
|
|
79
|
+
`useBundleVersionsMap: loading versions for bundle '${b.bundleKey}' failed`,
|
|
78
80
|
err,
|
|
79
81
|
);
|
|
80
82
|
return [b.id, []] as const;
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
UpdateBundleData,
|
|
17
17
|
UpdateBundleVersionDraftData,
|
|
18
18
|
} from '@saasicat/types';
|
|
19
|
-
import { defaultHttpClient, type HttpClient } from '
|
|
19
|
+
import { defaultHttpClient, type HttpClient } from '../client/types.js';
|
|
20
20
|
|
|
21
21
|
export interface UseBundlesOptions {
|
|
22
22
|
/**
|
|
@@ -58,13 +58,13 @@ export interface UseBundlesResult {
|
|
|
58
58
|
export function useBundles(options: UseBundlesOptions): UseBundlesResult {
|
|
59
59
|
if (!options?.adminEndpoint) {
|
|
60
60
|
throw new Error(
|
|
61
|
-
'useBundles: `adminEndpoint`
|
|
62
|
-
'"/api/v1/admin").
|
|
63
|
-
'
|
|
61
|
+
'useBundles: `adminEndpoint` is required (e.g. "/api/admin" or ' +
|
|
62
|
+
'"/api/v1/admin"). The platform has no default, because apps use ' +
|
|
63
|
+
'different globalPrefix conventions.',
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
if (!options?.projectKey) {
|
|
67
|
-
throw new Error('useBundles: `projectKey`
|
|
67
|
+
throw new Error('useBundles: `projectKey` is required.');
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const http = options.http ?? defaultHttpClient();
|
|
@@ -91,7 +91,7 @@ export function useBundles(options: UseBundlesOptions): UseBundlesResult {
|
|
|
91
91
|
throw new BundlesApiError(
|
|
92
92
|
res.status,
|
|
93
93
|
body,
|
|
94
|
-
`Bundles
|
|
94
|
+
`Bundles API responded with HTTP ${res.status}`,
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
97
|
return body as T;
|
|
@@ -117,7 +117,7 @@ export function useBundles(options: UseBundlesOptions): UseBundlesResult {
|
|
|
117
117
|
method: 'POST',
|
|
118
118
|
body: JSON.stringify(data),
|
|
119
119
|
});
|
|
120
|
-
if (!created) throw new BundlesApiError(0, null, 'Create
|
|
120
|
+
if (!created) throw new BundlesApiError(0, null, 'Create returned no body');
|
|
121
121
|
bundles.value = [...bundles.value, created];
|
|
122
122
|
return created;
|
|
123
123
|
}
|
|
@@ -127,7 +127,7 @@ export function useBundles(options: UseBundlesOptions): UseBundlesResult {
|
|
|
127
127
|
method: 'PATCH',
|
|
128
128
|
body: JSON.stringify(data),
|
|
129
129
|
});
|
|
130
|
-
if (!updated) throw new BundlesApiError(0, null, 'Update
|
|
130
|
+
if (!updated) throw new BundlesApiError(0, null, 'Update returned no body');
|
|
131
131
|
bundles.value = bundles.value.map((b) => (b.id === bundleId ? updated : b));
|
|
132
132
|
return updated;
|
|
133
133
|
}
|
|
@@ -189,10 +189,10 @@ export interface UseBundleVersionsResult {
|
|
|
189
189
|
|
|
190
190
|
export function useBundleVersions(options: UseBundleVersionsOptions): UseBundleVersionsResult {
|
|
191
191
|
if (!options?.adminEndpoint) {
|
|
192
|
-
throw new Error('useBundleVersions: `adminEndpoint`
|
|
192
|
+
throw new Error('useBundleVersions: `adminEndpoint` is required.');
|
|
193
193
|
}
|
|
194
194
|
if (!options?.bundleId) {
|
|
195
|
-
throw new Error('useBundleVersions: `bundleId`
|
|
195
|
+
throw new Error('useBundleVersions: `bundleId` is required.');
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
const http = options.http ?? defaultHttpClient();
|
|
@@ -220,7 +220,7 @@ export function useBundleVersions(options: UseBundleVersionsOptions): UseBundleV
|
|
|
220
220
|
throw new BundlesApiError(
|
|
221
221
|
res.status,
|
|
222
222
|
body,
|
|
223
|
-
`BundleVersions
|
|
223
|
+
`BundleVersions API responded with HTTP ${res.status}`,
|
|
224
224
|
);
|
|
225
225
|
}
|
|
226
226
|
return body as T;
|
|
@@ -246,7 +246,7 @@ export function useBundleVersions(options: UseBundleVersionsOptions): UseBundleV
|
|
|
246
246
|
method: 'POST',
|
|
247
247
|
body: JSON.stringify(data),
|
|
248
248
|
});
|
|
249
|
-
if (!result) throw new BundlesApiError(0, null, 'CreateDraft
|
|
249
|
+
if (!result) throw new BundlesApiError(0, null, 'CreateDraft returned no body');
|
|
250
250
|
versions.value = [...versions.value, result.bundleVersion];
|
|
251
251
|
return result;
|
|
252
252
|
}
|
|
@@ -259,7 +259,7 @@ export function useBundleVersions(options: UseBundleVersionsOptions): UseBundleV
|
|
|
259
259
|
`${versionUrlBase}/${versionId}`,
|
|
260
260
|
{ method: 'PATCH', body: JSON.stringify(data) },
|
|
261
261
|
);
|
|
262
|
-
if (!result) throw new BundlesApiError(0, null, 'UpdateDraft
|
|
262
|
+
if (!result) throw new BundlesApiError(0, null, 'UpdateDraft returned no body');
|
|
263
263
|
versions.value = versions.value.map((v) => (v.id === versionId ? result.bundleVersion : v));
|
|
264
264
|
return result;
|
|
265
265
|
}
|
|
@@ -277,7 +277,7 @@ export function useBundleVersions(options: UseBundleVersionsOptions): UseBundleV
|
|
|
277
277
|
`${versionUrlBase}/${versionId}/publish`,
|
|
278
278
|
{ method: 'POST', body: JSON.stringify(opts) },
|
|
279
279
|
);
|
|
280
|
-
if (!result) throw new BundlesApiError(0, null, 'Publish
|
|
280
|
+
if (!result) throw new BundlesApiError(0, null, 'Publish returned no body');
|
|
281
281
|
// Reload versions, because publishing may mark another version as
|
|
282
282
|
// superseded — the local cache would otherwise be inconsistent.
|
|
283
283
|
await load();
|