@saasicat/ui-vue 0.5.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 +17 -0
- package/dist/{chunk-EH7TOD4H.js → chunk-A3W2N26K.js} +73 -77
- package/dist/{chunk-3YP2ZAOD.js → chunk-QZOSDCNP.js} +36 -1
- package/dist/chunk-X5SQ5HMB.js +3231 -0
- package/dist/client/index.cjs +3279 -76
- package/dist/client/index.d.cts +31 -5
- package/dist/client/index.d.ts +31 -5
- package/dist/client/index.js +23 -5
- package/dist/index.cjs +3709 -465
- package/dist/index.d.cts +37 -54
- package/dist/index.d.ts +37 -54
- package/dist/index.js +347 -311
- package/dist/messages-7b0P8W75.d.cts +1652 -0
- package/dist/messages-7b0P8W75.d.ts +1652 -0
- package/dist/quasar/index.cjs +3185 -4
- package/dist/quasar/index.d.cts +11 -2
- package/dist/quasar/index.d.ts +11 -2
- package/dist/quasar/index.js +8 -3
- package/dist/testing-e2e/admin-pages-suite.cjs +14 -11
- package/dist/testing-e2e/admin-pages-suite.js +14 -11
- package/dist/{ui-notify-COUzXEQ4.d.ts → use-super-admin-i18n-B3v3-SWZ.d.ts} +35 -3
- package/dist/{ui-notify-D_eAAwRh.d.cts → use-super-admin-i18n-DMktRVEt.d.cts} +35 -3
- package/package.json +2 -2
- package/src/client/action-registry.ts +4 -4
- package/src/client/batch-column-fetcher.ts +4 -4
- package/src/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 +2 -0
- package/src/client/manifest-loader.ts +4 -4
- package/src/client/nav-builder.ts +44 -51
- package/src/client/plan-versions-catalog.ts +42 -30
- package/src/components/BundleVersionPublishDialog.vue +35 -24
- 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 +20 -9
- 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 +1 -1
- package/src/pages-standard/AdminLayout.vue +42 -17
- 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 +29 -15
- package/src/pages-standard/DiscoveryPage.vue +40 -28
- package/src/pages-standard/EmailHistoryPage.vue +92 -59
- package/src/pages-standard/MarketingCatalogPage.vue +35 -16
- package/src/pages-standard/PilotsPage.vue +57 -41
- package/src/pages-standard/PlanVersionsPage.vue +7 -3
- package/src/pages-standard/PlansPage.vue +89 -57
- package/src/pages-standard/PlatformEmailPage.vue +115 -50
- package/src/pages-standard/PromoCodeDetailPage.vue +66 -63
- package/src/pages-standard/PromoCodesPage.vue +46 -35
- package/src/pages-standard/SubscriptionsPage.vue +14 -9
- package/src/pages-standard/SuperAdminLoginPage.vue +16 -13
- package/src/pages-standard/SuperAdminSetupWizard.vue +51 -47
- package/src/pages-standard/TenantDetailPage.vue +50 -36
- package/src/pages-standard/TenantsPage.vue +54 -41
- package/src/pages-standard/UsersPage.vue +66 -42
- 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 +41 -25
- package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +45 -26
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +7 -5
- package/src/pages-standard/plan-versions/PlanVersionsList.vue +39 -16
- package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +24 -12
- 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 +72 -37
- package/src/pages-tenant/PlanChangeWizard.vue +8 -4
- package/src/pages-tenant/TenantPlanSection.vue +17 -13
- package/src/pages-tenant/default-i18n.ts +217 -1
- package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +8 -6
- package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +9 -10
- package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +4 -1
- package/src/quasar/create-super-admin-app.ts +19 -0
- package/src/testing-e2e/admin-pages-suite.ts +14 -11
- package/src/vue/project-page-host.ts +5 -3
- package/src/vue/use-audit-entries.ts +3 -3
- package/src/vue/use-bulk-publish.ts +6 -4
- package/src/vue/use-bundle-versions-map.ts +6 -4
- package/src/vue/use-bundles.ts +13 -13
- package/src/vue/use-catalog-entries.ts +13 -10
- package/src/vue/use-discovery.ts +8 -5
- package/src/vue/use-entitlement.ts +3 -3
- package/src/vue/use-live-plan-versions.ts +4 -4
- package/src/vue/use-marketing-projections.ts +9 -6
- package/src/vue/use-nav.ts +8 -2
- package/src/vue/use-plan-editor.ts +2 -2
- package/src/vue/use-plan-versions.ts +3 -3
- package/src/vue/use-plans.ts +12 -12
- package/src/vue/use-platform-tenant-actions.ts +9 -6
- package/src/vue/use-promotions.ts +9 -10
- package/src/vue/use-subscription-draft.ts +2 -3
- package/src/vue/use-super-admin-context.ts +3 -3
- package/src/vue/use-super-admin-i18n.ts +82 -0
- package/src/vue/use-tenant-action-flow.ts +11 -6
- package/src/vue/use-tenant-manifest.ts +1 -3
- package/src/vue/use-tenant-subscription-bundles.ts +4 -4
- package/src/vue/use-tenants.ts +3 -3
- package/dist/action-registry-CQ2wL3R-.d.cts +0 -85
- package/dist/action-registry-CQ2wL3R-.d.ts +0 -85
- package/dist/chunk-LVGUSD3T.js +0 -26
- 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/vue/use-business-types.ts +0 -269
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
<div class="sa-pilots">
|
|
3
3
|
<header class="sa-page-head">
|
|
4
4
|
<div>
|
|
5
|
-
<h1 class="sa-page-head__title">
|
|
6
|
-
<p class="sa-page-head__sub">
|
|
5
|
+
<h1 class="sa-page-head__title">{{ msg.title }}</h1>
|
|
6
|
+
<p class="sa-page-head__sub">
|
|
7
|
+
{{ formatMessage(msg.subtitleCount, { count: rows.length }) }}
|
|
8
|
+
</p>
|
|
7
9
|
</div>
|
|
8
10
|
<div class="sa-page-head__actions">
|
|
9
11
|
<slot name="head-actions">
|
|
@@ -12,7 +14,7 @@
|
|
|
12
14
|
unelevated
|
|
13
15
|
color="primary"
|
|
14
16
|
icon="add"
|
|
15
|
-
:label="
|
|
17
|
+
:label="resolvedCreateLabel"
|
|
16
18
|
@click="showCreate = true"
|
|
17
19
|
/>
|
|
18
20
|
</slot>
|
|
@@ -38,7 +40,7 @@
|
|
|
38
40
|
|
|
39
41
|
<q-banner v-if="reviewSoon.length" class="bg-amber-2 text-grey-9 q-mb-md" rounded>
|
|
40
42
|
<template #avatar><q-icon name="event" color="amber-9" /></template>
|
|
41
|
-
{{ reviewSoon.length }}
|
|
43
|
+
{{ formatMessage(msg.list.reviewSoonBanner, { count: reviewSoon.length }) }}
|
|
42
44
|
</q-banner>
|
|
43
45
|
|
|
44
46
|
<div class="sa-pilots__card">
|
|
@@ -109,6 +111,8 @@
|
|
|
109
111
|
<script setup lang="ts">
|
|
110
112
|
import { computed, onMounted, ref } from 'vue';
|
|
111
113
|
import { useQuasar } from 'quasar';
|
|
114
|
+
import { formatMessage } from '../client/i18n/format.js';
|
|
115
|
+
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
112
116
|
import { useSuperAdminNotify } from '../quasar/notify.js';
|
|
113
117
|
import PilotCreateDialog from '../components/dialogs/PilotCreateDialog.vue';
|
|
114
118
|
import PilotEditDialog from '../components/dialogs/PilotEditDialog.vue';
|
|
@@ -195,11 +199,15 @@ const props = withDefaults(
|
|
|
195
199
|
requireMfa: false,
|
|
196
200
|
requireMfaForExtend: false,
|
|
197
201
|
requireMfaForRevoke: false,
|
|
198
|
-
createLabel: 'Pilot anlegen',
|
|
199
202
|
defaultActions: () => ['edit', 'extend', 'revoke'],
|
|
200
203
|
},
|
|
201
204
|
);
|
|
202
205
|
|
|
206
|
+
const msg = useSaMessages('pilots');
|
|
207
|
+
const common = useSaMessages('common');
|
|
208
|
+
const { intlLocale } = useSuperAdminI18n();
|
|
209
|
+
const resolvedCreateLabel = computed(() => props.createLabel ?? msg.value.createAction);
|
|
210
|
+
|
|
203
211
|
const q = useQuasar();
|
|
204
212
|
const notify = useSuperAdminNotify();
|
|
205
213
|
const rows = ref<PilotRow[]>([]);
|
|
@@ -291,53 +299,53 @@ const statTiles = computed<
|
|
|
291
299
|
const counts = { all: rows.value.length, active: 0, expiring: 0, expired: 0 };
|
|
292
300
|
for (const r of rows.value) counts[classifyRow(r)]++;
|
|
293
301
|
return [
|
|
294
|
-
{ id: 'all', label:
|
|
295
|
-
{ id: 'active', label:
|
|
302
|
+
{ id: 'all', label: common.value.all, count: counts.all },
|
|
303
|
+
{ id: 'active', label: common.value.active, count: counts.active, tone: 'positive' },
|
|
296
304
|
{
|
|
297
305
|
id: 'expiring',
|
|
298
|
-
label:
|
|
306
|
+
label: msg.value.list.tileExpiring,
|
|
299
307
|
count: counts.expiring,
|
|
300
308
|
tone: 'warn',
|
|
301
|
-
hint:
|
|
309
|
+
hint: msg.value.list.tileExpiringHint,
|
|
302
310
|
},
|
|
303
|
-
{ id: 'expired', label:
|
|
311
|
+
{ id: 'expired', label: common.value.expired, count: counts.expired, tone: 'danger' },
|
|
304
312
|
];
|
|
305
313
|
});
|
|
306
314
|
|
|
307
|
-
const baseColumns = [
|
|
315
|
+
const baseColumns = computed(() => [
|
|
308
316
|
{
|
|
309
317
|
name: 'slug',
|
|
310
|
-
label:
|
|
318
|
+
label: msg.value.list.columnSlug,
|
|
311
319
|
field: (r: PilotRow) => r.tenant.slug,
|
|
312
320
|
align: 'left' as const,
|
|
313
321
|
sortable: true,
|
|
314
322
|
},
|
|
315
323
|
{
|
|
316
324
|
name: 'name',
|
|
317
|
-
label:
|
|
325
|
+
label: common.value.name,
|
|
318
326
|
field: (r: PilotRow) => r.tenant.name,
|
|
319
327
|
align: 'left' as const,
|
|
320
328
|
},
|
|
321
|
-
{ name: 'plan', label:
|
|
329
|
+
{ name: 'plan', label: msg.value.list.columnPlan, field: 'plan', align: 'left' as const },
|
|
322
330
|
{
|
|
323
331
|
name: 'note',
|
|
324
|
-
label:
|
|
332
|
+
label: msg.value.list.columnNote,
|
|
325
333
|
field: (r: PilotRow) => r.pilotNote ?? '—',
|
|
326
334
|
align: 'left' as const,
|
|
327
335
|
},
|
|
328
336
|
{
|
|
329
337
|
name: 'grantedBy',
|
|
330
|
-
label:
|
|
338
|
+
label: msg.value.list.columnGrantedBy,
|
|
331
339
|
field: (r: PilotRow) => r.grantedBy ?? '—',
|
|
332
340
|
align: 'left' as const,
|
|
333
341
|
},
|
|
334
342
|
{
|
|
335
343
|
name: 'pilotEndsAt',
|
|
336
|
-
label:
|
|
344
|
+
label: msg.value.list.columnEndsAt,
|
|
337
345
|
field: (r: PilotRow) => formatDate(r.pilotEndsAt) ?? '∞',
|
|
338
346
|
align: 'left' as const,
|
|
339
347
|
},
|
|
340
|
-
];
|
|
348
|
+
]);
|
|
341
349
|
|
|
342
350
|
// Built-in default actions (edit/extend/revoke) — are APPENDED to the
|
|
343
351
|
// consumer `actions`, not replaced. Order follows the `defaultActions` prop.
|
|
@@ -347,7 +355,7 @@ const bakedActions = computed<PilotRowAction[]>(() => {
|
|
|
347
355
|
if (id === 'edit' && props.enableEdit && props.submitEdit) {
|
|
348
356
|
out.push({
|
|
349
357
|
id: 'edit',
|
|
350
|
-
label:
|
|
358
|
+
label: common.value.edit,
|
|
351
359
|
icon: 'edit',
|
|
352
360
|
color: 'primary',
|
|
353
361
|
handler: (row) => onEditClick(row),
|
|
@@ -355,7 +363,7 @@ const bakedActions = computed<PilotRowAction[]>(() => {
|
|
|
355
363
|
} else if (id === 'extend' && props.enableExtend && props.submitExtend) {
|
|
356
364
|
out.push({
|
|
357
365
|
id: 'extend',
|
|
358
|
-
label:
|
|
366
|
+
label: msg.value.list.actionExtend,
|
|
359
367
|
icon: 'event_repeat',
|
|
360
368
|
color: 'primary',
|
|
361
369
|
handler: (row) => onExtendClick(row),
|
|
@@ -363,7 +371,7 @@ const bakedActions = computed<PilotRowAction[]>(() => {
|
|
|
363
371
|
} else if (id === 'revoke' && props.enableRevoke && props.submitRevoke) {
|
|
364
372
|
out.push({
|
|
365
373
|
id: 'revoke',
|
|
366
|
-
label:
|
|
374
|
+
label: msg.value.list.actionRevoke,
|
|
367
375
|
icon: 'block',
|
|
368
376
|
color: 'negative',
|
|
369
377
|
handler: (row) => onRevokeClick(row),
|
|
@@ -379,7 +387,7 @@ const mergedActions = computed<readonly PilotRowAction[]>(() => [
|
|
|
379
387
|
]);
|
|
380
388
|
|
|
381
389
|
const effectiveColumns = computed(() => {
|
|
382
|
-
const cols = [...baseColumns];
|
|
390
|
+
const cols = [...baseColumns.value];
|
|
383
391
|
if (mergedActions.value.length > 0) {
|
|
384
392
|
cols.push({
|
|
385
393
|
name: 'actions',
|
|
@@ -437,7 +445,7 @@ function errMsg(err: unknown): string {
|
|
|
437
445
|
return (
|
|
438
446
|
(err as { response?: { data?: { message?: string } } })?.response?.data?.message ??
|
|
439
447
|
(err as Error)?.message ??
|
|
440
|
-
|
|
448
|
+
msg.value.list.actionFailed
|
|
441
449
|
);
|
|
442
450
|
}
|
|
443
451
|
|
|
@@ -447,15 +455,23 @@ function onEditClick(row: PilotRow): void {
|
|
|
447
455
|
}
|
|
448
456
|
|
|
449
457
|
function onUpdated(result: PilotEditResult): void {
|
|
450
|
-
notify('positive',
|
|
451
|
-
caption: result.changed?.length
|
|
458
|
+
notify('positive', formatMessage(msg.value.list.updatedNotice, { slug: result.slug }), {
|
|
459
|
+
caption: result.changed?.length
|
|
460
|
+
? formatMessage(msg.value.list.updatedChangedCaption, {
|
|
461
|
+
fields: result.changed.join(', '),
|
|
462
|
+
})
|
|
463
|
+
: undefined,
|
|
452
464
|
});
|
|
453
465
|
void reload();
|
|
454
466
|
}
|
|
455
467
|
|
|
456
468
|
function onCreated(result: PilotCreateResult): void {
|
|
457
|
-
notify('positive',
|
|
458
|
-
caption: result.initialPassword
|
|
469
|
+
notify('positive', formatMessage(msg.value.list.createdNotice, { slug: result.slug }), {
|
|
470
|
+
caption: result.initialPassword
|
|
471
|
+
? formatMessage(msg.value.list.createdPasswordCaption, {
|
|
472
|
+
password: result.initialPassword,
|
|
473
|
+
})
|
|
474
|
+
: undefined,
|
|
459
475
|
timeoutMs: 8000,
|
|
460
476
|
});
|
|
461
477
|
void reload();
|
|
@@ -493,7 +509,7 @@ async function runAction(
|
|
|
493
509
|
} catch (err) {
|
|
494
510
|
const status = (err as { response?: { status?: number } })?.response?.status;
|
|
495
511
|
if (status === 401) {
|
|
496
|
-
mfaError.value =
|
|
512
|
+
mfaError.value = msg.value.mfa.invalidOrNotSetUp;
|
|
497
513
|
continue;
|
|
498
514
|
}
|
|
499
515
|
showMfa.value = false;
|
|
@@ -507,19 +523,19 @@ function onExtendClick(row: PilotRow): void {
|
|
|
507
523
|
if (!props.submitExtend) return;
|
|
508
524
|
const submit = props.submitExtend;
|
|
509
525
|
q.dialog({
|
|
510
|
-
title:
|
|
511
|
-
message:
|
|
526
|
+
title: formatMessage(msg.value.list.extendTitle, { slug: row.tenant.slug }),
|
|
527
|
+
message: msg.value.list.extendMessage,
|
|
512
528
|
prompt: {
|
|
513
529
|
model: row.pilotEndsAt?.slice(0, 10) ?? '',
|
|
514
530
|
type: 'date',
|
|
515
531
|
},
|
|
516
|
-
cancel:
|
|
517
|
-
ok: { label:
|
|
532
|
+
cancel: common.value.cancel,
|
|
533
|
+
ok: { label: msg.value.list.actionExtend, color: 'primary' },
|
|
518
534
|
}).onOk(async (until: string) => {
|
|
519
535
|
if (!until) return;
|
|
520
536
|
await runAction(
|
|
521
|
-
|
|
522
|
-
|
|
537
|
+
formatMessage(msg.value.list.extendMfaDescription, { slug: row.tenant.slug, until }),
|
|
538
|
+
formatMessage(msg.value.list.extendSuccess, { until }),
|
|
523
539
|
!!props.requireMfaForExtend,
|
|
524
540
|
(code) => submit(row.tenant.slug, until, code),
|
|
525
541
|
);
|
|
@@ -530,14 +546,14 @@ function onRevokeClick(row: PilotRow): void {
|
|
|
530
546
|
if (!props.submitRevoke) return;
|
|
531
547
|
const submit = props.submitRevoke;
|
|
532
548
|
q.dialog({
|
|
533
|
-
title:
|
|
534
|
-
message:
|
|
535
|
-
cancel:
|
|
536
|
-
ok: { label:
|
|
549
|
+
title: formatMessage(msg.value.list.revokeTitle, { slug: row.tenant.slug }),
|
|
550
|
+
message: msg.value.list.revokeMessage,
|
|
551
|
+
cancel: common.value.cancel,
|
|
552
|
+
ok: { label: msg.value.list.actionRevoke, color: 'negative' },
|
|
537
553
|
}).onOk(async () => {
|
|
538
554
|
await runAction(
|
|
539
|
-
|
|
540
|
-
|
|
555
|
+
formatMessage(msg.value.list.revokeMfaDescription, { slug: row.tenant.slug }),
|
|
556
|
+
msg.value.list.revokeSuccess,
|
|
541
557
|
!!props.requireMfaForRevoke,
|
|
542
558
|
(code) => submit(row.tenant.slug, code),
|
|
543
559
|
);
|
|
@@ -547,7 +563,7 @@ function onRevokeClick(row: PilotRow): void {
|
|
|
547
563
|
function formatDate(iso: string | null | undefined): string | null {
|
|
548
564
|
if (!iso) return null;
|
|
549
565
|
try {
|
|
550
|
-
return new Date(iso).toLocaleDateString(
|
|
566
|
+
return new Date(iso).toLocaleDateString(intlLocale.value, {
|
|
551
567
|
day: '2-digit',
|
|
552
568
|
month: 'short',
|
|
553
569
|
year: 'numeric',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="sa-pv">
|
|
3
3
|
<div v-if="loading" class="sa-pv__loading">
|
|
4
|
-
<q-spinner size="32px" />
|
|
4
|
+
<q-spinner size="32px" /> {{ msg.page.loadingCatalog }}
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
7
|
<template v-else>
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
:action-meta="auditActionMeta"
|
|
64
64
|
/>
|
|
65
65
|
<div v-else class="sa-pv__no-audit">
|
|
66
|
-
|
|
66
|
+
{{ msg.page.noAuditLoader }}
|
|
67
67
|
</div>
|
|
68
68
|
</slot>
|
|
69
69
|
</template>
|
|
@@ -85,6 +85,7 @@ import PlanVersionsMatrix from './plan-versions/PlanVersionsMatrix.vue';
|
|
|
85
85
|
import PlanVersionsAudit from './plan-versions/PlanVersionsAudit.vue';
|
|
86
86
|
import PlanVersionsDiff from './plan-versions/PlanVersionsDiff.vue';
|
|
87
87
|
import type { PlanVersionViewMode } from './plan-versions/types.js';
|
|
88
|
+
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
88
89
|
|
|
89
90
|
// Platform standard page: plan versions.
|
|
90
91
|
//
|
|
@@ -153,12 +154,15 @@ const emit = defineEmits<{
|
|
|
153
154
|
(e: 'createPlanDraft', planId: string): void;
|
|
154
155
|
}>();
|
|
155
156
|
|
|
157
|
+
const msg = useSaMessages('planVersions');
|
|
158
|
+
const { locale } = useSuperAdminI18n();
|
|
159
|
+
|
|
156
160
|
const selectedId = ref<string>('drafts');
|
|
157
161
|
const compareId = ref<string | null>(null);
|
|
158
162
|
const viewMode = ref<PlanVersionViewMode>('list');
|
|
159
163
|
|
|
160
164
|
const snapshots = computed<CatalogSnapshot[]>(() =>
|
|
161
|
-
buildSnapshots(props.data, { planSortOrder: props.planSortOrder }),
|
|
165
|
+
buildSnapshots(props.data, { planSortOrder: props.planSortOrder, locale: locale.value }),
|
|
162
166
|
);
|
|
163
167
|
|
|
164
168
|
const selected = computed<CatalogSnapshot | null>(
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
</template>
|
|
7
7
|
{{ error.message }}
|
|
8
8
|
<template #action>
|
|
9
|
-
<q-btn flat label="
|
|
9
|
+
<q-btn flat :label="common.reload" @click="load" />
|
|
10
10
|
</template>
|
|
11
11
|
</q-banner>
|
|
12
12
|
|
|
13
13
|
<div v-if="loading && plans.length === 0" class="sa-plans__loading">
|
|
14
14
|
<q-spinner size="32px" />
|
|
15
|
-
<span>
|
|
15
|
+
<span>{{ msg.page.loading }}</span>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<template v-else>
|
|
@@ -179,6 +179,9 @@ import {
|
|
|
179
179
|
type UsePlanVersionsResult,
|
|
180
180
|
} from '../vue/use-plans.js';
|
|
181
181
|
import type { HttpClient } from '../client/types.js';
|
|
182
|
+
import type { SaMessages } from '../client/i18n/index.js';
|
|
183
|
+
import { formatMessage } from '../client/i18n/format.js';
|
|
184
|
+
import { useSaMessages } from '../vue/use-super-admin-i18n.js';
|
|
182
185
|
import PlanVersionEditor from '../components/plan-version-editor/PlanVersionEditor.vue';
|
|
183
186
|
import PlanMatrix from '../components/plan-matrix/PlanMatrix.vue';
|
|
184
187
|
import PlanDetail from '../components/plan-detail/PlanDetail.vue';
|
|
@@ -247,6 +250,9 @@ const props = defineProps<{
|
|
|
247
250
|
loadPlanAudit?: (planId: string) => Promise<AuditRow[]>;
|
|
248
251
|
}>();
|
|
249
252
|
|
|
253
|
+
const msg = useSaMessages('plans');
|
|
254
|
+
const common = useSaMessages('common');
|
|
255
|
+
|
|
250
256
|
const composable: UsePlansResult = usePlans({
|
|
251
257
|
adminEndpoint: props.adminEndpoint,
|
|
252
258
|
projectKey: props.projectKey,
|
|
@@ -390,7 +396,7 @@ async function onCreateSubmit(payload: PlanCreateSubmit): Promise<void> {
|
|
|
390
396
|
|
|
391
397
|
await reloadAllVersions();
|
|
392
398
|
flashHighlight(created.planKey);
|
|
393
|
-
flashToast(
|
|
399
|
+
flashToast(formatMessage(msg.value.page.toastPlanCreated, { planKey: created.planKey }));
|
|
394
400
|
await onOpenPlan(created);
|
|
395
401
|
openCreateDraftWithPrefill({
|
|
396
402
|
features: payload.initialFeatures,
|
|
@@ -550,7 +556,7 @@ async function onUpdatePlanFromDetail(patch: { label: string }): Promise<void> {
|
|
|
550
556
|
selectedPlan.value = updated;
|
|
551
557
|
} catch (err: unknown) {
|
|
552
558
|
// eslint-disable-next-line no-console
|
|
553
|
-
console.error('[PlansPage] Plan
|
|
559
|
+
console.error('[PlansPage] Plan rename failed', err);
|
|
554
560
|
}
|
|
555
561
|
}
|
|
556
562
|
|
|
@@ -564,7 +570,10 @@ const availableBundles = ref<BundleEntry[]>([]);
|
|
|
564
570
|
// all consumer admin UIs label things identically.
|
|
565
571
|
const featureCatalogEntries = ref<FeatureCatalogEntryRow[]>([]);
|
|
566
572
|
const featureRegistry = computed<Record<string, { label?: string; group?: string }>>(() => ({
|
|
567
|
-
...buildFeatureRegistry(
|
|
573
|
+
...buildFeatureRegistry(
|
|
574
|
+
featureCatalogEntries.value,
|
|
575
|
+
props.displayLocale ?? CATALOG_DEFAULT_LOCALE,
|
|
576
|
+
),
|
|
568
577
|
...props.featureRegistry,
|
|
569
578
|
}));
|
|
570
579
|
|
|
@@ -659,28 +668,32 @@ function describeDraftSaveError(err: unknown): string {
|
|
|
659
668
|
const body = e?.body;
|
|
660
669
|
const code = body?.code;
|
|
661
670
|
if (status === 401 || status === 403) {
|
|
662
|
-
return
|
|
671
|
+
return msg.value.page.errorSessionExpiredSave;
|
|
663
672
|
}
|
|
664
673
|
if (status === 422 && code === 'STRICT_MODE_VIOLATIONS') {
|
|
665
674
|
const list = (body?.warnings ?? [])
|
|
666
675
|
.map((w) => w.message)
|
|
667
676
|
.filter(Boolean)
|
|
668
677
|
.join(' · ');
|
|
669
|
-
return
|
|
678
|
+
return formatMessage(msg.value.page.errorStrictMode, {
|
|
679
|
+
details: list || msg.value.page.errorStrictModeFallback,
|
|
680
|
+
});
|
|
670
681
|
}
|
|
671
682
|
if (status === 422 && code === 'PLAN_VERSION_REGRESSION') {
|
|
672
|
-
return
|
|
683
|
+
return msg.value.page.errorDraftRegression;
|
|
673
684
|
}
|
|
674
685
|
if (status === 422 && body?.message) {
|
|
675
686
|
// e.g. "Plan 'BASIC' hat bereits eine Draft-Version v4 …"
|
|
676
687
|
return body.message;
|
|
677
688
|
}
|
|
678
689
|
if (status !== undefined) {
|
|
679
|
-
return
|
|
690
|
+
return (
|
|
691
|
+
body?.message ?? formatMessage(msg.value.page.errorSaveFailedHttp, { status: status })
|
|
692
|
+
);
|
|
680
693
|
}
|
|
681
694
|
return err instanceof Error
|
|
682
|
-
?
|
|
683
|
-
:
|
|
695
|
+
? formatMessage(msg.value.page.errorSaveFailedDetail, { message: err.message })
|
|
696
|
+
: msg.value.page.errorSaveFailedConsole;
|
|
684
697
|
}
|
|
685
698
|
const draftEditing = ref<{
|
|
686
699
|
editingId: string | null;
|
|
@@ -903,13 +916,18 @@ async function onReviewSaveExit(): Promise<void> {
|
|
|
903
916
|
if (!saved) return;
|
|
904
917
|
const planKey = selectedPlan.value?.planKey ?? '';
|
|
905
918
|
flashHighlight(planKey);
|
|
906
|
-
flashToast(
|
|
919
|
+
flashToast(
|
|
920
|
+
formatMessage(msg.value.page.toastDraftSaved, {
|
|
921
|
+
version: saved.version,
|
|
922
|
+
planKey,
|
|
923
|
+
}),
|
|
924
|
+
);
|
|
907
925
|
reviewDraft.value = null;
|
|
908
926
|
draftEditing.value = null;
|
|
909
927
|
mode.value = selectedPlan.value ? 'cockpit' : 'list';
|
|
910
928
|
} catch (err: unknown) {
|
|
911
929
|
// eslint-disable-next-line no-console
|
|
912
|
-
console.error('[PlansPage]
|
|
930
|
+
console.error('[PlansPage] Saving the draft failed', err);
|
|
913
931
|
reviewError.value = describeDraftSaveError(err);
|
|
914
932
|
} finally {
|
|
915
933
|
draftSaving.value = false;
|
|
@@ -936,14 +954,17 @@ async function onReviewPublish(payload: {
|
|
|
936
954
|
const planKey = selectedPlan.value.planKey;
|
|
937
955
|
flashHighlight(planKey);
|
|
938
956
|
flashToast(
|
|
939
|
-
|
|
957
|
+
formatMessage(msg.value.page.toastPlanPublished, {
|
|
958
|
+
planKey,
|
|
959
|
+
version: result.planVersion.version,
|
|
960
|
+
}),
|
|
940
961
|
);
|
|
941
962
|
reviewDraft.value = null;
|
|
942
963
|
draftEditing.value = null;
|
|
943
964
|
mode.value = 'cockpit';
|
|
944
965
|
} catch (err: unknown) {
|
|
945
966
|
// eslint-disable-next-line no-console
|
|
946
|
-
console.error('[PlansPage]
|
|
967
|
+
console.error('[PlansPage] Publishing failed', err);
|
|
947
968
|
reviewError.value = describePublishError(err);
|
|
948
969
|
} finally {
|
|
949
970
|
publishing.value = false;
|
|
@@ -974,31 +995,29 @@ async function executeArchive(): Promise<void> {
|
|
|
974
995
|
await hardDelete(plan.id);
|
|
975
996
|
await reloadAllVersions();
|
|
976
997
|
archiveOpen.value = false;
|
|
977
|
-
flashToast(
|
|
998
|
+
flashToast(formatMessage(msg.value.page.toastPlanDeleted, { planKey: plan.planKey }));
|
|
978
999
|
} catch (err: unknown) {
|
|
979
1000
|
// eslint-disable-next-line no-console
|
|
980
|
-
console.error('[PlansPage] Archive/
|
|
1001
|
+
console.error('[PlansPage] Archive/purge failed', err);
|
|
981
1002
|
const status = (err as { status?: number })?.status;
|
|
982
1003
|
const body = (err as { body?: { code?: string; message?: string } })?.body;
|
|
983
1004
|
const errMessage = err instanceof Error ? err.message : String(err);
|
|
984
1005
|
if (status === 422 && body?.code === 'PLAN_HAS_PUBLISHED_VERSIONS') {
|
|
985
|
-
archiveError.value =
|
|
986
|
-
body.message ??
|
|
987
|
-
'Plan hat published Versionen — kann nicht gelöscht werden (Vertragsschutz P1).';
|
|
1006
|
+
archiveError.value = body.message ?? msg.value.page.errorPlanHasPublishedVersions;
|
|
988
1007
|
} else if (status === 422 && body?.code === 'PLAN_HAS_DRAFTS') {
|
|
989
|
-
archiveError.value =
|
|
990
|
-
body.message ??
|
|
991
|
-
'Plan hat noch einen offenen Draft — bitte erst über das Mülleimer-Icon verwerfen.';
|
|
1008
|
+
archiveError.value = body.message ?? msg.value.page.errorPlanHasDrafts;
|
|
992
1009
|
} else if (status === 422 && body?.code === 'PLAN_HARD_DELETE_NOT_IMPLEMENTED') {
|
|
993
|
-
archiveError.value =
|
|
994
|
-
'Backend unterstützt Hard-Delete noch nicht — API-Server neu bauen + starten.';
|
|
1010
|
+
archiveError.value = msg.value.page.errorHardDeleteNotImplemented;
|
|
995
1011
|
} else if (status === 404) {
|
|
996
|
-
archiveError.value =
|
|
1012
|
+
archiveError.value = msg.value.page.errorPlanAlreadyRemoved;
|
|
997
1013
|
await reloadAllVersions();
|
|
998
1014
|
} else if (status !== undefined) {
|
|
999
|
-
archiveError.value =
|
|
1015
|
+
archiveError.value =
|
|
1016
|
+
body?.message ?? formatMessage(msg.value.page.errorOperationFailedHttp, { status });
|
|
1000
1017
|
} else {
|
|
1001
|
-
archiveError.value =
|
|
1018
|
+
archiveError.value = formatMessage(msg.value.page.errorOperationFailedDetail, {
|
|
1019
|
+
message: errMessage,
|
|
1020
|
+
});
|
|
1002
1021
|
}
|
|
1003
1022
|
} finally {
|
|
1004
1023
|
archiving.value = false;
|
|
@@ -1037,35 +1056,38 @@ async function executeDiscard(): Promise<void> {
|
|
|
1037
1056
|
await reloadAllVersions();
|
|
1038
1057
|
discardOpen.value = false;
|
|
1039
1058
|
flashToast(
|
|
1040
|
-
|
|
1059
|
+
formatMessage(msg.value.page.toastDraftDiscarded, {
|
|
1060
|
+
version: draft.version,
|
|
1061
|
+
planKey: plan.planKey,
|
|
1062
|
+
}),
|
|
1041
1063
|
);
|
|
1042
1064
|
} catch (err: unknown) {
|
|
1043
1065
|
// Full error object to the console — in the diagnostic case you can see
|
|
1044
1066
|
// e.g. CORS preflight errors or the real network stack there.
|
|
1045
1067
|
// eslint-disable-next-line no-console
|
|
1046
|
-
console.error('[PlansPage] Discard
|
|
1068
|
+
console.error('[PlansPage] Discard failed', err);
|
|
1047
1069
|
|
|
1048
1070
|
const status = (err as { status?: number })?.status;
|
|
1049
1071
|
const body = (err as { body?: { code?: string; message?: string } })?.body;
|
|
1050
1072
|
const errMessage = err instanceof Error ? err.message : String(err);
|
|
1051
1073
|
|
|
1052
1074
|
if (status === 422 && body?.code === 'PLAN_VERSION_ALREADY_PUBLISHED') {
|
|
1053
|
-
discardError.value =
|
|
1054
|
-
'Diese Version ist bereits published und kann nicht verworfen werden.';
|
|
1075
|
+
discardError.value = msg.value.page.errorVersionAlreadyPublished;
|
|
1055
1076
|
} else if (status === 422 && body?.code === 'PLAN_VERSION_DISCARD_NOT_IMPLEMENTED') {
|
|
1056
|
-
discardError.value =
|
|
1057
|
-
'Backend unterstützt das Verwerfen noch nicht — bitte API-Server neu bauen + starten.';
|
|
1077
|
+
discardError.value = msg.value.page.errorDiscardNotImplemented;
|
|
1058
1078
|
} else if (status === 404) {
|
|
1059
|
-
discardError.value =
|
|
1060
|
-
'Draft wurde bereits entfernt (404) — Liste wird gleich neu geladen.';
|
|
1079
|
+
discardError.value = msg.value.page.errorDraftAlreadyRemoved;
|
|
1061
1080
|
await reloadAllVersions();
|
|
1062
1081
|
} else if (status !== undefined) {
|
|
1063
|
-
discardError.value =
|
|
1082
|
+
discardError.value =
|
|
1083
|
+
body?.message ?? formatMessage(msg.value.page.errorDiscardFailedHttp, { status });
|
|
1064
1084
|
} else {
|
|
1065
1085
|
// No HTTP status → usually a network error / CORS preflight / failed
|
|
1066
1086
|
// caller-side (e.g. usePlanVersions construction). Show the real
|
|
1067
1087
|
// error message so the diagnostic path doesn't end at "HTTP ?".
|
|
1068
|
-
discardError.value =
|
|
1088
|
+
discardError.value = formatMessage(msg.value.page.errorDiscardFailedDetail, {
|
|
1089
|
+
message: errMessage,
|
|
1090
|
+
});
|
|
1069
1091
|
}
|
|
1070
1092
|
} finally {
|
|
1071
1093
|
discarding.value = false;
|
|
@@ -1078,13 +1100,15 @@ async function executeDiscard(): Promise<void> {
|
|
|
1078
1100
|
// consistent after the call.
|
|
1079
1101
|
async function onSubmitTerminate(versionId: string, endsAt: string): Promise<void> {
|
|
1080
1102
|
if (!planVersions.value) {
|
|
1081
|
-
throw new Error('PlanVersions
|
|
1103
|
+
throw new Error('PlanVersions composable is not initialized');
|
|
1082
1104
|
}
|
|
1083
1105
|
await planVersions.value.terminateVersion(versionId, endsAt);
|
|
1084
1106
|
await reloadCockpitVersions();
|
|
1085
1107
|
if (selectedPlan.value) {
|
|
1086
1108
|
flashToast(
|
|
1087
|
-
|
|
1109
|
+
formatMessage(msg.value.page.toastVersionTerminated, {
|
|
1110
|
+
version: versions.value.find((v) => v.id === versionId)?.version ?? '?',
|
|
1111
|
+
}),
|
|
1088
1112
|
);
|
|
1089
1113
|
}
|
|
1090
1114
|
}
|
|
@@ -1122,7 +1146,10 @@ async function executePublish(): Promise<void> {
|
|
|
1122
1146
|
if (selectedPlan.value) {
|
|
1123
1147
|
flashHighlight(selectedPlan.value.planKey);
|
|
1124
1148
|
flashToast(
|
|
1125
|
-
|
|
1149
|
+
formatMessage(msg.value.page.toastPlanPublished, {
|
|
1150
|
+
planKey: selectedPlan.value.planKey,
|
|
1151
|
+
version: result.planVersion.version,
|
|
1152
|
+
}),
|
|
1126
1153
|
);
|
|
1127
1154
|
}
|
|
1128
1155
|
} catch (err: unknown) {
|
|
@@ -1140,19 +1167,21 @@ async function executePublish(): Promise<void> {
|
|
|
1140
1167
|
}
|
|
1141
1168
|
}
|
|
1142
1169
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
'features.
|
|
1148
|
-
'
|
|
1149
|
-
'quotas.
|
|
1150
|
-
'
|
|
1151
|
-
'bundles.
|
|
1170
|
+
// API field path of a regression change → catalog key of its label.
|
|
1171
|
+
const REGRESSION_FIELD_KEYS: Record<string, keyof SaMessages['plans']['regressionFields']> = {
|
|
1172
|
+
monthlyNet: 'monthlyNet',
|
|
1173
|
+
yearlyNet: 'yearlyNet',
|
|
1174
|
+
'features.removed': 'featuresRemoved',
|
|
1175
|
+
'features.added': 'featuresAdded',
|
|
1176
|
+
'quotas.lowered': 'quotasLowered',
|
|
1177
|
+
'quotas.raised': 'quotasRaised',
|
|
1178
|
+
'bundles.removed': 'bundlesRemoved',
|
|
1179
|
+
'bundles.added': 'bundlesAdded',
|
|
1152
1180
|
};
|
|
1153
1181
|
|
|
1154
1182
|
function fieldLabel(field: string): string {
|
|
1155
|
-
|
|
1183
|
+
const key = REGRESSION_FIELD_KEYS[field];
|
|
1184
|
+
return key ? msg.value.regressionFields[key] : field;
|
|
1156
1185
|
}
|
|
1157
1186
|
|
|
1158
1187
|
function formatChangeValue(value: unknown): string {
|
|
@@ -1175,21 +1204,24 @@ function describePublishError(err: unknown): string {
|
|
|
1175
1204
|
const status = (err as { status?: number })?.status;
|
|
1176
1205
|
const body = (err as { body?: { code?: string; message?: string } })?.body;
|
|
1177
1206
|
if (status === 401 || status === 403) {
|
|
1178
|
-
return
|
|
1207
|
+
return msg.value.page.errorSessionExpiredPublish;
|
|
1179
1208
|
}
|
|
1180
1209
|
if (status === 422 && body?.code === 'PLAN_VERSION_REGRESSION') {
|
|
1181
|
-
return
|
|
1210
|
+
return msg.value.page.errorPublishRegression;
|
|
1182
1211
|
}
|
|
1183
1212
|
if (status === 422 && body?.code === 'PLAN_VERSION_VALID_FROM_REQUIRED') {
|
|
1184
|
-
return
|
|
1213
|
+
return msg.value.page.errorValidFromRequired;
|
|
1185
1214
|
}
|
|
1186
1215
|
if (status === 422 && body?.code === 'PLAN_VERSION_VALID_FROM_NOT_AFTER_PREVIOUS') {
|
|
1187
|
-
return
|
|
1216
|
+
return msg.value.page.errorValidFromNotAfterPrevious;
|
|
1188
1217
|
}
|
|
1189
1218
|
if (status === 422 && body?.code === 'PLAN_VERSION_ZERO_PRICE') {
|
|
1190
|
-
return
|
|
1219
|
+
return msg.value.page.errorZeroPrice;
|
|
1191
1220
|
}
|
|
1192
|
-
return
|
|
1221
|
+
return (
|
|
1222
|
+
body?.message ??
|
|
1223
|
+
formatMessage(msg.value.page.errorPublishFailedHttp, { status: status ?? '?' })
|
|
1224
|
+
);
|
|
1193
1225
|
}
|
|
1194
1226
|
|
|
1195
1227
|
watch(plans, async () => {
|