@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
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<section class="sa-bundles">
|
|
3
3
|
<div class="sa-bundles__head">
|
|
4
|
-
<h2 class="sa-bundles__title">
|
|
4
|
+
<h2 class="sa-bundles__title">{{ msg.bundleOverview.title }}</h2>
|
|
5
5
|
<span class="sa-bundles__count">{{ bundles.length }}</span>
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
<div v-if="bundles.length === 0" class="sa-bundles__empty">
|
|
9
|
-
|
|
9
|
+
{{ msg.bundleOverview.empty }}
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
12
|
<div v-else class="sa-bundles__grid">
|
|
13
|
-
<q-card
|
|
13
|
+
<q-card
|
|
14
|
+
v-for="bundle in bundles"
|
|
15
|
+
:key="bundle.bundleKey"
|
|
16
|
+
flat
|
|
17
|
+
bordered
|
|
18
|
+
class="sa-bundle"
|
|
19
|
+
>
|
|
14
20
|
<q-card-section class="sa-bundle__header">
|
|
15
21
|
<div class="sa-bundle__label">{{ bundle.label ?? bundle.bundleKey }}</div>
|
|
16
22
|
<div class="sa-bundle__key">{{ bundle.bundleKey }}</div>
|
|
17
23
|
</q-card-section>
|
|
18
24
|
<q-separator />
|
|
19
25
|
<q-card-section>
|
|
20
|
-
<div class="sa-bundle__caption">
|
|
26
|
+
<div class="sa-bundle__caption">{{ msg.bundleOverview.contains }}</div>
|
|
21
27
|
<div class="sa-bundle__chips">
|
|
22
28
|
<q-chip
|
|
23
29
|
v-for="feature in bundle.features"
|
|
@@ -33,7 +39,7 @@
|
|
|
33
39
|
</div>
|
|
34
40
|
</q-card-section>
|
|
35
41
|
<q-card-section class="sa-bundle__compat">
|
|
36
|
-
<div class="sa-bundle__caption">
|
|
42
|
+
<div class="sa-bundle__caption">{{ msg.bundleOverview.compatibleWith }}</div>
|
|
37
43
|
<div class="sa-bundle__plans">{{ compatLabel(bundle) }}</div>
|
|
38
44
|
</q-card-section>
|
|
39
45
|
</q-card>
|
|
@@ -43,6 +49,7 @@
|
|
|
43
49
|
|
|
44
50
|
<script setup lang="ts">
|
|
45
51
|
import type { PlanRow } from '@saasicat/types';
|
|
52
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
46
53
|
|
|
47
54
|
interface BundleEntry {
|
|
48
55
|
bundleKey: string;
|
|
@@ -57,6 +64,8 @@ const props = defineProps<{
|
|
|
57
64
|
featureRegistry: Record<string, { label?: string; group?: string }>;
|
|
58
65
|
}>();
|
|
59
66
|
|
|
67
|
+
const msg = useSaMessages('plans');
|
|
68
|
+
|
|
60
69
|
function featureLabel(featureKey: string): string {
|
|
61
70
|
return props.featureRegistry[featureKey]?.label ?? featureKey;
|
|
62
71
|
}
|
|
@@ -68,7 +77,7 @@ function planLabel(planKey: string): string {
|
|
|
68
77
|
// Empty compatibility list = bundle applies to all plans (cf. PlanMatrix.hasBundle).
|
|
69
78
|
function compatLabel(bundle: BundleEntry): string {
|
|
70
79
|
const keys = bundle.compatiblePlanKeys ?? [];
|
|
71
|
-
if (keys.length === 0) return
|
|
80
|
+
if (keys.length === 0) return msg.value.bundleOverview.allPlans;
|
|
72
81
|
return keys.map(planLabel).join(', ');
|
|
73
82
|
}
|
|
74
83
|
</script>
|
|
@@ -6,21 +6,20 @@
|
|
|
6
6
|
>
|
|
7
7
|
<q-card style="min-width: 400px">
|
|
8
8
|
<q-card-section>
|
|
9
|
-
<div class="text-h6">
|
|
9
|
+
<div class="text-h6">{{ msg.discardDialog.title }}</div>
|
|
10
10
|
<div class="text-body2 q-mt-sm">
|
|
11
|
-
|
|
12
|
-
<code>{{ target?.plan.planKey }}</code>
|
|
13
|
-
Versions bleiben unverändert.
|
|
11
|
+
{{ bodyLead }}
|
|
12
|
+
<code>{{ target?.plan.planKey }}</code> {{ msg.discardDialog.bodyTail }}
|
|
14
13
|
</div>
|
|
15
14
|
</q-card-section>
|
|
16
15
|
<q-banner v-if="error" class="bg-warning q-mx-md q-mb-md" rounded>
|
|
17
16
|
{{ error }}
|
|
18
17
|
</q-banner>
|
|
19
18
|
<q-card-actions align="right">
|
|
20
|
-
<q-btn flat label="
|
|
19
|
+
<q-btn flat :label="common.cancel" @click="$emit('update:modelValue', false)" />
|
|
21
20
|
<q-btn
|
|
22
21
|
color="negative"
|
|
23
|
-
label="
|
|
22
|
+
:label="common.discard"
|
|
24
23
|
:loading="discarding"
|
|
25
24
|
@click="$emit('execute')"
|
|
26
25
|
/>
|
|
@@ -30,15 +29,27 @@
|
|
|
30
29
|
</template>
|
|
31
30
|
|
|
32
31
|
<script setup lang="ts">
|
|
32
|
+
import { computed } from 'vue';
|
|
33
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
34
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
33
35
|
import type { PlanDiscardTarget } from './types.js';
|
|
34
36
|
|
|
35
|
-
defineProps<{
|
|
37
|
+
const props = defineProps<{
|
|
36
38
|
modelValue: boolean;
|
|
37
39
|
target: PlanDiscardTarget | null;
|
|
38
40
|
error: string | null;
|
|
39
41
|
discarding: boolean;
|
|
40
42
|
}>();
|
|
41
43
|
|
|
44
|
+
const msg = useSaMessages('plans');
|
|
45
|
+
const common = useSaMessages('common');
|
|
46
|
+
|
|
47
|
+
const bodyLead = computed(() =>
|
|
48
|
+
formatMessage(msg.value.discardDialog.bodyLead, {
|
|
49
|
+
version: props.target?.draft.version ?? '',
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
|
|
42
53
|
defineEmits<{
|
|
43
54
|
(e: 'update:modelValue', value: boolean): void;
|
|
44
55
|
(e: 'execute'): void;
|
|
@@ -6,25 +6,24 @@
|
|
|
6
6
|
>
|
|
7
7
|
<q-card style="min-width: 420px">
|
|
8
8
|
<q-card-section>
|
|
9
|
-
<div class="text-h6">
|
|
9
|
+
<div class="text-h6">{{ msg.publishDialog.title }}</div>
|
|
10
10
|
<div class="text-body2 q-mt-sm">
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
{{ msg.publishDialog.bodyLead }} <code>{{ selectedPlan?.planKey }}</code
|
|
12
|
+
>{{ bodyTail }}
|
|
13
13
|
</div>
|
|
14
14
|
<div class="text-caption text-grey-7 q-mt-sm">
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
nächsten Renewal).
|
|
15
|
+
{{ msg.publishDialog.supersededNoteLead }} <code>supersededAt</code>
|
|
16
|
+
{{ msg.publishDialog.supersededNoteTail }}
|
|
18
17
|
</div>
|
|
19
18
|
<q-checkbox
|
|
20
19
|
:model-value="forceRegressive"
|
|
21
|
-
label="
|
|
20
|
+
:label="msg.publishDialog.forceRegressive"
|
|
22
21
|
class="q-mt-md"
|
|
23
22
|
@update:model-value="$emit('update:forceRegressive', Boolean($event))"
|
|
24
23
|
/>
|
|
25
24
|
<q-checkbox
|
|
26
25
|
:model-value="allowZeroPrice"
|
|
27
|
-
label="
|
|
26
|
+
:label="msg.publishDialog.allowZeroPrice"
|
|
28
27
|
class="q-mt-sm"
|
|
29
28
|
@update:model-value="$emit('update:allowZeroPrice', Boolean($event))"
|
|
30
29
|
/>
|
|
@@ -33,7 +32,7 @@
|
|
|
33
32
|
{{ publishError }}
|
|
34
33
|
</q-banner>
|
|
35
34
|
<q-card-section v-if="regressionChanges.length > 0" class="q-pt-none">
|
|
36
|
-
<div class="text-subtitle2 q-mb-xs">
|
|
35
|
+
<div class="text-subtitle2 q-mb-xs">{{ msg.publishDialog.regressiveChanges }}</div>
|
|
37
36
|
<ul class="sa-publish-regression">
|
|
38
37
|
<li v-for="c in regressionChanges" :key="c.field">
|
|
39
38
|
<strong>{{ fieldLabel(c.field) }}:</strong>
|
|
@@ -48,10 +47,10 @@
|
|
|
48
47
|
</ul>
|
|
49
48
|
</q-card-section>
|
|
50
49
|
<q-card-actions align="right">
|
|
51
|
-
<q-btn flat label="
|
|
50
|
+
<q-btn flat :label="common.cancel" @click="$emit('update:modelValue', false)" />
|
|
52
51
|
<q-btn
|
|
53
52
|
color="primary"
|
|
54
|
-
label="
|
|
53
|
+
:label="msg.publishDialog.confirm"
|
|
55
54
|
:loading="publishing"
|
|
56
55
|
@click="$emit('execute')"
|
|
57
56
|
/>
|
|
@@ -61,10 +60,13 @@
|
|
|
61
60
|
</template>
|
|
62
61
|
|
|
63
62
|
<script setup lang="ts">
|
|
63
|
+
import { computed } from 'vue';
|
|
64
64
|
import type { PlanRow, PlanVersionRow } from '@saasicat/types';
|
|
65
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
66
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
65
67
|
import type { RegressionChange } from './types.js';
|
|
66
68
|
|
|
67
|
-
defineProps<{
|
|
69
|
+
const props = defineProps<{
|
|
68
70
|
modelValue: boolean;
|
|
69
71
|
selectedPlan: PlanRow | null;
|
|
70
72
|
publishTarget: PlanVersionRow | null;
|
|
@@ -77,6 +79,15 @@ defineProps<{
|
|
|
77
79
|
formatChangeValue: (value: unknown) => string;
|
|
78
80
|
}>();
|
|
79
81
|
|
|
82
|
+
const msg = useSaMessages('plans');
|
|
83
|
+
const common = useSaMessages('common');
|
|
84
|
+
|
|
85
|
+
const bodyTail = computed(() =>
|
|
86
|
+
formatMessage(msg.value.publishDialog.bodyTail, {
|
|
87
|
+
version: props.publishTarget?.version ?? '',
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
|
|
80
91
|
defineEmits<{
|
|
81
92
|
(e: 'update:modelValue', value: boolean): void;
|
|
82
93
|
(e: 'update:forceRegressive', value: boolean): void;
|
|
@@ -28,9 +28,10 @@ export function planAccent(
|
|
|
28
28
|
return accents[planId] ?? '#3f6bff';
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
/** `locale` is a BCP-47 tag — pages pass `intlLocale.value` of the active UI locale. */
|
|
32
|
+
export function formatDate(iso: string | null | undefined, locale: string): string {
|
|
32
33
|
if (!iso) return '—';
|
|
33
|
-
return new Date(iso).toLocaleDateString(
|
|
34
|
+
return new Date(iso).toLocaleDateString(locale, {
|
|
34
35
|
day: '2-digit',
|
|
35
36
|
month: 'short',
|
|
36
37
|
year: 'numeric',
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
<div class="msb">
|
|
3
3
|
<header class="msb-head">
|
|
4
4
|
<div>
|
|
5
|
-
<h2 class="msb-title">
|
|
6
|
-
<p class="msb-sub">
|
|
7
|
-
Eigenständig gebuchte Add-On-Pakete zu deinem Plan. Mindestlaufzeit +
|
|
8
|
-
Kündigungs- Termin pro Bundle.
|
|
9
|
-
</p>
|
|
5
|
+
<h2 class="msb-title">{{ effectiveI18n.myBundlesTitle }}</h2>
|
|
6
|
+
<p class="msb-sub">{{ effectiveI18n.myBundlesSubtitle }}</p>
|
|
10
7
|
</div>
|
|
11
8
|
<button class="msb-btn msb-btn--primary" type="button" @click="openAddDialog">
|
|
12
9
|
<svg
|
|
@@ -19,22 +16,24 @@
|
|
|
19
16
|
>
|
|
20
17
|
<path d="M12 5v14M5 12h14" />
|
|
21
18
|
</svg>
|
|
22
|
-
<span>
|
|
19
|
+
<span>{{ effectiveI18n.bundleBookAction }}</span>
|
|
23
20
|
</button>
|
|
24
21
|
</header>
|
|
25
22
|
|
|
26
23
|
<div v-if="error" class="msb-error" role="alert">
|
|
27
|
-
<strong>
|
|
24
|
+
<strong>{{ effectiveI18n.errorLabel }}:</strong> {{ error.message }}
|
|
28
25
|
</div>
|
|
29
26
|
|
|
30
|
-
<div v-if="loading && bundles.length === 0" class="msb-loading">
|
|
27
|
+
<div v-if="loading && bundles.length === 0" class="msb-loading">
|
|
28
|
+
{{ effectiveI18n.loading }}
|
|
29
|
+
</div>
|
|
31
30
|
|
|
32
31
|
<div v-else-if="bundles.length === 0" class="msb-empty">
|
|
33
|
-
|
|
32
|
+
{{ effectiveI18n.myBundlesEmptyPrefix }}
|
|
34
33
|
<button type="button" class="msb-empty-link" @click="openAddDialog">
|
|
35
|
-
|
|
34
|
+
{{ effectiveI18n.bundleBookAction }}
|
|
36
35
|
</button>
|
|
37
|
-
|
|
36
|
+
{{ effectiveI18n.myBundlesEmptySuffix }}
|
|
38
37
|
</div>
|
|
39
38
|
|
|
40
39
|
<div v-else class="msb-list">
|
|
@@ -48,9 +47,10 @@
|
|
|
48
47
|
</span>
|
|
49
48
|
</div>
|
|
50
49
|
<div class="msb-card-meta">
|
|
51
|
-
|
|
50
|
+
{{ effectiveI18n.myBundlesBookedSince }} {{ formatDate(b.startedAt) }}
|
|
52
51
|
<template v-if="b.minimumTermEndsAt">
|
|
53
|
-
·
|
|
52
|
+
· {{ effectiveI18n.bundleMinimumTermUntil }}
|
|
53
|
+
{{ formatDate(b.minimumTermEndsAt) }}
|
|
54
54
|
</template>
|
|
55
55
|
</div>
|
|
56
56
|
</div>
|
|
@@ -61,11 +61,16 @@
|
|
|
61
61
|
:disabled="cancellingId === b.id"
|
|
62
62
|
@click="onCancel(b)"
|
|
63
63
|
>
|
|
64
|
-
{{
|
|
64
|
+
{{
|
|
65
|
+
cancellingId === b.id
|
|
66
|
+
? effectiveI18n.myBundlesCancelInProgress
|
|
67
|
+
: effectiveI18n.bundleCancelAction
|
|
68
|
+
}}
|
|
65
69
|
</button>
|
|
66
70
|
</header>
|
|
67
71
|
<div v-if="b.canceledAt !== null" class="msb-cancel-info">
|
|
68
|
-
|
|
72
|
+
{{ effectiveI18n.myBundlesCanceledAt }} {{ formatDate(b.canceledAt) }} ·
|
|
73
|
+
{{ effectiveI18n.myBundlesRunsUntil }}
|
|
69
74
|
<strong>{{ formatDate(b.canceledEffectiveAt) }}</strong>
|
|
70
75
|
</div>
|
|
71
76
|
</article>
|
|
@@ -75,11 +80,11 @@
|
|
|
75
80
|
<div v-if="addOpen" class="msb-modal-bg" @click="closeAddDialog">
|
|
76
81
|
<div class="msb-modal" @click.stop>
|
|
77
82
|
<div class="msb-modal-head">
|
|
78
|
-
<div class="msb-modal-title">
|
|
83
|
+
<div class="msb-modal-title">{{ effectiveI18n.bundleBookAction }}</div>
|
|
79
84
|
<button
|
|
80
85
|
class="msb-modal-x"
|
|
81
86
|
type="button"
|
|
82
|
-
aria-label="
|
|
87
|
+
:aria-label="effectiveI18n.wizardClose"
|
|
83
88
|
@click="closeAddDialog"
|
|
84
89
|
>
|
|
85
90
|
<svg
|
|
@@ -98,9 +103,13 @@
|
|
|
98
103
|
<!-- Selection list via public catalog, otherwise fallback to direct UUID input. -->
|
|
99
104
|
<template v-if="bookable.length > 0">
|
|
100
105
|
<label class="msb-field">
|
|
101
|
-
<span class="msb-field-label">
|
|
106
|
+
<span class="msb-field-label">{{
|
|
107
|
+
effectiveI18n.myBundlesAddBundleLabel
|
|
108
|
+
}}</span>
|
|
102
109
|
<select v-model="addForm.bundleVersionId" class="msb-input">
|
|
103
|
-
<option value=""
|
|
110
|
+
<option value="">
|
|
111
|
+
{{ effectiveI18n.myBundlesAddSelectPlaceholder }}
|
|
112
|
+
</option>
|
|
104
113
|
<option
|
|
105
114
|
v-for="b in bookable"
|
|
106
115
|
:key="b.bundleVersionId"
|
|
@@ -108,54 +117,67 @@
|
|
|
108
117
|
>
|
|
109
118
|
{{ b.label }} ({{ b.bundleKey }})
|
|
110
119
|
<template v-if="b.monthlyNet !== null">
|
|
111
|
-
— {{ b.monthlyNet }}
|
|
120
|
+
— {{ b.monthlyNet }}
|
|
121
|
+
{{ effectiveI18n.myBundlesPricePerMonthShort }}
|
|
112
122
|
</template>
|
|
113
123
|
</option>
|
|
114
124
|
</select>
|
|
115
125
|
<span v-if="hiddenBecauseIncompatible > 0" class="msb-field-hint">
|
|
116
|
-
{{ hiddenBecauseIncompatible }}
|
|
117
|
-
|
|
126
|
+
{{ hiddenBecauseIncompatible }}
|
|
127
|
+
{{ effectiveI18n.myBundlesHiddenIncompatible }}
|
|
128
|
+
<code>{{ currentPlanKey }}</code
|
|
118
129
|
>.
|
|
119
130
|
</span>
|
|
120
131
|
</label>
|
|
121
132
|
</template>
|
|
122
133
|
<template v-else>
|
|
123
134
|
<label class="msb-field">
|
|
124
|
-
<span class="msb-field-label">
|
|
135
|
+
<span class="msb-field-label">
|
|
136
|
+
{{ effectiveI18n.myBundlesBundleVersionIdLabel }}
|
|
137
|
+
</span>
|
|
125
138
|
<input
|
|
126
139
|
v-model="addForm.bundleVersionId"
|
|
127
140
|
class="msb-input"
|
|
128
|
-
placeholder="
|
|
141
|
+
:placeholder="effectiveI18n.myBundlesBundleVersionIdPlaceholder"
|
|
129
142
|
/>
|
|
143
|
+
<!-- Integration hint for the embedding app, not for tenants. -->
|
|
130
144
|
<span class="msb-field-hint">
|
|
131
|
-
|
|
145
|
+
From the public marketing catalog. Pass
|
|
132
146
|
<code>availableBundles</code>
|
|
133
|
-
|
|
147
|
+
as a prop to get a dropdown here.
|
|
134
148
|
</span>
|
|
135
149
|
</label>
|
|
136
150
|
</template>
|
|
137
151
|
<label class="msb-field">
|
|
138
|
-
<span class="msb-field-label">
|
|
152
|
+
<span class="msb-field-label">{{
|
|
153
|
+
effectiveI18n.myBundlesMinimumTermLabel
|
|
154
|
+
}}</span>
|
|
139
155
|
<input
|
|
140
156
|
v-model.number="addForm.minimumTermMonths"
|
|
141
157
|
type="number"
|
|
142
158
|
min="0"
|
|
143
159
|
max="120"
|
|
144
160
|
class="msb-input"
|
|
145
|
-
placeholder="
|
|
161
|
+
:placeholder="effectiveI18n.myBundlesMinimumTermPlaceholder"
|
|
146
162
|
/>
|
|
147
163
|
</label>
|
|
148
164
|
<div v-if="addError" class="msb-error">{{ addError }}</div>
|
|
149
165
|
</div>
|
|
150
166
|
<div class="msb-modal-foot">
|
|
151
|
-
<button class="msb-btn" type="button" @click="closeAddDialog">
|
|
167
|
+
<button class="msb-btn" type="button" @click="closeAddDialog">
|
|
168
|
+
{{ effectiveI18n.bundlePreviewClose }}
|
|
169
|
+
</button>
|
|
152
170
|
<button
|
|
153
171
|
class="msb-btn msb-btn--primary"
|
|
154
172
|
type="button"
|
|
155
173
|
:disabled="!canSubmit || adding"
|
|
156
174
|
@click="submitAdd"
|
|
157
175
|
>
|
|
158
|
-
{{
|
|
176
|
+
{{
|
|
177
|
+
adding
|
|
178
|
+
? effectiveI18n.myBundlesBookInProgress
|
|
179
|
+
: effectiveI18n.bundleBookAction
|
|
180
|
+
}}
|
|
159
181
|
</button>
|
|
160
182
|
</div>
|
|
161
183
|
</div>
|
|
@@ -167,7 +189,9 @@
|
|
|
167
189
|
import { computed, onMounted, reactive, ref } from 'vue';
|
|
168
190
|
import type { SubscriptionBundleRecord } from '@saasicat/types';
|
|
169
191
|
|
|
192
|
+
import { useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
170
193
|
import { useTenantSubscriptionBundles } from '../vue/use-tenant-subscription-bundles.js';
|
|
194
|
+
import { defaultTenantPlanSectionI18n, type TenantPlanSectionI18n } from './default-i18n.js';
|
|
171
195
|
|
|
172
196
|
// MySubscriptionBundlesPage — tenant self-service page "Meine Bundles".
|
|
173
197
|
// The hosting app embeds the page
|
|
@@ -208,6 +232,8 @@ const props = withDefaults(
|
|
|
208
232
|
currentPlanKey?: string | null;
|
|
209
233
|
/** Optional: auth token provider (for the Bearer header). */
|
|
210
234
|
getAuthToken?: () => string | null;
|
|
235
|
+
/** i18n overrides — missing keys fall back to the active locale's map. */
|
|
236
|
+
i18n?: Partial<TenantPlanSectionI18n>;
|
|
211
237
|
}>(),
|
|
212
238
|
{
|
|
213
239
|
bundleLabels: () => ({}),
|
|
@@ -217,6 +243,13 @@ const props = withDefaults(
|
|
|
217
243
|
},
|
|
218
244
|
);
|
|
219
245
|
|
|
246
|
+
const { locale, intlLocale } = useSuperAdminI18n();
|
|
247
|
+
|
|
248
|
+
const effectiveI18n = computed<TenantPlanSectionI18n>(() => ({
|
|
249
|
+
...defaultTenantPlanSectionI18n(locale.value),
|
|
250
|
+
...(props.i18n ?? {}),
|
|
251
|
+
}));
|
|
252
|
+
|
|
220
253
|
const { bundles, loading, error, load, add, cancel } = useTenantSubscriptionBundles({
|
|
221
254
|
billingEndpoint: props.billingEndpoint,
|
|
222
255
|
getAuthToken: props.getAuthToken,
|
|
@@ -308,9 +341,7 @@ async function submitAdd(): Promise<void> {
|
|
|
308
341
|
const cancellingId = ref<string | null>(null);
|
|
309
342
|
|
|
310
343
|
async function onCancel(b: SubscriptionBundleRecord): Promise<void> {
|
|
311
|
-
const ok = window.confirm(
|
|
312
|
-
`Bundle wirklich kündigen? Die Kündigung wird zum nächstmöglichen Termin wirksam.`,
|
|
313
|
-
);
|
|
344
|
+
const ok = window.confirm(effectiveI18n.value.myBundlesCancelConfirm);
|
|
314
345
|
if (!ok) return;
|
|
315
346
|
cancellingId.value = b.id;
|
|
316
347
|
try {
|
|
@@ -336,9 +367,9 @@ function statusOf(b: SubscriptionBundleRecord): 'active' | 'canceled-pending' |
|
|
|
336
367
|
|
|
337
368
|
function statusLabel(b: SubscriptionBundleRecord): string {
|
|
338
369
|
const s = statusOf(b);
|
|
339
|
-
if (s === 'active') return
|
|
340
|
-
if (s === 'canceled-pending') return
|
|
341
|
-
return
|
|
370
|
+
if (s === 'active') return effectiveI18n.value.statusActive;
|
|
371
|
+
if (s === 'canceled-pending') return effectiveI18n.value.myBundlesStatusCanceledPending;
|
|
372
|
+
return effectiveI18n.value.myBundlesStatusEnded;
|
|
342
373
|
}
|
|
343
374
|
|
|
344
375
|
function cardStatusClass(b: SubscriptionBundleRecord): string {
|
|
@@ -349,7 +380,11 @@ function formatDate(date: Date | string | null | undefined): string {
|
|
|
349
380
|
if (!date) return '—';
|
|
350
381
|
const d = typeof date === 'string' ? new Date(date) : date;
|
|
351
382
|
if (Number.isNaN(d.getTime())) return '—';
|
|
352
|
-
return d.toLocaleDateString(
|
|
383
|
+
return d.toLocaleDateString(intlLocale.value, {
|
|
384
|
+
year: 'numeric',
|
|
385
|
+
month: '2-digit',
|
|
386
|
+
day: '2-digit',
|
|
387
|
+
});
|
|
353
388
|
}
|
|
354
389
|
</script>
|
|
355
390
|
|
|
@@ -220,6 +220,7 @@ import { computed, ref, watch } from 'vue';
|
|
|
220
220
|
import LimitsRow from './LimitsRow.vue';
|
|
221
221
|
import PlanCycleToggle from '../components/plan/PlanCycleToggle.vue';
|
|
222
222
|
import PlanGrid from '../components/plan/PlanGrid.vue';
|
|
223
|
+
import { useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
223
224
|
import type { BillingCycleStr, PlanChangePreviewShape } from '../vue/use-tenant-billing.js';
|
|
224
225
|
import type { CatalogPlan } from '../vue/use-tenant-billing-catalog.js';
|
|
225
226
|
|
|
@@ -239,6 +240,7 @@ interface I18nStrings {
|
|
|
239
240
|
badgeCurrent: string;
|
|
240
241
|
badgePopular: string;
|
|
241
242
|
priceUnitMonthly: string;
|
|
243
|
+
priceUnitYearly: string;
|
|
242
244
|
priceOnRequest: string;
|
|
243
245
|
stepChoose: string;
|
|
244
246
|
stepChooseIntro: string;
|
|
@@ -302,8 +304,8 @@ interface Props {
|
|
|
302
304
|
formatQuotaLabel?: (key: string, value: number) => string;
|
|
303
305
|
/**
|
|
304
306
|
* Plain value per quota key, e.g. "200" or "10 GB" (for PlanGrid).
|
|
305
|
-
* Optional — default: takes `value.toLocaleString(
|
|
306
|
-
* `GB` suffix for `storage` keys, replaces -1 with ∞.
|
|
307
|
+
* Optional — default: takes `value.toLocaleString()` in the active UI
|
|
308
|
+
* locale, appends a `GB` suffix for `storage` keys, replaces -1 with ∞.
|
|
307
309
|
*/
|
|
308
310
|
formatQuotaValue?: (key: string, value: number) => string;
|
|
309
311
|
quotaLabel: (key: string) => string;
|
|
@@ -321,6 +323,8 @@ const emit = defineEmits<{
|
|
|
321
323
|
submitted: [];
|
|
322
324
|
}>();
|
|
323
325
|
|
|
326
|
+
const { intlLocale } = useSuperAdminI18n();
|
|
327
|
+
|
|
324
328
|
const model = computed({
|
|
325
329
|
get: () => props.modelValue,
|
|
326
330
|
set: (v) => emit('update:modelValue', v),
|
|
@@ -430,7 +434,7 @@ function formatQuotaValueResolved(key: string, value: number | null | undefined)
|
|
|
430
434
|
if (value === null || value === undefined || Number.isNaN(value)) return '–';
|
|
431
435
|
if (value < 0) return '∞';
|
|
432
436
|
if (key.toLowerCase().includes('storage')) return `${value} GB`;
|
|
433
|
-
return value.toLocaleString(
|
|
437
|
+
return value.toLocaleString(intlLocale.value);
|
|
434
438
|
}
|
|
435
439
|
|
|
436
440
|
const cycleI18n = computed(() => ({
|
|
@@ -443,7 +447,7 @@ const planGridI18n = computed(() => ({
|
|
|
443
447
|
popular: props.i18n.badgePopular,
|
|
444
448
|
current: props.i18n.badgeCurrent,
|
|
445
449
|
perMonth: props.i18n.priceUnitMonthly,
|
|
446
|
-
perYear: props.i18n.
|
|
450
|
+
perYear: props.i18n.priceUnitYearly,
|
|
447
451
|
priceOnRequest: props.i18n.priceOnRequest,
|
|
448
452
|
}));
|
|
449
453
|
|
|
@@ -122,7 +122,11 @@
|
|
|
122
122
|
<!-- Reactivate confirmation (analogous to cancellation): deliberate action -->
|
|
123
123
|
<q-dialog
|
|
124
124
|
:model-value="reactivateConfirmId !== null"
|
|
125
|
-
@update:model-value="
|
|
125
|
+
@update:model-value="
|
|
126
|
+
(v) => {
|
|
127
|
+
if (!v) closeReactivateConfirm();
|
|
128
|
+
}
|
|
129
|
+
"
|
|
126
130
|
>
|
|
127
131
|
<q-card style="min-width: 360px; max-width: 480px">
|
|
128
132
|
<q-card-section>
|
|
@@ -194,7 +198,7 @@ import { computed, ref } from 'vue';
|
|
|
194
198
|
import PackageSnapshotPanel from './PackageSnapshotPanel.vue';
|
|
195
199
|
import PendingVersionBanner from './PendingVersionBanner.vue';
|
|
196
200
|
import PlanChangeWizard from './PlanChangeWizard.vue';
|
|
197
|
-
import {
|
|
201
|
+
import { defaultTenantPlanSectionI18n, type TenantPlanSectionI18n } from './default-i18n.js';
|
|
198
202
|
import BundlePreviewDialog from './tenant-plan-section/BundlePreviewDialog.vue';
|
|
199
203
|
import TenantBundleStore from './tenant-plan-section/TenantBundleStore.vue';
|
|
200
204
|
import TenantFeatureMatrix from './tenant-plan-section/TenantFeatureMatrix.vue';
|
|
@@ -210,6 +214,7 @@ import {
|
|
|
210
214
|
type CatalogBundle,
|
|
211
215
|
type CatalogPlan,
|
|
212
216
|
} from '../vue/use-tenant-billing-catalog.js';
|
|
217
|
+
import { useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
213
218
|
import type { HttpClient } from '../client/types.js';
|
|
214
219
|
|
|
215
220
|
// TenantPlanSection — main component for the tenant plan/bundle self-service
|
|
@@ -234,7 +239,8 @@ interface Props {
|
|
|
234
239
|
* Plain value per quota key (e.g. "200" or "10 GB") — separate from the
|
|
235
240
|
* label so the wizard's `<PlanGrid>` can render value + label separately.
|
|
236
241
|
* Optional; without an override the wizard default applies
|
|
237
|
-
* (`value.toLocaleString(
|
|
242
|
+
* (`value.toLocaleString()` in the active UI locale + `GB` for storage
|
|
243
|
+
* keys, ∞ for -1).
|
|
238
244
|
*/
|
|
239
245
|
formatQuotaValue?: (key: string, value: number) => string;
|
|
240
246
|
|
|
@@ -245,7 +251,7 @@ interface Props {
|
|
|
245
251
|
/** Default: storage quotas (float), all others integer. */
|
|
246
252
|
isFractionalQuota?: (key: string) => boolean;
|
|
247
253
|
|
|
248
|
-
/** i18n overrides — missing keys fall back to
|
|
254
|
+
/** i18n overrides — missing keys fall back to the active locale's map. */
|
|
249
255
|
i18n?: Partial<TenantPlanSectionI18n>;
|
|
250
256
|
|
|
251
257
|
/**
|
|
@@ -264,6 +270,8 @@ interface Props {
|
|
|
264
270
|
|
|
265
271
|
const props = defineProps<Props>();
|
|
266
272
|
|
|
273
|
+
const { locale } = useSuperAdminI18n();
|
|
274
|
+
|
|
267
275
|
const billing = useTenantBilling({
|
|
268
276
|
http: props.http,
|
|
269
277
|
getAuthToken: props.getAuthToken,
|
|
@@ -287,8 +295,7 @@ const bundleError = ref<string | null>(null);
|
|
|
287
295
|
|
|
288
296
|
// Bundle preview dialog state (#37/#61)
|
|
289
297
|
type PendingBundleAction =
|
|
290
|
-
|
|
291
|
-
| { kind: 'cancel'; subscriptionBundleId: string };
|
|
298
|
+
{ kind: 'add'; bundleVersionId: string } | { kind: 'cancel'; subscriptionBundleId: string };
|
|
292
299
|
const bundlePreviewOpen = ref(false);
|
|
293
300
|
const bundlePreview = ref<BundlePreviewShape | null>(null);
|
|
294
301
|
const bundlePreviewLoading = ref(false);
|
|
@@ -301,7 +308,7 @@ const loading = computed(() => billing.loading.value || catalog.loading.value);
|
|
|
301
308
|
const error = computed(() => billing.error.value ?? catalog.error.value);
|
|
302
309
|
|
|
303
310
|
const effectiveI18n = computed<TenantPlanSectionI18n>(() => ({
|
|
304
|
-
...
|
|
311
|
+
...defaultTenantPlanSectionI18n(locale.value),
|
|
305
312
|
...(props.i18n ?? {}),
|
|
306
313
|
}));
|
|
307
314
|
|
|
@@ -331,10 +338,7 @@ const availableBundles = computed<CatalogBundle[]>(() => catalog.bundles.value ?
|
|
|
331
338
|
// no longer active and disappears from the list.
|
|
332
339
|
function isSubscriptionBundleActive(b: SubscriptionBundleShape): boolean {
|
|
333
340
|
if (b.canceledAt === null) return true;
|
|
334
|
-
return (
|
|
335
|
-
b.canceledEffectiveAt !== null &&
|
|
336
|
-
new Date(b.canceledEffectiveAt).getTime() > Date.now()
|
|
337
|
-
);
|
|
341
|
+
return b.canceledEffectiveAt !== null && new Date(b.canceledEffectiveAt).getTime() > Date.now();
|
|
338
342
|
}
|
|
339
343
|
|
|
340
344
|
const bookedBundles = computed<SubscriptionBundleShape[]>(() =>
|
|
@@ -348,8 +352,7 @@ const hasBundleStore = computed(
|
|
|
348
352
|
const featureRegistry = computed(() => catalog.featureRegistry.value);
|
|
349
353
|
const activeFeatures = computed<string[]>(() => usage.value?.limits.features ?? []);
|
|
350
354
|
const hasFeatureOverview = computed(
|
|
351
|
-
() =>
|
|
352
|
-
Object.keys(featureRegistry.value ?? {}).length > 0 || activeFeatures.value.length > 0,
|
|
355
|
+
() => Object.keys(featureRegistry.value ?? {}).length > 0 || activeFeatures.value.length > 0,
|
|
353
356
|
);
|
|
354
357
|
|
|
355
358
|
const currentPlanName = computed(() => {
|
|
@@ -435,6 +438,7 @@ const wizardI18n = computed(() => ({
|
|
|
435
438
|
badgeCurrent: effectiveI18n.value.wizardBadgeCurrent,
|
|
436
439
|
badgePopular: effectiveI18n.value.wizardBadgePopular,
|
|
437
440
|
priceUnitMonthly: effectiveI18n.value.wizardPriceUnitMonthly,
|
|
441
|
+
priceUnitYearly: effectiveI18n.value.wizardPriceUnitYearly,
|
|
438
442
|
priceOnRequest: effectiveI18n.value.wizardPriceOnRequest,
|
|
439
443
|
stepChoose: effectiveI18n.value.wizardStepChoose,
|
|
440
444
|
stepChooseIntro: effectiveI18n.value.wizardStepChooseIntro,
|