@saasicat/ui-vue 0.5.0 → 0.7.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
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
flat
|
|
7
7
|
dense
|
|
8
8
|
icon="arrow_back"
|
|
9
|
-
:label="
|
|
9
|
+
:label="labels.back"
|
|
10
10
|
:to="backRoute"
|
|
11
11
|
class="sa-tenant-detail__back"
|
|
12
12
|
/>
|
|
13
|
-
<h1 class="sa-page-head__title">{{ data?.name ??
|
|
13
|
+
<h1 class="sa-page-head__title">{{ data?.name ?? labels.title }}</h1>
|
|
14
14
|
<p v-if="data" class="sa-page-head__sub">
|
|
15
|
-
{{
|
|
15
|
+
{{ labels.slug }}: <code>{{ data.slug }}</code>
|
|
16
16
|
</p>
|
|
17
17
|
</div>
|
|
18
18
|
<slot name="header-actions" :data="data" :reload="load" />
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
<div class="sa-tenant-detail__body">
|
|
22
22
|
<div v-if="loading" class="sa-tenant-detail__state">
|
|
23
|
-
<q-spinner size="32px" />
|
|
23
|
+
<q-spinner size="32px" /> {{ common.loading }}
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
26
|
<template v-else-if="data">
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
<div class="sa-card q-mb-md">
|
|
29
29
|
<header class="sa-tenant-detail__card-head">
|
|
30
30
|
<div>
|
|
31
|
-
<h2 class="sa-card__title">{{
|
|
31
|
+
<h2 class="sa-card__title">{{ labels.masterData }}</h2>
|
|
32
32
|
<p v-if="stammdatenSub" class="sa-tenant-detail__card-sub">
|
|
33
33
|
{{ stammdatenSub }}
|
|
34
34
|
</p>
|
|
@@ -49,24 +49,24 @@
|
|
|
49
49
|
</header>
|
|
50
50
|
<slot name="stammdaten" :data="data">
|
|
51
51
|
<div class="sa-tenant-detail__grid">
|
|
52
|
-
<KvBlock :label="
|
|
52
|
+
<KvBlock :label="labels.plan" :value="data.subscription?.plan ?? '—'" />
|
|
53
53
|
<KvBlock
|
|
54
|
-
:label="
|
|
54
|
+
:label="labels.status"
|
|
55
55
|
:value="data.subscription?.status ?? '—'"
|
|
56
56
|
/>
|
|
57
57
|
<KvBlock
|
|
58
|
-
:label="
|
|
59
|
-
:value="data.subscription?.isPilot ?
|
|
58
|
+
:label="labels.pilot"
|
|
59
|
+
:value="data.subscription?.isPilot ? common.yes : common.no"
|
|
60
60
|
/>
|
|
61
61
|
<KvBlock
|
|
62
|
-
:label="
|
|
62
|
+
:label="labels.trialEnd"
|
|
63
63
|
:value="formatDate(data.subscription?.trialEndsAt)"
|
|
64
64
|
/>
|
|
65
65
|
<KvBlock
|
|
66
|
-
:label="
|
|
66
|
+
:label="labels.pilotEnd"
|
|
67
67
|
:value="formatDate(data.subscription?.pilotEndsAt)"
|
|
68
68
|
/>
|
|
69
|
-
<KvBlock v-if="data.vatId" :label="
|
|
69
|
+
<KvBlock v-if="data.vatId" :label="labels.vatId" :value="data.vatId" />
|
|
70
70
|
<slot name="extra-stammdaten" :data="data" />
|
|
71
71
|
</div>
|
|
72
72
|
</slot>
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
|
|
75
75
|
<!-- Usage -->
|
|
76
76
|
<div v-if="verbrauchFields.length > 0" class="sa-card q-mb-md">
|
|
77
|
-
<h3 class="sa-card__title">{{
|
|
77
|
+
<h3 class="sa-card__title">{{ labels.usage }}</h3>
|
|
78
78
|
<div class="sa-tenant-detail__grid">
|
|
79
79
|
<KvBlock
|
|
80
80
|
v-for="(field, i) in verbrauchFields"
|
|
@@ -87,11 +87,11 @@
|
|
|
87
87
|
|
|
88
88
|
<!-- Users -->
|
|
89
89
|
<div v-if="showUsers && data.users" class="sa-card q-mb-md">
|
|
90
|
-
<h3 class="sa-card__title">{{
|
|
90
|
+
<h3 class="sa-card__title">{{ labels.users }}</h3>
|
|
91
91
|
<q-table
|
|
92
92
|
flat
|
|
93
93
|
:rows="data.users"
|
|
94
|
-
:columns="userColumns ??
|
|
94
|
+
:columns="userColumns ?? defaultUserColumns"
|
|
95
95
|
row-key="id"
|
|
96
96
|
:pagination="{ rowsPerPage: 0 }"
|
|
97
97
|
hide-pagination
|
|
@@ -127,9 +127,11 @@ import type { RouteLocationRaw } from 'vue-router';
|
|
|
127
127
|
import type { QTableColumn } from 'quasar';
|
|
128
128
|
import { useSuperAdminNotify } from '../quasar/notify.js';
|
|
129
129
|
import type { AdminManifest, TenantActionDef, TenantDto } from '@saasicat/types';
|
|
130
|
+
import { formatMessage } from '../client/i18n/format.js';
|
|
130
131
|
import KvBlock from '../components/KvBlock.vue';
|
|
131
132
|
import MfaPromptDialog from '../components/MfaPromptDialog.vue';
|
|
132
133
|
import TenantActionConfirmDialog from '../components/TenantActionConfirmDialog.vue';
|
|
134
|
+
import { useSaMessages } from '../vue/use-super-admin-i18n.js';
|
|
133
135
|
import { useTenantActionFlow } from '../vue/use-tenant-action-flow.js';
|
|
134
136
|
|
|
135
137
|
export interface TenantDetailData {
|
|
@@ -185,21 +187,29 @@ const props = withDefaults(
|
|
|
185
187
|
{
|
|
186
188
|
verbrauchFields: () => [],
|
|
187
189
|
showUsers: true,
|
|
188
|
-
backLabel: 'Mandanten-Liste',
|
|
189
|
-
titleLabel: 'Mandant',
|
|
190
|
-
slugLabel: 'Slug',
|
|
191
|
-
stammdatenLabel: 'Stammdaten',
|
|
192
|
-
planLabel: 'Plan',
|
|
193
|
-
statusLabel: 'Status',
|
|
194
|
-
pilotLabel: 'Pilot',
|
|
195
|
-
trialEndLabel: 'Trial-Ende',
|
|
196
|
-
pilotEndLabel: 'Pilot-Ende',
|
|
197
|
-
vatIdLabel: 'USt-IdNr.',
|
|
198
|
-
verbrauchLabel: 'Verbrauch',
|
|
199
|
-
usersLabel: 'User',
|
|
200
190
|
},
|
|
201
191
|
);
|
|
202
192
|
|
|
193
|
+
const msg = useSaMessages('tenants');
|
|
194
|
+
const common = useSaMessages('common');
|
|
195
|
+
|
|
196
|
+
// Every label stays overridable via props — only the German literals moved
|
|
197
|
+
// into the catalog.
|
|
198
|
+
const labels = computed(() => ({
|
|
199
|
+
back: props.backLabel ?? msg.value.detail.backToList,
|
|
200
|
+
title: props.titleLabel ?? msg.value.tenant,
|
|
201
|
+
slug: props.slugLabel ?? msg.value.detail.slug,
|
|
202
|
+
masterData: props.stammdatenLabel ?? msg.value.detail.masterData,
|
|
203
|
+
plan: props.planLabel ?? msg.value.plan,
|
|
204
|
+
status: props.statusLabel ?? common.value.status,
|
|
205
|
+
pilot: props.pilotLabel ?? msg.value.detail.pilot,
|
|
206
|
+
trialEnd: props.trialEndLabel ?? msg.value.detail.trialEnd,
|
|
207
|
+
pilotEnd: props.pilotEndLabel ?? msg.value.detail.pilotEnd,
|
|
208
|
+
vatId: props.vatIdLabel ?? msg.value.detail.vatId,
|
|
209
|
+
usage: props.verbrauchLabel ?? msg.value.detail.usage,
|
|
210
|
+
users: props.usersLabel ?? msg.value.detail.users,
|
|
211
|
+
}));
|
|
212
|
+
|
|
203
213
|
const notify = useSuperAdminNotify();
|
|
204
214
|
const data = ref<TenantDetailData | null>(null);
|
|
205
215
|
const loading = ref(false);
|
|
@@ -250,7 +260,10 @@ function showMfaDialog(def: TenantActionDef, ctx: { row: TenantDto }): Promise<s
|
|
|
250
260
|
return new Promise((resolve) => {
|
|
251
261
|
mfaState.value = {
|
|
252
262
|
show: true,
|
|
253
|
-
description:
|
|
263
|
+
description: formatMessage(msg.value.actions.mfaDescription, {
|
|
264
|
+
action: def.label,
|
|
265
|
+
tenant: ctx.row.name,
|
|
266
|
+
}),
|
|
254
267
|
error: '',
|
|
255
268
|
};
|
|
256
269
|
pendingMfaResolve = (code) => {
|
|
@@ -347,34 +360,35 @@ function toneColor(actionKey: string): string {
|
|
|
347
360
|
return 'primary';
|
|
348
361
|
}
|
|
349
362
|
|
|
350
|
-
const
|
|
351
|
-
{ name: 'email', label:
|
|
363
|
+
const defaultUserColumns = computed<QTableColumn[]>(() => [
|
|
364
|
+
{ name: 'email', label: msg.value.detail.userEmail, field: 'email', align: 'left' },
|
|
352
365
|
{
|
|
353
366
|
name: 'name',
|
|
354
|
-
label:
|
|
367
|
+
label: common.value.name,
|
|
355
368
|
field: (r: unknown) => {
|
|
356
369
|
const row = r as Record<string, unknown>;
|
|
357
370
|
return `${row.firstName ?? ''} ${row.lastName ?? ''}`.trim();
|
|
358
371
|
},
|
|
359
372
|
align: 'left',
|
|
360
373
|
},
|
|
361
|
-
{ name: 'role', label:
|
|
374
|
+
{ name: 'role', label: msg.value.detail.userRole, field: 'role', align: 'left' },
|
|
362
375
|
{
|
|
363
376
|
name: 'status',
|
|
364
|
-
label:
|
|
365
|
-
field: (r: unknown) =>
|
|
377
|
+
label: common.value.status,
|
|
378
|
+
field: (r: unknown) =>
|
|
379
|
+
(r as Record<string, unknown>).isActive ? common.value.active : common.value.inactive,
|
|
366
380
|
align: 'left',
|
|
367
381
|
},
|
|
368
382
|
{
|
|
369
383
|
name: 'lastLogin',
|
|
370
|
-
label:
|
|
384
|
+
label: msg.value.detail.userLastLogin,
|
|
371
385
|
field: (r: unknown) => {
|
|
372
386
|
const v = (r as Record<string, unknown>).lastLoginAt;
|
|
373
387
|
return v ? String(v).slice(0, 10) : '—';
|
|
374
388
|
},
|
|
375
389
|
align: 'left',
|
|
376
390
|
},
|
|
377
|
-
];
|
|
391
|
+
]);
|
|
378
392
|
</script>
|
|
379
393
|
|
|
380
394
|
<style scoped>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="sa-tenants">
|
|
3
3
|
<header class="sa-tenants__head">
|
|
4
4
|
<div>
|
|
5
|
-
<h1 class="sa-tenants__title">
|
|
5
|
+
<h1 class="sa-tenants__title">{{ msg.list.title }}</h1>
|
|
6
6
|
<p v-if="subtitle" class="sa-tenants__sub">{{ subtitle }}</p>
|
|
7
7
|
</div>
|
|
8
8
|
</header>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<q-icon name="search" size="15px" />
|
|
13
13
|
<input
|
|
14
14
|
v-model="searchInput"
|
|
15
|
-
placeholder="
|
|
15
|
+
:placeholder="msg.list.searchPlaceholder"
|
|
16
16
|
@input="reloadDebounced"
|
|
17
17
|
/>
|
|
18
18
|
<button v-if="searchInput" class="sa-tenants__clear" @click="onClearSearch">
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
</button>
|
|
21
21
|
</div>
|
|
22
22
|
<select v-model="statusFilter" class="sa-tenants__select" @change="applyFilter">
|
|
23
|
-
<option :value="null">
|
|
24
|
-
<option v-for="opt in
|
|
23
|
+
<option :value="null">{{ msg.list.allStatuses }}</option>
|
|
24
|
+
<option v-for="opt in resolvedStatusOptions" :key="opt.value" :value="opt.value">
|
|
25
25
|
{{ opt.label }}
|
|
26
26
|
</option>
|
|
27
27
|
</select>
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
class="sa-tenants__select"
|
|
32
32
|
@change="applyFilter"
|
|
33
33
|
>
|
|
34
|
-
<option :value="null">{{
|
|
34
|
+
<option :value="null">{{ resolvedPlanFilterLabel }}</option>
|
|
35
35
|
<option v-for="p in planOptions" :key="p" :value="p">{{ p }}</option>
|
|
36
36
|
</select>
|
|
37
37
|
<slot name="filters-extra" />
|
|
38
38
|
</div>
|
|
39
39
|
|
|
40
40
|
<q-banner v-if="error" class="bg-red-1 text-red-9 q-mb-md" rounded>
|
|
41
|
-
<strong>
|
|
41
|
+
<strong>{{ common.error }}:</strong> {{ error.message }}
|
|
42
42
|
</q-banner>
|
|
43
43
|
|
|
44
44
|
<div class="sa-tenants__card">
|
|
@@ -46,11 +46,13 @@
|
|
|
46
46
|
<table>
|
|
47
47
|
<thead>
|
|
48
48
|
<tr>
|
|
49
|
-
<th>
|
|
50
|
-
<th v-if="showPlanColumn">{{
|
|
51
|
-
<th>
|
|
52
|
-
<th v-if="usageFields.length > 0" class="num">
|
|
53
|
-
|
|
49
|
+
<th>{{ msg.tenant }}</th>
|
|
50
|
+
<th v-if="showPlanColumn">{{ resolvedPlanColumnLabel }}</th>
|
|
51
|
+
<th>{{ common.status }}</th>
|
|
52
|
+
<th v-if="usageFields.length > 0" class="num">
|
|
53
|
+
{{ msg.list.columnUsage }}
|
|
54
|
+
</th>
|
|
55
|
+
<th class="num">{{ msg.list.columnCreatedAt }}</th>
|
|
54
56
|
<th v-if="hasActions" class="num"></th>
|
|
55
57
|
</tr>
|
|
56
58
|
</thead>
|
|
@@ -100,7 +102,7 @@
|
|
|
100
102
|
</div>
|
|
101
103
|
</td>
|
|
102
104
|
<td class="num sa-tenants__mono">
|
|
103
|
-
{{
|
|
105
|
+
{{ formatCreatedAt(row) }}
|
|
104
106
|
</td>
|
|
105
107
|
<td v-if="hasActions" class="num sa-tenants__actions">
|
|
106
108
|
<slot name="row-actions" :row="row" :actions="visibleActions(row)">
|
|
@@ -127,17 +129,16 @@
|
|
|
127
129
|
</table>
|
|
128
130
|
<div v-if="items.length === 0 && !loading" class="sa-tenants__empty">
|
|
129
131
|
<q-icon name="search_off" size="32px" />
|
|
130
|
-
<div>
|
|
132
|
+
<div>{{ msg.list.empty }}</div>
|
|
131
133
|
</div>
|
|
132
134
|
<div v-if="loading" class="sa-tenants__loading">
|
|
133
|
-
<q-spinner size="20px" />
|
|
135
|
+
<q-spinner size="20px" /> {{ common.loading }}
|
|
134
136
|
</div>
|
|
135
137
|
</div>
|
|
136
138
|
</div>
|
|
137
139
|
|
|
138
140
|
<footer class="sa-tenants__foot">
|
|
139
|
-
{{
|
|
140
|
-
{{ Math.max(1, Math.ceil(total / pageSize)) }}
|
|
141
|
+
{{ paginationLabel }}
|
|
141
142
|
</footer>
|
|
142
143
|
|
|
143
144
|
<!-- Manifest-Driven Action Flow: only mounted when the `manifest` prop
|
|
@@ -166,13 +167,10 @@
|
|
|
166
167
|
|
|
167
168
|
<script setup lang="ts">
|
|
168
169
|
import { computed, ref, watch } from 'vue';
|
|
169
|
-
import type {
|
|
170
|
-
AdminManifest,
|
|
171
|
-
TenantActionDef,
|
|
172
|
-
TenantDto,
|
|
173
|
-
TenantListFilter,
|
|
174
|
-
} from '@saasicat/types';
|
|
170
|
+
import type { AdminManifest, TenantActionDef, TenantDto, TenantListFilter } from '@saasicat/types';
|
|
175
171
|
import type { HttpClient } from '../client/types.js';
|
|
172
|
+
import { formatMessage } from '../client/i18n/format.js';
|
|
173
|
+
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
176
174
|
import { useTenants } from '../vue/use-tenants.js';
|
|
177
175
|
import {
|
|
178
176
|
usePlatformTenantActions,
|
|
@@ -181,12 +179,7 @@ import {
|
|
|
181
179
|
import MfaPromptDialog from '../components/MfaPromptDialog.vue';
|
|
182
180
|
import TenantActionConfirmDialog from '../components/TenantActionConfirmDialog.vue';
|
|
183
181
|
import StatusPill, { type PillTone } from './tenants/StatusPill.vue';
|
|
184
|
-
import {
|
|
185
|
-
DEFAULT_PLAN_ACCENTS,
|
|
186
|
-
formatDateDe,
|
|
187
|
-
planAccent,
|
|
188
|
-
tenantInitials,
|
|
189
|
-
} from './tenants/format.js';
|
|
182
|
+
import { DEFAULT_PLAN_ACCENTS, formatDate, planAccent, tenantInitials } from './tenants/format.js';
|
|
190
183
|
|
|
191
184
|
// Platform standard page: tenant list.
|
|
192
185
|
//
|
|
@@ -196,7 +189,7 @@ import {
|
|
|
196
189
|
//
|
|
197
190
|
// App-specific bits via props:
|
|
198
191
|
// - `subtitle` : header subtitle
|
|
199
|
-
// - `statusOptions` : filter dropdown values (default:
|
|
192
|
+
// - `statusOptions` : filter dropdown values (default: active/suspended)
|
|
200
193
|
// - `planOptions` : filter dropdown values for plan; omitted = hides dropdown
|
|
201
194
|
// - `planAccents` : plan id → accent color (avatar + plan dot)
|
|
202
195
|
// - `planLabelField` : field for the plan display (e.g. "plan" or "bundleKey")
|
|
@@ -331,12 +324,6 @@ const props = withDefaults(
|
|
|
331
324
|
}>(),
|
|
332
325
|
{
|
|
333
326
|
pageSize: 25,
|
|
334
|
-
statusOptions: () => [
|
|
335
|
-
{ value: 'ACTIVE', label: 'Aktiv' },
|
|
336
|
-
{ value: 'INACTIVE', label: 'Suspendiert' },
|
|
337
|
-
],
|
|
338
|
-
planFilterLabel: 'Alle Pläne',
|
|
339
|
-
planColumnLabel: 'Plan',
|
|
340
327
|
showPlanColumn: true,
|
|
341
328
|
planLabelField: 'plan',
|
|
342
329
|
planAccents: () => DEFAULT_PLAN_ACCENTS,
|
|
@@ -345,6 +332,20 @@ const props = withDefaults(
|
|
|
345
332
|
},
|
|
346
333
|
);
|
|
347
334
|
|
|
335
|
+
const msg = useSaMessages('tenants');
|
|
336
|
+
const common = useSaMessages('common');
|
|
337
|
+
const { intlLocale } = useSuperAdminI18n();
|
|
338
|
+
|
|
339
|
+
const resolvedStatusOptions = computed<ReadonlyArray<{ value: string; label: string }>>(
|
|
340
|
+
() =>
|
|
341
|
+
props.statusOptions ?? [
|
|
342
|
+
{ value: 'ACTIVE', label: common.value.active },
|
|
343
|
+
{ value: 'INACTIVE', label: msg.value.list.suspended },
|
|
344
|
+
],
|
|
345
|
+
);
|
|
346
|
+
const resolvedPlanFilterLabel = computed(() => props.planFilterLabel ?? msg.value.list.allPlans);
|
|
347
|
+
const resolvedPlanColumnLabel = computed(() => props.planColumnLabel ?? msg.value.plan);
|
|
348
|
+
|
|
348
349
|
const filter = ref<TenantListFilter>({});
|
|
349
350
|
const searchInput = ref('');
|
|
350
351
|
const statusFilter = ref<string | null>(null);
|
|
@@ -360,6 +361,14 @@ const { items, page, total, loading, error, goToPage, setPageSize } = list;
|
|
|
360
361
|
|
|
361
362
|
setPageSize(props.pageSize);
|
|
362
363
|
|
|
364
|
+
const paginationLabel = computed(() =>
|
|
365
|
+
formatMessage(msg.value.list.pagination, {
|
|
366
|
+
total: total.value,
|
|
367
|
+
page: page.value,
|
|
368
|
+
pages: Math.max(1, Math.ceil(total.value / props.pageSize)),
|
|
369
|
+
}),
|
|
370
|
+
);
|
|
371
|
+
|
|
363
372
|
let searchTimer: ReturnType<typeof setTimeout> | null = null;
|
|
364
373
|
function reloadDebounced(): void {
|
|
365
374
|
if (searchTimer) clearTimeout(searchTimer);
|
|
@@ -427,7 +436,7 @@ if (manifestFlow && typeof window !== 'undefined') {
|
|
|
427
436
|
if (orphans.length > 0) {
|
|
428
437
|
// eslint-disable-next-line no-console
|
|
429
438
|
console.warn(
|
|
430
|
-
'[PlatformTenantsPage] Manifest
|
|
439
|
+
'[PlatformTenantsPage] Manifest actions without a handler in createSuperAdminApp({ actions }):',
|
|
431
440
|
orphans,
|
|
432
441
|
);
|
|
433
442
|
}
|
|
@@ -449,8 +458,8 @@ const combinedActions = computed<TenantRowAction[]>(() => {
|
|
|
449
458
|
if (typeof window !== 'undefined') {
|
|
450
459
|
// eslint-disable-next-line no-console
|
|
451
460
|
console.warn(
|
|
452
|
-
`[PlatformTenantsPage] Custom
|
|
453
|
-
`
|
|
461
|
+
`[PlatformTenantsPage] Custom action "${a.id}" (actionKey="${a.actionKey}") ` +
|
|
462
|
+
`collides with a manifest action — the manifest wins.`,
|
|
454
463
|
);
|
|
455
464
|
}
|
|
456
465
|
return false;
|
|
@@ -488,14 +497,18 @@ function usageStr(row: TenantRow, key: string): string | undefined {
|
|
|
488
497
|
return typeof v === 'string' ? v : undefined;
|
|
489
498
|
}
|
|
490
499
|
|
|
500
|
+
function formatCreatedAt(row: TenantRow): string {
|
|
501
|
+
return formatDate(usageStr(row, 'createdAt'), intlLocale.value);
|
|
502
|
+
}
|
|
503
|
+
|
|
491
504
|
function resolvedPills(row: TenantRow): StatusPillDef[] {
|
|
492
505
|
if (props.pillsForRow) return props.pillsForRow(row);
|
|
493
|
-
// Default: only
|
|
506
|
+
// Default: only active/suspended.
|
|
494
507
|
const isActive = (row as TenantDto).isActive;
|
|
495
508
|
return [
|
|
496
509
|
isActive
|
|
497
|
-
? { label:
|
|
498
|
-
: { label:
|
|
510
|
+
? { label: common.value.active, icon: 'check_circle', tone: 'positive' }
|
|
511
|
+
: { label: msg.value.list.suspended, icon: 'block', tone: 'negative' },
|
|
499
512
|
];
|
|
500
513
|
}
|
|
501
514
|
|