@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
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
<div class="pcd-modal" role="dialog" aria-labelledby="pcd-title">
|
|
9
9
|
<div class="pcd-head">
|
|
10
10
|
<div class="pcd-head-text">
|
|
11
|
-
<div id="pcd-title" class="pcd-title">
|
|
12
|
-
<div class="pcd-sub">
|
|
13
|
-
Plan-Stamm anlegen. Im nächsten Schritt weist du Features, Quotas und
|
|
14
|
-
Bundles zu.
|
|
15
|
-
</div>
|
|
11
|
+
<div id="pcd-title" class="pcd-title">{{ msg.createDialog.title }}</div>
|
|
12
|
+
<div class="pcd-sub">{{ msg.createDialog.subtitle }}</div>
|
|
16
13
|
</div>
|
|
17
|
-
<button
|
|
14
|
+
<button
|
|
15
|
+
class="pcd-close"
|
|
16
|
+
type="button"
|
|
17
|
+
:aria-label="common.close"
|
|
18
|
+
@click="onCancel"
|
|
19
|
+
>
|
|
18
20
|
<svg
|
|
19
21
|
width="14"
|
|
20
22
|
height="14"
|
|
@@ -32,41 +34,43 @@
|
|
|
32
34
|
<div class="pcd-row pcd-row--2col">
|
|
33
35
|
<div class="pcd-field">
|
|
34
36
|
<div class="pcd-field-label">
|
|
35
|
-
|
|
37
|
+
{{ msg.createDialog.labelPlanKey }}
|
|
36
38
|
<span class="pcd-kbd">UNIQUE</span>
|
|
37
39
|
</div>
|
|
38
40
|
<input
|
|
39
41
|
ref="keyInput"
|
|
40
42
|
class="pcd-input"
|
|
41
43
|
:class="{ 'pcd-input--error': keyError }"
|
|
42
|
-
placeholder="
|
|
44
|
+
:placeholder="msg.createDialog.placeholderPlanKey"
|
|
43
45
|
:value="form.planKey"
|
|
44
46
|
@input="onPlanKeyInput"
|
|
45
47
|
/>
|
|
46
48
|
<div v-if="keyError" class="pcd-hint pcd-hint--error">{{ keyError }}</div>
|
|
47
|
-
<div v-else class="pcd-hint">
|
|
48
|
-
Großbuchstaben + Unterstriche · API-stabil, nicht änderbar nach Publish
|
|
49
|
-
</div>
|
|
49
|
+
<div v-else class="pcd-hint">{{ msg.createDialog.hintPlanKey }}</div>
|
|
50
50
|
</div>
|
|
51
51
|
<div class="pcd-field">
|
|
52
|
-
<div class="pcd-field-label">
|
|
53
|
-
<input
|
|
54
|
-
|
|
52
|
+
<div class="pcd-field-label">{{ msg.createDialog.labelDisplayName }}</div>
|
|
53
|
+
<input
|
|
54
|
+
class="pcd-input"
|
|
55
|
+
:placeholder="msg.createDialog.placeholderDisplayName"
|
|
56
|
+
v-model="form.label"
|
|
57
|
+
/>
|
|
58
|
+
<div class="pcd-hint">{{ msg.createDialog.hintDisplayName }}</div>
|
|
55
59
|
</div>
|
|
56
60
|
</div>
|
|
57
61
|
|
|
58
62
|
<div class="pcd-field">
|
|
59
|
-
<div class="pcd-field-label">
|
|
63
|
+
<div class="pcd-field-label">{{ common.description }}</div>
|
|
60
64
|
<textarea
|
|
61
65
|
class="pcd-input pcd-input--textarea"
|
|
62
66
|
rows="2"
|
|
63
|
-
placeholder="
|
|
67
|
+
:placeholder="msg.createDialog.placeholderDescription"
|
|
64
68
|
v-model="form.description"
|
|
65
69
|
/>
|
|
66
70
|
</div>
|
|
67
71
|
|
|
68
72
|
<div class="pcd-field">
|
|
69
|
-
<div class="pcd-field-label">
|
|
73
|
+
<div class="pcd-field-label">{{ msg.createDialog.labelBasis }}</div>
|
|
70
74
|
<div class="pcd-choice-grid">
|
|
71
75
|
<button
|
|
72
76
|
v-for="opt in choiceOptions"
|
|
@@ -87,7 +91,7 @@
|
|
|
87
91
|
|
|
88
92
|
<div class="pcd-foot">
|
|
89
93
|
<button class="pcd-btn pcd-btn--ghost" type="button" @click="onCancel">
|
|
90
|
-
|
|
94
|
+
{{ common.cancel }}
|
|
91
95
|
</button>
|
|
92
96
|
<button
|
|
93
97
|
class="pcd-btn pcd-btn--primary"
|
|
@@ -95,7 +99,9 @@
|
|
|
95
99
|
:disabled="!canSubmit || submitting"
|
|
96
100
|
@click="onSubmit"
|
|
97
101
|
>
|
|
98
|
-
<span>{{
|
|
102
|
+
<span>{{
|
|
103
|
+
submitting ? msg.createDialog.submitting : msg.createDialog.submit
|
|
104
|
+
}}</span>
|
|
99
105
|
<span class="pcd-ico" aria-hidden="true">
|
|
100
106
|
<svg
|
|
101
107
|
width="14"
|
|
@@ -116,6 +122,8 @@
|
|
|
116
122
|
|
|
117
123
|
<script setup lang="ts">
|
|
118
124
|
import { computed, reactive, ref, watch } from 'vue';
|
|
125
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
126
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
119
127
|
|
|
120
128
|
// PlanCreateDialog — step 1 of the "new plan" flow from the plan
|
|
121
129
|
// simulation. Collects the plan master data (key, label, description) plus the
|
|
@@ -168,13 +176,16 @@ const emit = defineEmits<{
|
|
|
168
176
|
(e: 'cancel'): void;
|
|
169
177
|
}>();
|
|
170
178
|
|
|
171
|
-
const
|
|
179
|
+
const msg = useSaMessages('plans');
|
|
180
|
+
const common = useSaMessages('common');
|
|
181
|
+
|
|
182
|
+
const emptyBasis = computed<TemplateOption>(() => ({
|
|
172
183
|
key: 'empty',
|
|
173
|
-
label:
|
|
184
|
+
label: msg.value.createDialog.emptyPlan,
|
|
174
185
|
features: [],
|
|
175
186
|
quotas: {},
|
|
176
187
|
bundles: [],
|
|
177
|
-
};
|
|
188
|
+
}));
|
|
178
189
|
|
|
179
190
|
const form = reactive({
|
|
180
191
|
planKey: '',
|
|
@@ -206,10 +217,10 @@ function onPlanKeyInput(e: Event): void {
|
|
|
206
217
|
const keyError = computed<string | null>(() => {
|
|
207
218
|
if (!form.planKey) return null;
|
|
208
219
|
if (!/^[A-Z][A-Z0-9_]*$/.test(form.planKey)) {
|
|
209
|
-
return
|
|
220
|
+
return msg.value.createDialog.errorKeyFormat;
|
|
210
221
|
}
|
|
211
222
|
if (props.existingPlanKeys.includes(form.planKey)) {
|
|
212
|
-
return
|
|
223
|
+
return formatMessage(msg.value.createDialog.errorKeyExists, { key: form.planKey });
|
|
213
224
|
}
|
|
214
225
|
return null;
|
|
215
226
|
});
|
|
@@ -222,12 +233,12 @@ const choiceOptions = computed(() => {
|
|
|
222
233
|
const options = [
|
|
223
234
|
{
|
|
224
235
|
key: 'empty',
|
|
225
|
-
title:
|
|
226
|
-
subtitle:
|
|
236
|
+
title: msg.value.createDialog.emptyPlan,
|
|
237
|
+
subtitle: msg.value.createDialog.emptyPlanHint,
|
|
227
238
|
},
|
|
228
239
|
...props.availableTemplates.map((t) => ({
|
|
229
240
|
key: t.key,
|
|
230
|
-
title:
|
|
241
|
+
title: formatMessage(msg.value.createDialog.cloneOf, { label: t.label }),
|
|
231
242
|
subtitle: countsLabel(t),
|
|
232
243
|
})),
|
|
233
244
|
];
|
|
@@ -235,17 +246,16 @@ const choiceOptions = computed(() => {
|
|
|
235
246
|
});
|
|
236
247
|
|
|
237
248
|
function countsLabel(t: TemplateOption): string {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
return parts.join(' · ');
|
|
249
|
+
return formatMessage(msg.value.createDialog.counts, {
|
|
250
|
+
features: t.features.length,
|
|
251
|
+
quotas: Object.keys(t.quotas).length,
|
|
252
|
+
bundles: t.bundles.length,
|
|
253
|
+
});
|
|
244
254
|
}
|
|
245
255
|
|
|
246
256
|
function selectedTemplate(): TemplateOption {
|
|
247
|
-
if (form.basis === 'empty') return
|
|
248
|
-
return props.availableTemplates.find((t) => t.key === form.basis) ??
|
|
257
|
+
if (form.basis === 'empty') return emptyBasis.value;
|
|
258
|
+
return props.availableTemplates.find((t) => t.key === form.basis) ?? emptyBasis.value;
|
|
249
259
|
}
|
|
250
260
|
|
|
251
261
|
function onSubmit(): void {
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
<section class="pd-panel pd-audit">
|
|
3
3
|
<div class="pd-panel-head">
|
|
4
4
|
<div style="min-width: 0">
|
|
5
|
-
<h3 class="pd-panel-title">
|
|
6
|
-
<div class="pd-panel-sub">
|
|
5
|
+
<h3 class="pd-panel-title">{{ msg.auditLog.title }}</h3>
|
|
6
|
+
<div class="pd-panel-sub">{{ msg.auditLog.subtitle }}</div>
|
|
7
7
|
</div>
|
|
8
8
|
</div>
|
|
9
9
|
<div class="pd-audit-body">
|
|
10
|
-
<div v-if="loadingAudit" class="pd-diff-empty">
|
|
10
|
+
<div v-if="loadingAudit" class="pd-diff-empty">{{ msg.auditLog.loading }}</div>
|
|
11
11
|
<div v-for="a in auditRows" :key="a.id" class="pd-audit-row">
|
|
12
12
|
<span :class="['pd-audit-dot', `pd-audit-${auditKind(a.action)}`]" />
|
|
13
13
|
<span class="pd-audit-when">{{ formatAuditDate(a.createdAt) }}</span>
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
</template>
|
|
21
21
|
|
|
22
22
|
<script setup lang="ts">
|
|
23
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
24
|
+
import { useSaMessages, useSuperAdminI18n } from '../../vue/use-super-admin-i18n.js';
|
|
23
25
|
import type { AuditRow } from './types.js';
|
|
24
26
|
|
|
25
27
|
defineProps<{
|
|
@@ -27,6 +29,9 @@ defineProps<{
|
|
|
27
29
|
loadingAudit: boolean;
|
|
28
30
|
}>();
|
|
29
31
|
|
|
32
|
+
const msg = useSaMessages('planDetail');
|
|
33
|
+
const { intlLocale } = useSuperAdminI18n();
|
|
34
|
+
|
|
30
35
|
function auditKind(action: string): string {
|
|
31
36
|
const a = action.toLowerCase();
|
|
32
37
|
if (a.includes('publish')) return 'publish';
|
|
@@ -51,9 +56,9 @@ function formatAuditDate(iso: string): string {
|
|
|
51
56
|
const d = new Date(iso);
|
|
52
57
|
if (Number.isNaN(d.getTime())) return iso;
|
|
53
58
|
const isToday = d.toDateString() === new Date().toDateString();
|
|
54
|
-
const time = d.toLocaleTimeString(
|
|
59
|
+
const time = d.toLocaleTimeString(intlLocale.value, { hour: '2-digit', minute: '2-digit' });
|
|
55
60
|
return isToday
|
|
56
|
-
?
|
|
57
|
-
: `${d.toLocaleDateString(
|
|
61
|
+
? formatMessage(msg.value.auditLog.today, { time })
|
|
62
|
+
: `${d.toLocaleDateString(intlLocale.value, { day: '2-digit', month: 'short' })} · ${time}`;
|
|
58
63
|
}
|
|
59
64
|
</script>
|
|
@@ -77,11 +77,9 @@
|
|
|
77
77
|
|
|
78
78
|
<script setup lang="ts">
|
|
79
79
|
import { computed, ref, watch } from 'vue';
|
|
80
|
-
import {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
type PlanVersionRow,
|
|
84
|
-
} from '@saasicat/types';
|
|
80
|
+
import { isVersionEditable, type PlanRow, type PlanVersionRow } from '@saasicat/types';
|
|
81
|
+
import { formatCurrency } from '../../client/i18n/currency.js';
|
|
82
|
+
import { useSaMessages, useSuperAdminI18n } from '../../vue/use-super-admin-i18n.js';
|
|
85
83
|
import PlanAuditLog from './PlanAuditLog.vue';
|
|
86
84
|
import PlanDetailHeader from './PlanDetailHeader.vue';
|
|
87
85
|
import PlanDetailKpis from './PlanDetailKpis.vue';
|
|
@@ -143,6 +141,9 @@ const emit = defineEmits<{
|
|
|
143
141
|
(e: 'terminate', versionId: string, endsAt: string): void;
|
|
144
142
|
}>();
|
|
145
143
|
|
|
144
|
+
const msg = useSaMessages('planDetail');
|
|
145
|
+
const { locale, intlLocale } = useSuperAdminI18n();
|
|
146
|
+
|
|
146
147
|
// ── Status / Selection ──────────────────────────────────────────────
|
|
147
148
|
function statusOf(v: PlanVersionRow): PlanVersionStatus {
|
|
148
149
|
if (v.publishedAt === null) return 'draft';
|
|
@@ -201,7 +202,7 @@ const timelineTicks = computed(() => {
|
|
|
201
202
|
for (const v of chronological.value) {
|
|
202
203
|
if (v.validFrom) ticks.push(v.validFrom.slice(0, 7));
|
|
203
204
|
}
|
|
204
|
-
ticks.push(
|
|
205
|
+
ticks.push(msg.value.versions.timelineNow);
|
|
205
206
|
return ticks;
|
|
206
207
|
});
|
|
207
208
|
|
|
@@ -209,9 +210,7 @@ const timelineTicks = computed(() => {
|
|
|
209
210
|
function formatMoney(raw: string | number): string {
|
|
210
211
|
const num = typeof raw === 'string' ? Number(raw) : raw;
|
|
211
212
|
if (!Number.isFinite(num)) return String(raw);
|
|
212
|
-
|
|
213
|
-
if (Number.isInteger(num)) return `${num} €`;
|
|
214
|
-
return `${num.toFixed(2).replace('.', ',')} €`;
|
|
213
|
+
return formatCurrency(num, locale.value);
|
|
215
214
|
}
|
|
216
215
|
function quotasOf(v: PlanVersionRow): Record<string, number> {
|
|
217
216
|
if (v.quotas && Object.keys(v.quotas).length > 0) return v.quotas;
|
|
@@ -290,7 +289,7 @@ const diffRows = computed<DiffRow[]>(() => {
|
|
|
290
289
|
id: 'af-' + f,
|
|
291
290
|
kind: 'add',
|
|
292
291
|
sign: '+',
|
|
293
|
-
tag:
|
|
292
|
+
tag: msg.value.diff.tagNew,
|
|
294
293
|
label: featureLabel(f),
|
|
295
294
|
key: f,
|
|
296
295
|
});
|
|
@@ -299,7 +298,7 @@ const diffRows = computed<DiffRow[]>(() => {
|
|
|
299
298
|
id: 'rf-' + f,
|
|
300
299
|
kind: 'rm',
|
|
301
300
|
sign: '−',
|
|
302
|
-
tag:
|
|
301
|
+
tag: msg.value.diff.tagRemoved,
|
|
303
302
|
label: featureLabel(f),
|
|
304
303
|
key: f,
|
|
305
304
|
});
|
|
@@ -309,7 +308,7 @@ const diffRows = computed<DiffRow[]>(() => {
|
|
|
309
308
|
id: 'cq-' + q.key,
|
|
310
309
|
kind: 'mod',
|
|
311
310
|
sign: '~',
|
|
312
|
-
tag:
|
|
311
|
+
tag: msg.value.diff.tagChanged,
|
|
313
312
|
label: quotaLabel(q.key),
|
|
314
313
|
key: q.key,
|
|
315
314
|
from: `${q.from} ${u}`.trim(),
|
|
@@ -322,7 +321,7 @@ const diffRows = computed<DiffRow[]>(() => {
|
|
|
322
321
|
id: 'aq-' + q.key,
|
|
323
322
|
kind: 'add',
|
|
324
323
|
sign: '+',
|
|
325
|
-
tag:
|
|
324
|
+
tag: msg.value.diff.tagNew,
|
|
326
325
|
label: quotaLabel(q.key),
|
|
327
326
|
key: q.key,
|
|
328
327
|
from: '—',
|
|
@@ -335,7 +334,7 @@ const diffRows = computed<DiffRow[]>(() => {
|
|
|
335
334
|
id: 'rq-' + q.key,
|
|
336
335
|
kind: 'rm',
|
|
337
336
|
sign: '−',
|
|
338
|
-
tag:
|
|
337
|
+
tag: msg.value.diff.tagRemoved,
|
|
339
338
|
label: quotaLabel(q.key),
|
|
340
339
|
key: q.key,
|
|
341
340
|
from: `${q.value} ${u}`.trim(),
|
|
@@ -347,8 +346,8 @@ const diffRows = computed<DiffRow[]>(() => {
|
|
|
347
346
|
id: 'price',
|
|
348
347
|
kind: 'mod',
|
|
349
348
|
sign: '~',
|
|
350
|
-
tag:
|
|
351
|
-
label:
|
|
349
|
+
tag: msg.value.diff.tagChanged,
|
|
350
|
+
label: msg.value.diff.priceLabel,
|
|
352
351
|
key: 'pricing',
|
|
353
352
|
from: `${formatMoney(predecessor.value.monthlyNet)} / ${formatMoney(predecessor.value.yearlyNet)}`,
|
|
354
353
|
to: `${formatMoney(selectedVersion.value.monthlyNet)} / ${formatMoney(selectedVersion.value.yearlyNet)}`,
|
|
@@ -375,7 +374,7 @@ function formatDate(iso: string | null | undefined): string {
|
|
|
375
374
|
if (!iso) return '—';
|
|
376
375
|
const d = new Date(iso);
|
|
377
376
|
if (Number.isNaN(d.getTime())) return iso;
|
|
378
|
-
return d.toLocaleDateString(
|
|
377
|
+
return d.toLocaleDateString(intlLocale.value, {
|
|
379
378
|
day: '2-digit',
|
|
380
379
|
month: '2-digit',
|
|
381
380
|
year: 'numeric',
|
|
@@ -401,17 +400,16 @@ async function executeTerminate(): Promise<void> {
|
|
|
401
400
|
const e = err as { status?: number; body?: { code?: string; message?: string } };
|
|
402
401
|
const code = e?.body?.code;
|
|
403
402
|
if (code === 'PLAN_TERMINATE_DATE_NOT_FUTURE') {
|
|
404
|
-
terminateError.value =
|
|
403
|
+
terminateError.value = msg.value.terminateDialog.errorDateNotFuture;
|
|
405
404
|
} else if (code === 'PLAN_VERSION_NOT_LIVE') {
|
|
406
|
-
terminateError.value =
|
|
407
|
-
'Diese Version ist nicht live (draft oder superseded) und kann nicht terminiert werden.';
|
|
405
|
+
terminateError.value = msg.value.terminateDialog.errorVersionNotLive;
|
|
408
406
|
} else if (code === 'PLAN_TERMINATE_NOT_IMPLEMENTED') {
|
|
409
|
-
terminateError.value =
|
|
410
|
-
'Backend unterstützt Terminate noch nicht — bitte API-Server neu bauen.';
|
|
407
|
+
terminateError.value = msg.value.terminateDialog.errorNotImplemented;
|
|
411
408
|
} else if (e?.body?.message) {
|
|
412
409
|
terminateError.value = e.body.message;
|
|
413
410
|
} else {
|
|
414
|
-
terminateError.value =
|
|
411
|
+
terminateError.value =
|
|
412
|
+
err instanceof Error ? err.message : msg.value.terminateDialog.errorFailed;
|
|
415
413
|
}
|
|
416
414
|
} finally {
|
|
417
415
|
terminating.value = false;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
@keydown.escape="cancelName"
|
|
14
14
|
@blur="commitName"
|
|
15
15
|
/>
|
|
16
|
-
<span class="pd-title-hint">
|
|
16
|
+
<span class="pd-title-hint">{{ msg.header.nameEditHint }}</span>
|
|
17
17
|
</template>
|
|
18
18
|
<template v-else>
|
|
19
19
|
<h1 class="pd-title">{{ plan.label }}</h1>
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
v-if="draftVersion"
|
|
22
22
|
class="pd-title-edit-btn"
|
|
23
23
|
type="button"
|
|
24
|
-
title="
|
|
25
|
-
aria-label="
|
|
24
|
+
:title="msg.header.editNameTitle"
|
|
25
|
+
:aria-label="msg.header.editNameAria"
|
|
26
26
|
@click="startEditName"
|
|
27
27
|
>
|
|
28
28
|
<svg
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
</button>
|
|
41
41
|
</template>
|
|
42
42
|
</div>
|
|
43
|
-
<div class="pd-desc">{{ plan.description ||
|
|
43
|
+
<div class="pd-desc">{{ plan.description || msg.header.noDescription }}</div>
|
|
44
44
|
</div>
|
|
45
45
|
</div>
|
|
46
46
|
<div class="pd-actions">
|
|
@@ -57,13 +57,13 @@
|
|
|
57
57
|
<path d="M5 12h14M13 5l7 7-7 7" />
|
|
58
58
|
</svg>
|
|
59
59
|
</span>
|
|
60
|
-
<span>
|
|
60
|
+
<span>{{ common.back }}</span>
|
|
61
61
|
</button>
|
|
62
62
|
<button
|
|
63
63
|
v-if="tenantTotal === 0 && !draftVersion && publishedCount === 0"
|
|
64
64
|
class="btn danger"
|
|
65
65
|
type="button"
|
|
66
|
-
title="
|
|
66
|
+
:title="msg.header.deletePlanTitle"
|
|
67
67
|
@click="$emit('deletePlan')"
|
|
68
68
|
>
|
|
69
69
|
<svg
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"
|
|
79
79
|
/>
|
|
80
80
|
</svg>
|
|
81
|
-
<span>
|
|
81
|
+
<span>{{ msg.header.deletePlan }}</span>
|
|
82
82
|
</button>
|
|
83
83
|
<button class="btn" type="button" @click="$emit('clonePlan')">
|
|
84
84
|
<svg
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
<rect x="9" y="9" width="11" height="11" rx="2" />
|
|
93
93
|
<path d="M5 15V5a2 2 0 012-2h10" />
|
|
94
94
|
</svg>
|
|
95
|
-
<span>
|
|
95
|
+
<span>{{ msg.header.clonePlan }}</span>
|
|
96
96
|
</button>
|
|
97
97
|
<button
|
|
98
98
|
v-if="!draftVersion"
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
>
|
|
111
111
|
<path d="M12 5v14M5 12h14" />
|
|
112
112
|
</svg>
|
|
113
|
-
<span>
|
|
113
|
+
<span>{{ msg.header.newDraftVersion }}</span>
|
|
114
114
|
</button>
|
|
115
115
|
<button
|
|
116
116
|
v-else
|
|
@@ -128,15 +128,17 @@
|
|
|
128
128
|
>
|
|
129
129
|
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
|
|
130
130
|
</svg>
|
|
131
|
-
<span>
|
|
131
|
+
<span>{{ publishDraftLabel }}</span>
|
|
132
132
|
</button>
|
|
133
133
|
</div>
|
|
134
134
|
</div>
|
|
135
135
|
</template>
|
|
136
136
|
|
|
137
137
|
<script setup lang="ts">
|
|
138
|
-
import { nextTick, ref, watch } from 'vue';
|
|
138
|
+
import { computed, nextTick, ref, watch } from 'vue';
|
|
139
139
|
import type { PlanRow, PlanVersionRow } from '@saasicat/types';
|
|
140
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
141
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
140
142
|
|
|
141
143
|
const props = defineProps<{
|
|
142
144
|
plan: PlanRow;
|
|
@@ -154,6 +156,13 @@ const emit = defineEmits<{
|
|
|
154
156
|
(e: 'updatePlan', patch: { label: string }): void;
|
|
155
157
|
}>();
|
|
156
158
|
|
|
159
|
+
const msg = useSaMessages('planDetail');
|
|
160
|
+
const common = useSaMessages('common');
|
|
161
|
+
|
|
162
|
+
const publishDraftLabel = computed(() =>
|
|
163
|
+
formatMessage(msg.value.header.publishDraft, { version: props.draftVersion?.version ?? '' }),
|
|
164
|
+
);
|
|
165
|
+
|
|
157
166
|
const editingName = ref(false);
|
|
158
167
|
const nameDraft = ref(props.plan.label);
|
|
159
168
|
const nameInput = ref<HTMLInputElement | null>(null);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="pd-kpis">
|
|
3
3
|
<div class="pd-kpi">
|
|
4
|
-
<div class="pd-kpi-label">
|
|
4
|
+
<div class="pd-kpi-label">{{ msg.kpis.activeVersion }}</div>
|
|
5
5
|
<div class="pd-kpi-big">
|
|
6
6
|
<template v-if="liveVersion">
|
|
7
7
|
v{{ liveVersion.version }}
|
|
@@ -9,49 +9,62 @@
|
|
|
9
9
|
</template>
|
|
10
10
|
<template v-else>—</template>
|
|
11
11
|
</div>
|
|
12
|
-
<div class="pd-kpi-sub">
|
|
13
|
-
{{
|
|
14
|
-
liveVersion?.validFrom
|
|
15
|
-
? `seit ${liveVersion.validFrom.slice(0, 10)}`
|
|
16
|
-
: 'keine veröffentlichte Version'
|
|
17
|
-
}}
|
|
18
|
-
</div>
|
|
12
|
+
<div class="pd-kpi-sub">{{ activeVersionSub }}</div>
|
|
19
13
|
</div>
|
|
20
14
|
|
|
21
15
|
<div class="pd-kpi">
|
|
22
|
-
<div class="pd-kpi-label">
|
|
16
|
+
<div class="pd-kpi-label">{{ msg.kpis.tenantImpact }}</div>
|
|
23
17
|
<div class="pd-kpi-big">{{ tenantTotal }}</div>
|
|
24
|
-
<div class="pd-kpi-sub">
|
|
18
|
+
<div class="pd-kpi-sub">{{ msg.kpis.tenantsOnPlan }}</div>
|
|
25
19
|
</div>
|
|
26
20
|
|
|
27
21
|
<div class="pd-kpi">
|
|
28
|
-
<div class="pd-kpi-label">
|
|
22
|
+
<div class="pd-kpi-label">{{ msg.kpis.versions }}</div>
|
|
29
23
|
<div class="pd-kpi-big">{{ versionCount }}</div>
|
|
30
|
-
<div class="pd-kpi-sub">
|
|
31
|
-
{{ publishedCount }} live/superseded{{ draftVersion ? ' · 1 Draft' : '' }}
|
|
32
|
-
</div>
|
|
24
|
+
<div class="pd-kpi-sub">{{ versionsSummary }}</div>
|
|
33
25
|
</div>
|
|
34
26
|
|
|
35
27
|
<div :class="['pd-kpi', draftVersion ? 'draft' : '']">
|
|
36
|
-
<div class="pd-kpi-label">
|
|
28
|
+
<div class="pd-kpi-label">{{ msg.kpis.openDraft }}</div>
|
|
37
29
|
<div class="pd-kpi-big" :style="draftVersion ? '' : 'color:#cbd5e1'">
|
|
38
30
|
{{ draftVersion ? `v${draftVersion.version}` : '—' }}
|
|
39
31
|
</div>
|
|
40
32
|
<div class="pd-kpi-sub">
|
|
41
|
-
{{ draftVersion ?
|
|
33
|
+
{{ draftVersion ? msg.kpis.readyToEdit : msg.kpis.noOpenDraft }}
|
|
42
34
|
</div>
|
|
43
35
|
</div>
|
|
44
36
|
</div>
|
|
45
37
|
</template>
|
|
46
38
|
|
|
47
39
|
<script setup lang="ts">
|
|
40
|
+
import { computed } from 'vue';
|
|
48
41
|
import type { PlanVersionRow } from '@saasicat/types';
|
|
42
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
43
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
49
44
|
|
|
50
|
-
defineProps<{
|
|
45
|
+
const props = defineProps<{
|
|
51
46
|
liveVersion: PlanVersionRow | null;
|
|
52
47
|
draftVersion: PlanVersionRow | null;
|
|
53
48
|
tenantTotal: number;
|
|
54
49
|
versionCount: number;
|
|
55
50
|
publishedCount: number;
|
|
56
51
|
}>();
|
|
52
|
+
|
|
53
|
+
const msg = useSaMessages('planDetail');
|
|
54
|
+
|
|
55
|
+
const activeVersionSub = computed(() => {
|
|
56
|
+
const validFrom = props.liveVersion?.validFrom;
|
|
57
|
+
return validFrom
|
|
58
|
+
? formatMessage(msg.value.kpis.activeSince, { date: validFrom.slice(0, 10) })
|
|
59
|
+
: msg.value.kpis.noPublishedVersion;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const versionsSummary = computed(() =>
|
|
63
|
+
props.draftVersion
|
|
64
|
+
? formatMessage(msg.value.kpis.versionsSummaryWithDrafts, {
|
|
65
|
+
published: props.publishedCount,
|
|
66
|
+
drafts: 1,
|
|
67
|
+
})
|
|
68
|
+
: formatMessage(msg.value.kpis.versionsSummary, { published: props.publishedCount }),
|
|
69
|
+
);
|
|
57
70
|
</script>
|
|
@@ -7,17 +7,20 @@
|
|
|
7
7
|
<q-card style="min-width: 420px">
|
|
8
8
|
<q-card-section>
|
|
9
9
|
<div class="text-h6">
|
|
10
|
-
{{
|
|
10
|
+
{{
|
|
11
|
+
target?.endsAt
|
|
12
|
+
? msg.terminateDialog.titleChangeEndDate
|
|
13
|
+
: msg.terminateDialog.titleTerminate
|
|
14
|
+
}}
|
|
11
15
|
</div>
|
|
12
16
|
<div class="text-body2 q-mt-sm">
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
ab diesem Datum nicht mehr möglich.
|
|
17
|
+
{{ msg.terminateDialog.bodyPlanPrefix }} <code>{{ plan.planKey }}</code>
|
|
18
|
+
{{ bodyExpiry }} <b>{{ msg.terminateDialog.bodyWithout }}</b>
|
|
19
|
+
{{ msg.terminateDialog.bodySuffix }}
|
|
17
20
|
</div>
|
|
18
21
|
<q-input
|
|
19
22
|
:model-value="dateInput"
|
|
20
|
-
label="
|
|
23
|
+
:label="msg.terminateDialog.dateLabel"
|
|
21
24
|
type="text"
|
|
22
25
|
class="q-mt-md"
|
|
23
26
|
mask="####-##-##"
|
|
@@ -43,10 +46,10 @@
|
|
|
43
46
|
{{ error }}
|
|
44
47
|
</q-banner>
|
|
45
48
|
<q-card-actions align="right">
|
|
46
|
-
<q-btn flat label="
|
|
49
|
+
<q-btn flat :label="common.cancel" @click="$emit('update:modelValue', false)" />
|
|
47
50
|
<q-btn
|
|
48
51
|
color="primary"
|
|
49
|
-
label="
|
|
52
|
+
:label="msg.terminateDialog.submit"
|
|
50
53
|
:loading="terminating"
|
|
51
54
|
:disable="!dateInput"
|
|
52
55
|
@click="$emit('execute')"
|
|
@@ -57,9 +60,12 @@
|
|
|
57
60
|
</template>
|
|
58
61
|
|
|
59
62
|
<script setup lang="ts">
|
|
63
|
+
import { computed } from 'vue';
|
|
60
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';
|
|
61
67
|
|
|
62
|
-
defineProps<{
|
|
68
|
+
const props = defineProps<{
|
|
63
69
|
modelValue: boolean;
|
|
64
70
|
plan: PlanRow;
|
|
65
71
|
target: PlanVersionRow | null;
|
|
@@ -68,6 +74,13 @@ defineProps<{
|
|
|
68
74
|
terminating: boolean;
|
|
69
75
|
}>();
|
|
70
76
|
|
|
77
|
+
const msg = useSaMessages('planDetail');
|
|
78
|
+
const common = useSaMessages('common');
|
|
79
|
+
|
|
80
|
+
const bodyExpiry = computed(() =>
|
|
81
|
+
formatMessage(msg.value.terminateDialog.bodyExpiry, { version: props.target?.version ?? '' }),
|
|
82
|
+
);
|
|
83
|
+
|
|
71
84
|
defineEmits<{
|
|
72
85
|
(e: 'update:modelValue', value: boolean): void;
|
|
73
86
|
(e: 'update:dateInput', value: string): void;
|