@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
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="sa-discovery__kpis">
|
|
3
3
|
<div class="sa-discovery__kpi">
|
|
4
|
-
<div class="sa-discovery__kpi-label">
|
|
4
|
+
<div class="sa-discovery__kpi-label">{{ msg.kpi.featuresTotal }}</div>
|
|
5
5
|
<div class="sa-discovery__kpi-value">{{ featuresCount }}</div>
|
|
6
|
-
<div class="sa-discovery__kpi-sub">
|
|
7
|
-
aggregiert aus {{ capabilitiesCount }} Capabilities
|
|
8
|
-
</div>
|
|
6
|
+
<div class="sa-discovery__kpi-sub">{{ featuresTotalSub }}</div>
|
|
9
7
|
</div>
|
|
10
8
|
<div class="sa-discovery__kpi good">
|
|
11
|
-
<div class="sa-discovery__kpi-label">
|
|
9
|
+
<div class="sa-discovery__kpi-label">{{ msg.kpi.approved }}</div>
|
|
12
10
|
<div class="sa-discovery__kpi-value">{{ approvedCount }}</div>
|
|
13
|
-
<div class="sa-discovery__kpi-sub">
|
|
11
|
+
<div class="sa-discovery__kpi-sub">{{ msg.kpi.approvedSub }}</div>
|
|
14
12
|
</div>
|
|
15
13
|
<div class="sa-discovery__kpi" :class="{ warn: pendingCount > 0 }">
|
|
16
|
-
<div class="sa-discovery__kpi-label">
|
|
14
|
+
<div class="sa-discovery__kpi-label">{{ msg.kpi.pending }}</div>
|
|
17
15
|
<div class="sa-discovery__kpi-value">{{ pendingCount }}</div>
|
|
18
|
-
<div class="sa-discovery__kpi-sub">
|
|
16
|
+
<div class="sa-discovery__kpi-sub">{{ msg.kpi.pendingSub }}</div>
|
|
19
17
|
</div>
|
|
20
18
|
<div class="sa-discovery__kpi">
|
|
21
|
-
<div class="sa-discovery__kpi-label">
|
|
19
|
+
<div class="sa-discovery__kpi-label">{{ msg.kpi.outdated }}</div>
|
|
22
20
|
<div class="sa-discovery__kpi-value">{{ outdatedCount + obsoleteCount }}</div>
|
|
23
|
-
<div class="sa-discovery__kpi-sub">
|
|
24
|
-
{{ outdatedCount }} outdated · {{ obsoleteCount }} obsolete
|
|
25
|
-
</div>
|
|
21
|
+
<div class="sa-discovery__kpi-sub">{{ outdatedSub }}</div>
|
|
26
22
|
</div>
|
|
27
23
|
<div class="sa-discovery__kpi" :class="{ bad: orphanCount > 0 }">
|
|
28
|
-
<div class="sa-discovery__kpi-label">
|
|
24
|
+
<div class="sa-discovery__kpi-label">{{ msg.kpi.orphans }}</div>
|
|
29
25
|
<div class="sa-discovery__kpi-value">{{ orphanCount }}</div>
|
|
30
|
-
<div class="sa-discovery__kpi-sub">
|
|
26
|
+
<div class="sa-discovery__kpi-sub">{{ msg.orphansTitle }}</div>
|
|
31
27
|
</div>
|
|
32
28
|
</div>
|
|
33
29
|
</template>
|
|
34
30
|
|
|
35
31
|
<script setup lang="ts">
|
|
32
|
+
import { computed } from 'vue';
|
|
33
|
+
|
|
34
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
35
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
36
|
+
|
|
36
37
|
// Feature-centric KPI bar (#20 Slice 1, sim layout): the approval
|
|
37
38
|
// counts refer to features, no longer to capability reviews.
|
|
38
|
-
defineProps<{
|
|
39
|
+
const props = defineProps<{
|
|
39
40
|
featuresCount: number;
|
|
40
41
|
capabilitiesCount: number;
|
|
41
42
|
approvedCount: number;
|
|
@@ -44,4 +45,16 @@ defineProps<{
|
|
|
44
45
|
obsoleteCount: number;
|
|
45
46
|
orphanCount: number;
|
|
46
47
|
}>();
|
|
48
|
+
|
|
49
|
+
const msg = useSaMessages('discovery');
|
|
50
|
+
|
|
51
|
+
const featuresTotalSub = computed(() =>
|
|
52
|
+
formatMessage(msg.value.kpi.featuresTotalSub, { count: props.capabilitiesCount }),
|
|
53
|
+
);
|
|
54
|
+
const outdatedSub = computed(() =>
|
|
55
|
+
formatMessage(msg.value.kpi.outdatedSub, {
|
|
56
|
+
outdated: props.outdatedCount,
|
|
57
|
+
obsolete: props.obsoleteCount,
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
47
60
|
</script>
|
|
@@ -15,26 +15,25 @@
|
|
|
15
15
|
<span
|
|
16
16
|
v-if="quota.successorKey"
|
|
17
17
|
class="sa-qc__flag sa-qc__flag--succ"
|
|
18
|
-
:title="
|
|
18
|
+
:title="replacedByLabel"
|
|
19
19
|
>
|
|
20
|
-
|
|
20
|
+
{{ replacedByLabel }}
|
|
21
21
|
</span>
|
|
22
22
|
<span
|
|
23
23
|
v-if="quota.replaces.length"
|
|
24
24
|
class="sa-qc__flag sa-qc__flag--repl"
|
|
25
|
-
:title="
|
|
25
|
+
:title="replacesLabel"
|
|
26
26
|
>
|
|
27
|
-
|
|
27
|
+
{{ replacesLabel }}
|
|
28
28
|
</span>
|
|
29
29
|
</div>
|
|
30
30
|
<div class="sa-qc__sub">
|
|
31
|
-
|
|
31
|
+
{{ msg.unit }} <code>{{ quota.unit }}</code> · {{ msg.quota.usageProvider }}
|
|
32
32
|
<code v-if="quota.usageProvider">{{ quota.usageProvider }}</code>
|
|
33
|
-
<span v-else class="sa-qc__missing">
|
|
33
|
+
<span v-else class="sa-qc__missing">{{ msg.quota.usageProviderMissing }}</span>
|
|
34
34
|
</div>
|
|
35
35
|
<div v-if="!quota.usageProvider" class="sa-qc__warning">
|
|
36
|
-
|
|
37
|
-
§6.3).
|
|
36
|
+
{{ msg.quota.noUsageProviderWarning }}
|
|
38
37
|
</div>
|
|
39
38
|
</div>
|
|
40
39
|
|
|
@@ -61,8 +60,8 @@
|
|
|
61
60
|
<div v-if="quota.discoveryStatus === 'outdated'" class="sa-qc__banner">
|
|
62
61
|
<q-icon name="warning" size="16px" />
|
|
63
62
|
<span>
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
{{ msg.quota.outdatedBanner }} <b>{{ msg.reapproveEmphasis }}</b
|
|
64
|
+
>.
|
|
66
65
|
</span>
|
|
67
66
|
</div>
|
|
68
67
|
<CatalogEntryTransPanel
|
|
@@ -88,6 +87,8 @@ import type {
|
|
|
88
87
|
} from '@saasicat/types';
|
|
89
88
|
import CatalogEntryTransPanel from './CatalogEntryTransPanel.vue';
|
|
90
89
|
import DiscoveryStatusControl from './DiscoveryStatusControl.vue';
|
|
90
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
91
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
91
92
|
import {
|
|
92
93
|
coverageClass,
|
|
93
94
|
coveragePct,
|
|
@@ -114,11 +115,20 @@ const emit = defineEmits<{
|
|
|
114
115
|
'quota-locale': [key: string, locale: string, patch: CatalogEntryI18nFields];
|
|
115
116
|
}>();
|
|
116
117
|
|
|
118
|
+
const msg = useSaMessages('discovery');
|
|
119
|
+
|
|
117
120
|
// Draft buffer for the header label — the fields themselves are buffered by
|
|
118
121
|
// the CatalogEntryTransPanel; here only so the title follows along while typing.
|
|
119
122
|
const drafts = reactive<{ label?: string }>({});
|
|
120
123
|
const labelValue = computed(() => drafts.label ?? props.quota.label ?? '');
|
|
121
124
|
|
|
125
|
+
const replacedByLabel = computed(() =>
|
|
126
|
+
formatMessage(msg.value.replacedBy, { key: props.quota.successorKey ?? '' }),
|
|
127
|
+
);
|
|
128
|
+
const replacesLabel = computed(() =>
|
|
129
|
+
formatMessage(msg.value.replaces, { keys: props.quota.replaces.join(', ') }),
|
|
130
|
+
);
|
|
131
|
+
|
|
122
132
|
/** Mirror base edits from the translation panel + bubble them up. */
|
|
123
133
|
function onTransBase(patch: { label?: string; description?: string }): void {
|
|
124
134
|
if (patch.label !== undefined) drafts.label = patch.label;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="sa-stc" @click.stop>
|
|
3
3
|
<span class="sa-review" :class="`sa-review--${status}`">
|
|
4
|
-
{{
|
|
5
|
-
<q-tooltip>{{
|
|
4
|
+
{{ statusLabel(status, locale) }}
|
|
5
|
+
<q-tooltip>{{ statusHint(status, locale) }}</q-tooltip>
|
|
6
6
|
</span>
|
|
7
7
|
<q-btn
|
|
8
8
|
dense
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
:loading="busy"
|
|
16
16
|
@click="emit('set-status', primary.to)"
|
|
17
17
|
>
|
|
18
|
-
<q-tooltip>{{
|
|
18
|
+
<q-tooltip>{{ statusHint(primary.to, locale) }}</q-tooltip>
|
|
19
19
|
</q-btn>
|
|
20
20
|
<q-btn v-if="menu.length" dense flat round size="sm" icon="more_horiz">
|
|
21
21
|
<q-menu auto-close anchor="bottom right" self="top right">
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
<script setup lang="ts">
|
|
40
40
|
import { computed } from 'vue';
|
|
41
41
|
import type { DiscoveryStatus } from '@saasicat/types';
|
|
42
|
-
import {
|
|
42
|
+
import { useSuperAdminI18n } from '../../vue/use-super-admin-i18n.js';
|
|
43
|
+
import { primaryReviewAction, reviewMenuActions, statusHint, statusLabel } from './discovery-ui.js';
|
|
43
44
|
|
|
44
45
|
// StatusControl (#20, design sim): status chip + contextual primary action
|
|
45
46
|
// + kebab menu for the remaining lifecycle transitions. Shared by the feature
|
|
@@ -56,8 +57,10 @@ const emit = defineEmits<{
|
|
|
56
57
|
'set-status': [target: DiscoveryStatus];
|
|
57
58
|
}>();
|
|
58
59
|
|
|
59
|
-
const
|
|
60
|
-
|
|
60
|
+
const { locale } = useSuperAdminI18n();
|
|
61
|
+
|
|
62
|
+
const primary = computed(() => primaryReviewAction(props.status, locale.value));
|
|
63
|
+
const menu = computed(() => reviewMenuActions(props.status, locale.value));
|
|
61
64
|
</script>
|
|
62
65
|
|
|
63
66
|
<style scoped>
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
// `<script setup>` allows no named exports and the cards share the same
|
|
4
4
|
// labels/colors/coverage calculations.
|
|
5
5
|
|
|
6
|
-
import type {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from '@saasicat/types';
|
|
6
|
+
import type { CatalogEntryI18n, DiscoveryStatus } from '@saasicat/types';
|
|
7
|
+
import { DEFAULT_SA_LOCALE, type SaLocale } from '../../client/i18n/locale.js';
|
|
8
|
+
import { commonMessages } from '../../client/i18n/messages/common.js';
|
|
9
|
+
import { discoveryMessages } from '../../client/i18n/messages/discovery.js';
|
|
11
10
|
|
|
12
11
|
/** Translatable fields of a catalog entry. */
|
|
13
12
|
export type I18nField = 'label' | 'description' | 'unit';
|
|
@@ -43,30 +42,21 @@ export function localeFull(locale: string): string {
|
|
|
43
42
|
return LOCALE_NAMES[locale]?.full ?? locale;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
export function i18nFieldLabel(f: I18nField): string {
|
|
47
|
-
|
|
45
|
+
export function i18nFieldLabel(f: I18nField, locale: SaLocale = DEFAULT_SA_LOCALE): string {
|
|
46
|
+
if (f === 'label') return discoveryMessages[locale].trans.fieldLabel;
|
|
47
|
+
if (f === 'description') return commonMessages[locale].description;
|
|
48
|
+
return discoveryMessages[locale].unit;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
// ─── Approval lifecycle (#20): status display + state machine ────────────────
|
|
51
52
|
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
hint: 'Für Pläne, Bundles & Marketing freigegeben.',
|
|
60
|
-
},
|
|
61
|
-
outdated: {
|
|
62
|
-
label: 'Outdated',
|
|
63
|
-
hint: 'Code hat sich seit der Freigabe geändert — bitte erneut prüfen und freigeben.',
|
|
64
|
-
},
|
|
65
|
-
obsolete: {
|
|
66
|
-
label: 'Obsolete',
|
|
67
|
-
hint: 'Abgekündigt — nicht mehr verwenden, in neuen Plänen ausblenden.',
|
|
68
|
-
},
|
|
69
|
-
};
|
|
53
|
+
export function statusLabel(status: DiscoveryStatus, locale: SaLocale = DEFAULT_SA_LOCALE): string {
|
|
54
|
+
return discoveryMessages[locale].statusLabels[status];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function statusHint(status: DiscoveryStatus, locale: SaLocale = DEFAULT_SA_LOCALE): string {
|
|
58
|
+
return discoveryMessages[locale].statusHints[status];
|
|
59
|
+
}
|
|
70
60
|
|
|
71
61
|
export interface ReviewAction {
|
|
72
62
|
label: string;
|
|
@@ -77,53 +67,51 @@ export interface ReviewAction {
|
|
|
77
67
|
}
|
|
78
68
|
|
|
79
69
|
/** Primary action per status (design sim `StatusControl`, #20). */
|
|
80
|
-
export function primaryReviewAction(
|
|
70
|
+
export function primaryReviewAction(
|
|
71
|
+
status: DiscoveryStatus,
|
|
72
|
+
locale: SaLocale = DEFAULT_SA_LOCALE,
|
|
73
|
+
): ReviewAction {
|
|
74
|
+
const actions = discoveryMessages[locale].reviewActions;
|
|
81
75
|
switch (status) {
|
|
82
76
|
case 'pending':
|
|
83
|
-
return { label:
|
|
77
|
+
return { label: actions.approve, to: 'approved', emphasized: true };
|
|
84
78
|
case 'approved':
|
|
85
|
-
return { label:
|
|
79
|
+
return { label: actions.revoke, to: 'pending', emphasized: false };
|
|
86
80
|
case 'outdated':
|
|
87
|
-
return { label:
|
|
81
|
+
return { label: actions.reapprove, to: 'approved', emphasized: true };
|
|
88
82
|
case 'obsolete':
|
|
89
|
-
return { label:
|
|
83
|
+
return { label: actions.reactivate, to: 'pending', emphasized: false };
|
|
90
84
|
}
|
|
91
85
|
}
|
|
92
86
|
|
|
93
87
|
/** Kebab menu actions per status (design sim `StatusControl`, #20). */
|
|
94
|
-
export function reviewMenuActions(
|
|
88
|
+
export function reviewMenuActions(
|
|
89
|
+
status: DiscoveryStatus,
|
|
90
|
+
locale: SaLocale = DEFAULT_SA_LOCALE,
|
|
91
|
+
): ReviewAction[] {
|
|
92
|
+
const actions = discoveryMessages[locale].reviewActions;
|
|
93
|
+
const markObsolete: ReviewAction = {
|
|
94
|
+
label: actions.markObsolete,
|
|
95
|
+
to: 'obsolete',
|
|
96
|
+
emphasized: false,
|
|
97
|
+
danger: true,
|
|
98
|
+
};
|
|
95
99
|
switch (status) {
|
|
96
100
|
case 'pending':
|
|
97
|
-
return [
|
|
98
|
-
{ label: 'Als obsolet markieren', to: 'obsolete', emphasized: false, danger: true },
|
|
99
|
-
];
|
|
101
|
+
return [markObsolete];
|
|
100
102
|
case 'approved':
|
|
101
103
|
return [
|
|
102
|
-
{ label:
|
|
103
|
-
|
|
104
|
+
{ label: actions.markOutdated, to: 'outdated', emphasized: false },
|
|
105
|
+
markObsolete,
|
|
104
106
|
];
|
|
105
107
|
case 'outdated':
|
|
106
|
-
return [
|
|
107
|
-
{ label: 'Freigabe entziehen', to: 'pending', emphasized: false },
|
|
108
|
-
{ label: 'Als obsolet markieren', to: 'obsolete', emphasized: false, danger: true },
|
|
109
|
-
];
|
|
108
|
+
return [{ label: actions.revoke, to: 'pending', emphasized: false }, markObsolete];
|
|
110
109
|
case 'obsolete':
|
|
111
110
|
return [];
|
|
112
111
|
}
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
// ─── Capability
|
|
116
|
-
|
|
117
|
-
const CODE_STATUS_LABELS: Record<CapabilityCodeStatus, string> = {
|
|
118
|
-
active: 'Aktiv',
|
|
119
|
-
experimental: 'Experimental',
|
|
120
|
-
deprecated: 'Deprecated',
|
|
121
|
-
retired: 'Retired',
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export function codeStatusLabel(status: CapabilityCodeStatus): string {
|
|
125
|
-
return CODE_STATUS_LABELS[status] ?? status;
|
|
126
|
-
}
|
|
114
|
+
// ─── Capability kind styling (read-only code facts, #20) ─────────────────────
|
|
127
115
|
|
|
128
116
|
const KIND_COLORS: Record<string, string> = {
|
|
129
117
|
endpoint: '#2563eb',
|
|
@@ -2,21 +2,18 @@
|
|
|
2
2
|
<div class="mc-admin">
|
|
3
3
|
<div class="mc-admin-head">
|
|
4
4
|
<div style="flex: 1">
|
|
5
|
-
<div class="mc-admin-title">
|
|
6
|
-
<div class="mc-admin-sub">
|
|
7
|
-
Sichtbarkeit, Reihenfolge & Highlight-Badges pro Plan · Edits gehen direkt
|
|
8
|
-
live
|
|
9
|
-
</div>
|
|
5
|
+
<div class="mc-admin-title">{{ msg.tabs.admin }}</div>
|
|
6
|
+
<div class="mc-admin-sub">{{ msg.admin.subtitle }}</div>
|
|
10
7
|
</div>
|
|
11
8
|
</div>
|
|
12
9
|
|
|
13
10
|
<div class="mc-admin-grid">
|
|
14
11
|
<div class="mc-admin-thead">
|
|
15
|
-
<div>
|
|
16
|
-
<div>
|
|
17
|
-
<div>
|
|
18
|
-
<div>
|
|
19
|
-
<div>
|
|
12
|
+
<div>{{ msg.admin.colPlan }}</div>
|
|
13
|
+
<div>{{ msg.admin.colVisible }}</div>
|
|
14
|
+
<div>{{ msg.admin.colBadge }}</div>
|
|
15
|
+
<div>{{ msg.admin.colPriority }}</div>
|
|
16
|
+
<div>{{ msg.admin.colHighlight }}</div>
|
|
20
17
|
<div></div>
|
|
21
18
|
</div>
|
|
22
19
|
|
|
@@ -45,7 +42,9 @@
|
|
|
45
42
|
v-if="row.publishedVersions.length > 1"
|
|
46
43
|
class="mc-version-tabs"
|
|
47
44
|
role="tablist"
|
|
48
|
-
:aria-label="
|
|
45
|
+
:aria-label="
|
|
46
|
+
formatMessage(msg.admin.versionTabsLabel, { plan: row.plan.label })
|
|
47
|
+
"
|
|
49
48
|
>
|
|
50
49
|
<button
|
|
51
50
|
v-for="v in row.publishedVersions"
|
|
@@ -107,23 +106,23 @@
|
|
|
107
106
|
</div>
|
|
108
107
|
<div class="mc-admin-row-end">
|
|
109
108
|
<span v-if="!row.liveVersion" class="mc-chip mc-chip--muted">
|
|
110
|
-
|
|
109
|
+
{{ msg.admin.noLiveVersion }}
|
|
111
110
|
</span>
|
|
112
111
|
<span v-else-if="!row.m.visible" class="mc-chip mc-chip--muted">
|
|
113
|
-
|
|
112
|
+
{{ msg.admin.hidden }}
|
|
114
113
|
</span>
|
|
115
114
|
<span v-else-if="row.m.highlight" class="mc-chip mc-chip--featured">
|
|
116
|
-
|
|
115
|
+
{{ msg.admin.featured }}
|
|
117
116
|
</span>
|
|
118
|
-
<span v-else class="mc-chip mc-chip--live">live</span>
|
|
117
|
+
<span v-else class="mc-chip mc-chip--live">{{ msg.admin.live }}</span>
|
|
119
118
|
<button
|
|
120
119
|
v-if="row.liveVersion"
|
|
121
120
|
type="button"
|
|
122
121
|
class="mc-expand-btn"
|
|
123
122
|
:title="
|
|
124
123
|
expandedKey === row.plan.planKey
|
|
125
|
-
?
|
|
126
|
-
:
|
|
124
|
+
? common.close
|
|
125
|
+
: msg.admin.expandTitle
|
|
127
126
|
"
|
|
128
127
|
@click="$emit('toggle-expand', row)"
|
|
129
128
|
>
|
|
@@ -151,19 +150,27 @@
|
|
|
151
150
|
<div class="mc-expand-col">
|
|
152
151
|
<div class="mc-expand-sec">
|
|
153
152
|
<div class="mc-field-head">
|
|
154
|
-
<label class="mc-expand-label">
|
|
153
|
+
<label class="mc-expand-label">
|
|
154
|
+
{{ msg.admin.planNameLabel }}
|
|
155
|
+
</label>
|
|
155
156
|
<span
|
|
156
157
|
v-if="activeLocale !== defaultLocale"
|
|
157
158
|
class="mc-source-hint"
|
|
158
159
|
>
|
|
159
160
|
{{ defaultLocale.toUpperCase() }}:
|
|
160
|
-
<em
|
|
161
|
+
<em>
|
|
162
|
+
{{
|
|
163
|
+
formatMessage(msg.admin.planNameSourceValue, {
|
|
164
|
+
label: row.plan.label,
|
|
165
|
+
})
|
|
166
|
+
}}
|
|
167
|
+
</em>
|
|
161
168
|
</span>
|
|
162
169
|
</div>
|
|
163
170
|
<div v-if="activeLocale === defaultLocale" class="mc-locked-value">
|
|
164
171
|
<span>{{ row.plan.label }}</span>
|
|
165
172
|
<span class="mc-locked-hint">
|
|
166
|
-
|
|
173
|
+
{{ msg.admin.planNameLocked }}
|
|
167
174
|
</span>
|
|
168
175
|
</div>
|
|
169
176
|
<input
|
|
@@ -178,29 +185,31 @@
|
|
|
178
185
|
v-if="activeLocale !== defaultLocale && !row.m.displayLabel"
|
|
179
186
|
class="mc-expand-hint"
|
|
180
187
|
>
|
|
181
|
-
|
|
188
|
+
{{
|
|
189
|
+
formatMessage(msg.admin.planNameFallbackHint, {
|
|
190
|
+
label: row.plan.label,
|
|
191
|
+
})
|
|
192
|
+
}}
|
|
182
193
|
</div>
|
|
183
194
|
</div>
|
|
184
195
|
|
|
185
196
|
<div class="mc-expand-sec">
|
|
186
|
-
<label class="mc-expand-label">
|
|
197
|
+
<label class="mc-expand-label">{{ msg.admin.teaserLabel }}</label>
|
|
187
198
|
<textarea
|
|
188
199
|
class="mc-field mc-field--area"
|
|
189
200
|
rows="2"
|
|
190
|
-
placeholder="
|
|
201
|
+
:placeholder="msg.admin.teaserPlaceholder"
|
|
191
202
|
:value="row.m.description"
|
|
192
203
|
:disabled="busy"
|
|
193
204
|
@change="
|
|
194
205
|
$emit('patch', row, { description: textValue($event) })
|
|
195
206
|
"
|
|
196
207
|
/>
|
|
197
|
-
<div class="mc-expand-hint">
|
|
198
|
-
erscheint unter dem Plannamen in der Vorschau
|
|
199
|
-
</div>
|
|
208
|
+
<div class="mc-expand-hint">{{ msg.admin.teaserHint }}</div>
|
|
200
209
|
</div>
|
|
201
210
|
|
|
202
211
|
<div class="mc-expand-sec">
|
|
203
|
-
<label class="mc-expand-label">
|
|
212
|
+
<label class="mc-expand-label">{{ msg.admin.trialLabel }}</label>
|
|
204
213
|
<div class="mc-trial-row">
|
|
205
214
|
<label class="mc-toggle">
|
|
206
215
|
<input
|
|
@@ -216,7 +225,11 @@
|
|
|
216
225
|
<span />
|
|
217
226
|
</label>
|
|
218
227
|
<span class="mc-trial-label">
|
|
219
|
-
{{
|
|
228
|
+
{{
|
|
229
|
+
row.m.trialEnabled
|
|
230
|
+
? msg.admin.trialOn
|
|
231
|
+
: msg.admin.trialOff
|
|
232
|
+
}}
|
|
220
233
|
</span>
|
|
221
234
|
<span class="mc-trial-days">
|
|
222
235
|
<input
|
|
@@ -233,19 +246,18 @@
|
|
|
233
246
|
})
|
|
234
247
|
"
|
|
235
248
|
/>
|
|
236
|
-
<span class="mc-trial-unit">
|
|
249
|
+
<span class="mc-trial-unit">
|
|
250
|
+
{{ msg.admin.trialDaysUnit }}
|
|
251
|
+
</span>
|
|
237
252
|
</span>
|
|
238
253
|
</div>
|
|
239
|
-
<div class="mc-expand-hint">
|
|
240
|
-
CTA-Text generiert sich automatisch („Jetzt
|
|
241
|
-
{{ row.m.trialDays }} Tage testen“).
|
|
242
|
-
</div>
|
|
254
|
+
<div class="mc-expand-hint">{{ trialCtaHint(row) }}</div>
|
|
243
255
|
</div>
|
|
244
256
|
|
|
245
257
|
<div class="mc-expand-sec">
|
|
246
|
-
<label class="mc-expand-label"
|
|
247
|
-
|
|
248
|
-
>
|
|
258
|
+
<label class="mc-expand-label">
|
|
259
|
+
{{ msg.admin.ctaOverrideLabel }}
|
|
260
|
+
</label>
|
|
249
261
|
<input
|
|
250
262
|
class="mc-field"
|
|
251
263
|
:placeholder="autoCtaText(row)"
|
|
@@ -257,7 +269,7 @@
|
|
|
257
269
|
})
|
|
258
270
|
"
|
|
259
271
|
/>
|
|
260
|
-
<div class="mc-expand-hint">
|
|
272
|
+
<div class="mc-expand-hint">{{ msg.admin.ctaOverrideHint }}</div>
|
|
261
273
|
</div>
|
|
262
274
|
</div>
|
|
263
275
|
|
|
@@ -265,10 +277,14 @@
|
|
|
265
277
|
<div class="mc-expand-sec">
|
|
266
278
|
<div class="mc-tf-head">
|
|
267
279
|
<label class="mc-expand-label" style="margin: 0">
|
|
268
|
-
|
|
280
|
+
{{ msg.topFeatures }}
|
|
269
281
|
</label>
|
|
270
282
|
<span class="mc-expand-hint" style="margin-left: auto">
|
|
271
|
-
{{
|
|
283
|
+
{{
|
|
284
|
+
formatMessage(msg.admin.topFeatureCount, {
|
|
285
|
+
count: editFeatures.length,
|
|
286
|
+
})
|
|
287
|
+
}}
|
|
272
288
|
</span>
|
|
273
289
|
</div>
|
|
274
290
|
|
|
@@ -280,7 +296,7 @@
|
|
|
280
296
|
:placeholder="
|
|
281
297
|
f.key
|
|
282
298
|
? resolveComponentLabel(f.key)
|
|
283
|
-
:
|
|
299
|
+
: msg.admin.featureLabelPlaceholder
|
|
284
300
|
"
|
|
285
301
|
:value="f.label"
|
|
286
302
|
:disabled="busy"
|
|
@@ -291,7 +307,7 @@
|
|
|
291
307
|
/>
|
|
292
308
|
<input
|
|
293
309
|
class="mc-field mc-tf-strong"
|
|
294
|
-
placeholder="
|
|
310
|
+
:placeholder="msg.admin.featureStrongPlaceholder"
|
|
295
311
|
:value="f.strong"
|
|
296
312
|
:disabled="busy"
|
|
297
313
|
@input="
|
|
@@ -303,7 +319,7 @@
|
|
|
303
319
|
<button
|
|
304
320
|
type="button"
|
|
305
321
|
class="mc-iconbtn"
|
|
306
|
-
title="
|
|
322
|
+
:title="msg.admin.moveUp"
|
|
307
323
|
:disabled="i === 0 || busy"
|
|
308
324
|
@click="$emit('move-feature', row, i, -1)"
|
|
309
325
|
>
|
|
@@ -312,7 +328,7 @@
|
|
|
312
328
|
<button
|
|
313
329
|
type="button"
|
|
314
330
|
class="mc-iconbtn"
|
|
315
|
-
title="
|
|
331
|
+
:title="msg.admin.moveDown"
|
|
316
332
|
:disabled="i === editFeatures.length - 1 || busy"
|
|
317
333
|
@click="$emit('move-feature', row, i, 1)"
|
|
318
334
|
>
|
|
@@ -321,7 +337,7 @@
|
|
|
321
337
|
<button
|
|
322
338
|
type="button"
|
|
323
339
|
class="mc-iconbtn mc-iconbtn--danger"
|
|
324
|
-
title="
|
|
340
|
+
:title="common.remove"
|
|
325
341
|
:disabled="busy"
|
|
326
342
|
@click="$emit('remove-feature', row, i)"
|
|
327
343
|
>
|
|
@@ -339,8 +355,7 @@
|
|
|
339
355
|
</div>
|
|
340
356
|
</div>
|
|
341
357
|
<div v-if="editFeatures.length === 0" class="mc-tf-empty">
|
|
342
|
-
|
|
343
|
-
Text hinzu.
|
|
358
|
+
{{ msg.admin.topFeaturesEmpty }}
|
|
344
359
|
</div>
|
|
345
360
|
</div>
|
|
346
361
|
|
|
@@ -361,20 +376,26 @@
|
|
|
361
376
|
>
|
|
362
377
|
<path d="M12 5v14M5 12h14" />
|
|
363
378
|
</svg>
|
|
364
|
-
<span>
|
|
379
|
+
<span>{{ msg.admin.addCustomFeature }}</span>
|
|
365
380
|
</button>
|
|
366
381
|
<div
|
|
367
382
|
v-if="suggestionsFor(row).length > 0"
|
|
368
383
|
class="mc-tf-suggestions"
|
|
369
384
|
>
|
|
370
|
-
<span class="mc-expand-hint">
|
|
385
|
+
<span class="mc-expand-hint">
|
|
386
|
+
{{ msg.admin.suggestionsLabel }}
|
|
387
|
+
</span>
|
|
371
388
|
<button
|
|
372
389
|
v-for="(s, i) in suggestionsFor(row)"
|
|
373
390
|
:key="i"
|
|
374
391
|
type="button"
|
|
375
392
|
class="mc-tf-chip"
|
|
376
393
|
:disabled="busy"
|
|
377
|
-
:title="
|
|
394
|
+
:title="
|
|
395
|
+
formatMessage(msg.admin.addSuggestionTitle, {
|
|
396
|
+
label: s.label,
|
|
397
|
+
})
|
|
398
|
+
"
|
|
378
399
|
@click="$emit('add-suggestion', row, s)"
|
|
379
400
|
>
|
|
380
401
|
<svg
|
|
@@ -403,6 +424,8 @@
|
|
|
403
424
|
|
|
404
425
|
<script setup lang="ts">
|
|
405
426
|
import type { MarketingTopFeature, PlanRow, PlanVersionRow } from '@saasicat/types';
|
|
427
|
+
import { formatMessage } from '../../client/i18n/format.js';
|
|
428
|
+
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
406
429
|
import type { FeatureSuggestion, MarketingRow, ResolvedMarketing } from './types.js';
|
|
407
430
|
|
|
408
431
|
defineProps<{
|
|
@@ -434,6 +457,15 @@ defineEmits<{
|
|
|
434
457
|
(e: 'add-suggestion', row: MarketingRow, suggestion: FeatureSuggestion): void;
|
|
435
458
|
}>();
|
|
436
459
|
|
|
460
|
+
const msg = useSaMessages('marketing');
|
|
461
|
+
const common = useSaMessages('common');
|
|
462
|
+
|
|
463
|
+
/** Hint below the trial toggle — shows the auto CTA text for the set trial days. */
|
|
464
|
+
function trialCtaHint(row: MarketingRow): string {
|
|
465
|
+
const cta = formatMessage(msg.value.cta.trial, { days: row.m.trialDays });
|
|
466
|
+
return formatMessage(msg.value.admin.ctaAutoHint, { cta });
|
|
467
|
+
}
|
|
468
|
+
|
|
437
469
|
function textValue(event: Event): string {
|
|
438
470
|
return (event.target as HTMLInputElement | HTMLTextAreaElement | null)?.value ?? '';
|
|
439
471
|
}
|