@saasicat/ui-vue 0.19.0 → 0.21.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/dist/{catalog-JXdKdIqG.d.cts → catalog-BY_t9prv.d.cts} +1 -0
- package/dist/{catalog-JXdKdIqG.d.ts → catalog-BY_t9prv.d.ts} +1 -0
- package/dist/{chunk-RVG6LQHL.js → chunk-7IFKVGZV.js} +1 -1
- package/dist/{chunk-DPTIOQXX.js → chunk-JN6HZGLV.js} +2 -0
- package/dist/{chunk-ORX3ZG47.js → chunk-OTP3OSSW.js} +1 -1
- package/dist/client/index.cjs +2 -0
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +2 -2
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/quasar/index.cjs +2 -0
- package/dist/quasar/index.d.cts +2 -2
- package/dist/quasar/index.d.ts +2 -2
- package/dist/quasar/index.js +2 -2
- package/dist/{use-super-admin-i18n-1nCOkvgi.d.cts → use-super-admin-i18n-DlJRGIVq.d.cts} +1 -1
- package/dist/{use-super-admin-i18n-BlqcD0N7.d.ts → use-super-admin-i18n-c_Eqsu9u.d.ts} +1 -1
- package/package.json +2 -2
- package/src/client/i18n/messages/common.ts +2 -0
- package/src/components/admin-page/AdminBody.vue +40 -0
- package/src/components/admin-page/AdminHero.vue +32 -0
- package/src/components/admin-page/AdminPage.vue +16 -0
- package/src/components/admin-page/AdminSection.vue +38 -0
- package/src/components/plan-cockpit/PlanCockpit.vue +15 -0
- package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +8 -8
- package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +10 -11
- package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +8 -8
- package/src/components/plan-cockpit/PlanCockpitVersions.vue +9 -11
- package/src/components/plan-detail/PlanAuditLog.vue +7 -8
- package/src/components/plan-detail/PlanDetail.vue +14 -1
- package/src/components/plan-detail/PlanVersionDiffPanel.vue +5 -8
- package/src/components/plan-detail/PlanVersionsPanel.vue +9 -11
- package/src/components/plan-list/PlanList.vue +224 -257
- package/src/pages-standard/AdminLayout.vue +4 -2
- package/src/pages-standard/AuditPage.vue +18 -43
- package/src/pages-standard/BundlesPage.vue +17 -34
- package/src/pages-standard/DashboardPage.vue +20 -51
- package/src/pages-standard/DiscoveryPage.vue +38 -51
- package/src/pages-standard/EmailHistoryPage.vue +20 -71
- package/src/pages-standard/MarketingCatalogPage.vue +304 -315
- package/src/pages-standard/PilotsPage.vue +24 -56
- package/src/pages-standard/PlansPage.vue +25 -22
- package/src/pages-standard/PlatformEmailPage.vue +19 -51
- package/src/pages-standard/PromoCodeDetailPage.vue +39 -50
- package/src/pages-standard/PromoCodesPage.vue +8 -15
- package/src/pages-standard/SubscriptionsPage.vue +9 -35
- package/src/pages-standard/SuperAdminSetupWizard.vue +2 -1
- package/src/pages-standard/TenantDetailPage.vue +79 -174
- package/src/pages-standard/TenantsPage.vue +8 -33
- package/src/pages-standard/UsersPage.vue +21 -54
- package/src/pages-standard/bundles-page/BundlesToolbar.vue +67 -0
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +111 -73
- package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +54 -62
- package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +53 -35
- package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +7 -7
- package/src/pages-standard/plans-page/PlansPageToast.vue +5 -5
- package/src/pages-standard/sa-theme.css +85 -29
- package/src/pages-standard/tenant-detail/TenantMasterData.vue +28 -0
- package/src/pages-standard/tenant-detail/TenantUsage.vue +31 -0
- package/src/pages-standard/tenant-detail/TenantUsers.vue +21 -0
- package/src/pages-standard/tenant-detail/types.ts +38 -0
- package/src/vue/use-mfa-prompt.ts +72 -0
- package/src/pages-standard/bundles-page/BundlesHeader.vue +0 -68
- package/src/pages-standard/discovery-page/DiscoveryHeader.vue +0 -34
|
@@ -86,9 +86,11 @@
|
|
|
86
86
|
</q-drawer>
|
|
87
87
|
|
|
88
88
|
<q-page-container>
|
|
89
|
-
|
|
89
|
+
<!-- The document's only <main>. Pages must not add one of their
|
|
90
|
+
own, and must not use QPage, which renders <main> itself. -->
|
|
91
|
+
<main :class="['sa-admin-content', { 'sa-admin-content--fullbleed': isFullbleed }]">
|
|
90
92
|
<router-view />
|
|
91
|
-
</
|
|
93
|
+
</main>
|
|
92
94
|
</q-page-container>
|
|
93
95
|
</q-layout>
|
|
94
96
|
</template>
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
</header>
|
|
2
|
+
<AdminPage class="sa-audit">
|
|
3
|
+
<AdminHero
|
|
4
|
+
:title="msg.title"
|
|
5
|
+
:subtitle="formatMessage(msg.subtitle, { count: rows.length })"
|
|
6
|
+
>
|
|
7
|
+
<template #actions>
|
|
8
|
+
<q-btn
|
|
9
|
+
unelevated
|
|
10
|
+
color="primary"
|
|
11
|
+
icon="refresh"
|
|
12
|
+
:label="msg.filterButton"
|
|
13
|
+
@click="reload"
|
|
14
|
+
/>
|
|
15
|
+
</template>
|
|
16
|
+
</AdminHero>
|
|
18
17
|
|
|
19
18
|
<div class="sa-audit__body">
|
|
20
19
|
<div class="sa-audit__filter">
|
|
@@ -89,11 +88,13 @@
|
|
|
89
88
|
</q-card-actions>
|
|
90
89
|
</q-card>
|
|
91
90
|
</q-dialog>
|
|
92
|
-
</
|
|
91
|
+
</AdminPage>
|
|
93
92
|
</template>
|
|
94
93
|
|
|
95
94
|
<script setup lang="ts">
|
|
96
95
|
import { computed, onMounted, reactive, ref } from 'vue';
|
|
96
|
+
import AdminHero from '../components/admin-page/AdminHero.vue';
|
|
97
|
+
import AdminPage from '../components/admin-page/AdminPage.vue';
|
|
97
98
|
import { formatMessage } from '../client/i18n/format.js';
|
|
98
99
|
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
99
100
|
|
|
@@ -206,32 +207,6 @@ function formatTs(iso: string | null | undefined): string {
|
|
|
206
207
|
</script>
|
|
207
208
|
|
|
208
209
|
<style scoped>
|
|
209
|
-
.sa-audit {
|
|
210
|
-
min-height: calc(100vh - 56px);
|
|
211
|
-
background: var(--sa-bg-app, #f1f5f9);
|
|
212
|
-
}
|
|
213
|
-
.sa-audit__head {
|
|
214
|
-
padding: 20px 28px 8px;
|
|
215
|
-
display: flex;
|
|
216
|
-
align-items: flex-start;
|
|
217
|
-
justify-content: space-between;
|
|
218
|
-
gap: 16px;
|
|
219
|
-
}
|
|
220
|
-
.sa-audit__title {
|
|
221
|
-
margin: 0;
|
|
222
|
-
font-family: var(--sa-font-head, system-ui, sans-serif);
|
|
223
|
-
font-weight: 700;
|
|
224
|
-
font-size: 22px;
|
|
225
|
-
color: var(--sa-heading, #0f172a);
|
|
226
|
-
}
|
|
227
|
-
.sa-audit__sub {
|
|
228
|
-
margin: 4px 0 0;
|
|
229
|
-
color: var(--sa-muted-dark, #475569);
|
|
230
|
-
font-size: 13.5px;
|
|
231
|
-
}
|
|
232
|
-
.sa-audit__body {
|
|
233
|
-
padding: 12px 28px 28px;
|
|
234
|
-
}
|
|
235
210
|
.sa-audit__filter {
|
|
236
211
|
display: flex;
|
|
237
212
|
gap: 10px;
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
<AdminPage class="sa-bundles">
|
|
3
|
+
<AdminHero :title="msg.header.title" :subtitle="msg.header.subtitle">
|
|
4
|
+
<template #actions>
|
|
5
|
+
<BundlesToolbar
|
|
6
|
+
:loading="loading"
|
|
7
|
+
:display-locale="displayLocale"
|
|
8
|
+
:locales="locales"
|
|
9
|
+
@create="openCreatePanel"
|
|
10
|
+
@refresh="load"
|
|
11
|
+
@update:display-locale="(value) => (displayLocale = value)"
|
|
12
|
+
/>
|
|
13
|
+
</template>
|
|
14
|
+
</AdminHero>
|
|
11
15
|
|
|
12
16
|
<q-banner v-if="error" class="sa-bundles__error" rounded>
|
|
13
17
|
<template #avatar><q-icon name="warning" color="negative" /></template>
|
|
@@ -131,7 +135,7 @@
|
|
|
131
135
|
:submit="onPublishSubmit"
|
|
132
136
|
@submitted="onPublishSubmitted"
|
|
133
137
|
/>
|
|
134
|
-
</
|
|
138
|
+
</AdminPage>
|
|
135
139
|
</template>
|
|
136
140
|
|
|
137
141
|
<script setup lang="ts">
|
|
@@ -174,7 +178,9 @@ import { useSaMessages } from '../vue/use-super-admin-i18n.js';
|
|
|
174
178
|
import BundleAccordionList from './bundles-page/BundleAccordionList.vue';
|
|
175
179
|
import BundleDetailPanel from './bundles-page/BundleDetailPanel.vue';
|
|
176
180
|
import BundlesFilterBar from './bundles-page/BundlesFilterBar.vue';
|
|
177
|
-
import
|
|
181
|
+
import AdminHero from '../components/admin-page/AdminHero.vue';
|
|
182
|
+
import AdminPage from '../components/admin-page/AdminPage.vue';
|
|
183
|
+
import BundlesToolbar from './bundles-page/BundlesToolbar.vue';
|
|
178
184
|
import BundlesKpis from './bundles-page/BundlesKpis.vue';
|
|
179
185
|
import type {
|
|
180
186
|
BundleEditForm,
|
|
@@ -643,33 +649,10 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
643
649
|
|
|
644
650
|
<style>
|
|
645
651
|
.sa-bundles {
|
|
646
|
-
padding: 16px;
|
|
647
652
|
display: flex;
|
|
648
653
|
flex-direction: column;
|
|
649
654
|
gap: 14px;
|
|
650
655
|
}
|
|
651
|
-
.sa-bundles__head {
|
|
652
|
-
display: flex;
|
|
653
|
-
justify-content: space-between;
|
|
654
|
-
align-items: flex-end;
|
|
655
|
-
gap: 16px;
|
|
656
|
-
}
|
|
657
|
-
.sa-bundles__title {
|
|
658
|
-
font-size: 20px;
|
|
659
|
-
font-weight: 700;
|
|
660
|
-
margin: 0;
|
|
661
|
-
}
|
|
662
|
-
.sa-bundles__sub {
|
|
663
|
-
margin: 4px 0 0;
|
|
664
|
-
color: #64748b;
|
|
665
|
-
font-size: 12px;
|
|
666
|
-
max-width: 560px;
|
|
667
|
-
}
|
|
668
|
-
.sa-bundles__head-actions {
|
|
669
|
-
display: flex;
|
|
670
|
-
align-items: center;
|
|
671
|
-
gap: 12px;
|
|
672
|
-
}
|
|
673
656
|
.sa-bundles__error {
|
|
674
657
|
border-left: 4px solid #dc2626;
|
|
675
658
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
<h1 class="sa-page-head__title">{{ msg.title }}</h1>
|
|
6
|
-
<p v-if="subtitle" class="sa-page-head__sub">{{ subtitle }}</p>
|
|
7
|
-
</div>
|
|
8
|
-
<div class="sa-page-head__actions">
|
|
2
|
+
<AdminPage class="sa-dashboard">
|
|
3
|
+
<AdminHero :title="msg.title" :subtitle="subtitle">
|
|
4
|
+
<template #actions>
|
|
9
5
|
<q-btn
|
|
10
6
|
flat
|
|
11
7
|
dense
|
|
@@ -14,8 +10,8 @@
|
|
|
14
10
|
:aria-label="common.reload"
|
|
15
11
|
@click="reload"
|
|
16
12
|
/>
|
|
17
|
-
</
|
|
18
|
-
</
|
|
13
|
+
</template>
|
|
14
|
+
</AdminHero>
|
|
19
15
|
|
|
20
16
|
<q-banner v-if="error" class="bg-red-1 text-red-9 q-mb-md" rounded>
|
|
21
17
|
<strong>{{ common.error }}:</strong> {{ error.message }}
|
|
@@ -47,15 +43,15 @@
|
|
|
47
43
|
</div>
|
|
48
44
|
|
|
49
45
|
<div v-if="distributions && distributions.length > 0" class="sa-dashboard__rows">
|
|
50
|
-
<
|
|
46
|
+
<AdminSection
|
|
51
47
|
v-for="dist in distributions"
|
|
52
48
|
:key="dist.id"
|
|
53
|
-
|
|
49
|
+
:title="dist.label"
|
|
50
|
+
class="sa-card sa-dashboard__row-card"
|
|
54
51
|
>
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
</header>
|
|
52
|
+
<template v-if="dist.total" #actions>
|
|
53
|
+
<span class="sa-dashboard__count">{{ dist.total }}</span>
|
|
54
|
+
</template>
|
|
59
55
|
<ul class="sa-dashboard__bar-list">
|
|
60
56
|
<li
|
|
61
57
|
v-for="entry in dist.entries"
|
|
@@ -75,16 +71,14 @@
|
|
|
75
71
|
<span class="sa-dashboard__bar-count">{{ entry.value }}</span>
|
|
76
72
|
</li>
|
|
77
73
|
</ul>
|
|
78
|
-
</
|
|
74
|
+
</AdminSection>
|
|
79
75
|
</div>
|
|
80
76
|
|
|
81
|
-
<
|
|
77
|
+
<AdminSection
|
|
82
78
|
v-if="resolvedShortcuts.length > 0"
|
|
83
|
-
|
|
79
|
+
:title="msg.shortcutsTitle"
|
|
80
|
+
class="sa-card sa-dashboard__shortcuts"
|
|
84
81
|
>
|
|
85
|
-
<header class="sa-dashboard__row-head">
|
|
86
|
-
<h2>{{ msg.shortcutsTitle }}</h2>
|
|
87
|
-
</header>
|
|
88
82
|
<div class="sa-dashboard__shortcut-grid">
|
|
89
83
|
<a
|
|
90
84
|
v-for="s in resolvedShortcuts"
|
|
@@ -99,10 +93,10 @@
|
|
|
99
93
|
</div>
|
|
100
94
|
</a>
|
|
101
95
|
</div>
|
|
102
|
-
</
|
|
96
|
+
</AdminSection>
|
|
103
97
|
|
|
104
98
|
<slot name="after-kpis" />
|
|
105
|
-
</
|
|
99
|
+
</AdminPage>
|
|
106
100
|
</template>
|
|
107
101
|
|
|
108
102
|
<script setup lang="ts">
|
|
@@ -114,6 +108,9 @@ import { formatMessage } from '../client/i18n/format.js';
|
|
|
114
108
|
import { SUPER_ADMIN_MANIFEST_KEY } from '../vue/super-admin-context.js';
|
|
115
109
|
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
116
110
|
import { useSuperAdminHttp } from '../vue/use-super-admin-context.js';
|
|
111
|
+
import AdminHero from '../components/admin-page/AdminHero.vue';
|
|
112
|
+
import AdminPage from '../components/admin-page/AdminPage.vue';
|
|
113
|
+
import AdminSection from '../components/admin-page/AdminSection.vue';
|
|
117
114
|
|
|
118
115
|
// Platform standard page: Dashboard.
|
|
119
116
|
//
|
|
@@ -403,16 +400,7 @@ function barWidth(value: number, max?: number): number {
|
|
|
403
400
|
</script>
|
|
404
401
|
|
|
405
402
|
<style scoped>
|
|
406
|
-
.sa-dashboard {
|
|
407
|
-
min-height: calc(100vh - 56px);
|
|
408
|
-
background: var(--sa-bg-app, #f1f5f9);
|
|
409
|
-
padding: 20px 28px 28px;
|
|
410
|
-
}
|
|
411
403
|
/* Header look comes from the global .sa-page-head — only a margin tweak here. */
|
|
412
|
-
.sa-dashboard__head {
|
|
413
|
-
margin-bottom: 4px;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
404
|
.sa-dashboard__strip {
|
|
417
405
|
display: grid;
|
|
418
406
|
/* Responsive: 4 cards while there is space, then automatically 3 → 2 → 1. */
|
|
@@ -494,25 +482,6 @@ function barWidth(value: number, max?: number): number {
|
|
|
494
482
|
}
|
|
495
483
|
}
|
|
496
484
|
|
|
497
|
-
.sa-dashboard__card {
|
|
498
|
-
background: #fff;
|
|
499
|
-
border: 1px solid var(--sa-border, #e2e8f0);
|
|
500
|
-
border-radius: 12px;
|
|
501
|
-
padding: 16px 18px;
|
|
502
|
-
}
|
|
503
|
-
.sa-dashboard__row-head {
|
|
504
|
-
display: flex;
|
|
505
|
-
align-items: center;
|
|
506
|
-
gap: 10px;
|
|
507
|
-
margin-bottom: 12px;
|
|
508
|
-
}
|
|
509
|
-
.sa-dashboard__row-head h2 {
|
|
510
|
-
margin: 0;
|
|
511
|
-
font-family: var(--sa-font-head, system-ui, sans-serif);
|
|
512
|
-
font-size: 14px;
|
|
513
|
-
font-weight: 700;
|
|
514
|
-
color: var(--sa-heading, #0f172a);
|
|
515
|
-
}
|
|
516
485
|
.sa-dashboard__count {
|
|
517
486
|
background: var(--sa-primary-soft, rgba(63, 107, 255, 0.08));
|
|
518
487
|
color: var(--sa-primary, #3f6bff);
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
2
|
+
<AdminPage class="sa-discovery">
|
|
3
|
+
<AdminHero :title="msg.title">
|
|
4
|
+
<template #subtitle>
|
|
5
|
+
{{ msg.subtitleLead }} <b>{{ msg.subtitleEmphasis }}</b> {{ msg.subtitleTail }}
|
|
6
|
+
</template>
|
|
7
|
+
<template #actions>
|
|
8
|
+
<q-btn
|
|
9
|
+
unelevated
|
|
10
|
+
color="primary"
|
|
11
|
+
icon="bolt"
|
|
12
|
+
:label="msg.runDiscovery"
|
|
13
|
+
:loading="loading"
|
|
14
|
+
@click="onRunDiscovery"
|
|
15
|
+
/>
|
|
16
|
+
</template>
|
|
17
|
+
</AdminHero>
|
|
4
18
|
|
|
5
19
|
<DiscoveryMetaBanner
|
|
6
20
|
:app-label="appLabel"
|
|
@@ -53,11 +67,15 @@
|
|
|
53
67
|
/>
|
|
54
68
|
</div>
|
|
55
69
|
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
<AdminSection
|
|
71
|
+
v-for="group in featureGroups"
|
|
72
|
+
:key="group.label"
|
|
73
|
+
:title="group.label"
|
|
74
|
+
class="sa-discovery__group"
|
|
75
|
+
>
|
|
76
|
+
<template #actions>
|
|
59
77
|
<span class="sa-discovery__group-count">{{ group.features.length }}</span>
|
|
60
|
-
</
|
|
78
|
+
</template>
|
|
61
79
|
<div class="sa-discovery__cardlist">
|
|
62
80
|
<DiscoveryFeatureCard
|
|
63
81
|
v-for="f in group.features"
|
|
@@ -74,19 +92,19 @@
|
|
|
74
92
|
@feature-locale="onFeatureLocale"
|
|
75
93
|
/>
|
|
76
94
|
</div>
|
|
77
|
-
</
|
|
95
|
+
</AdminSection>
|
|
78
96
|
<div v-if="filteredFeatures.length === 0" class="sa-discovery__empty-row">
|
|
79
97
|
{{ msg.noFeaturesMatchFilters }}
|
|
80
98
|
</div>
|
|
81
99
|
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
<AdminSection
|
|
101
|
+
v-if="orphanCaps.length"
|
|
102
|
+
:title="msg.orphansTitle"
|
|
103
|
+
class="sa-discovery__group sa-discovery__group--orphan"
|
|
104
|
+
>
|
|
105
|
+
<template #actions>
|
|
88
106
|
<span class="sa-discovery__group-count">{{ orphanCaps.length }}</span>
|
|
89
|
-
</
|
|
107
|
+
</template>
|
|
90
108
|
<p class="sa-discovery__orphan-hint">
|
|
91
109
|
{{ msg.orphanHint.before }} <code>feature:</code>{{ msg.orphanHint.middle }}
|
|
92
110
|
<code>@ImplementsCapability</code>{{ msg.orphanHint.after }}
|
|
@@ -95,7 +113,7 @@
|
|
|
95
113
|
:capabilities="orphanCaps"
|
|
96
114
|
:declared-at-by-key="declaredAtByKey"
|
|
97
115
|
/>
|
|
98
|
-
</
|
|
116
|
+
</AdminSection>
|
|
99
117
|
</q-tab-panel>
|
|
100
118
|
|
|
101
119
|
<q-tab-panel name="quotas" class="sa-discovery__panel">
|
|
@@ -117,7 +135,7 @@
|
|
|
117
135
|
</div>
|
|
118
136
|
</q-tab-panel>
|
|
119
137
|
</q-tab-panels>
|
|
120
|
-
</
|
|
138
|
+
</AdminPage>
|
|
121
139
|
</template>
|
|
122
140
|
|
|
123
141
|
<script setup lang="ts">
|
|
@@ -135,7 +153,9 @@ import type {
|
|
|
135
153
|
} from '@saasicat/types';
|
|
136
154
|
import DiscoveryCapList from './discovery-page/DiscoveryCapList.vue';
|
|
137
155
|
import DiscoveryFeatureCard from './discovery-page/DiscoveryFeatureCard.vue';
|
|
138
|
-
import
|
|
156
|
+
import AdminHero from '../components/admin-page/AdminHero.vue';
|
|
157
|
+
import AdminSection from '../components/admin-page/AdminSection.vue';
|
|
158
|
+
import AdminPage from '../components/admin-page/AdminPage.vue';
|
|
139
159
|
import DiscoveryKpis from './discovery-page/DiscoveryKpis.vue';
|
|
140
160
|
import DiscoveryMetaBanner from './discovery-page/DiscoveryMetaBanner.vue';
|
|
141
161
|
import DiscoveryQuotaCard from './discovery-page/DiscoveryQuotaCard.vue';
|
|
@@ -438,23 +458,6 @@ onMounted(() => {
|
|
|
438
458
|
flex-direction: column;
|
|
439
459
|
gap: 14px;
|
|
440
460
|
}
|
|
441
|
-
.sa-discovery__head {
|
|
442
|
-
display: flex;
|
|
443
|
-
justify-content: space-between;
|
|
444
|
-
align-items: flex-end;
|
|
445
|
-
gap: 16px;
|
|
446
|
-
}
|
|
447
|
-
.sa-discovery__title {
|
|
448
|
-
font-size: 20px;
|
|
449
|
-
font-weight: 700;
|
|
450
|
-
margin: 0;
|
|
451
|
-
}
|
|
452
|
-
.sa-discovery__sub {
|
|
453
|
-
margin: 4px 0 0;
|
|
454
|
-
color: #64748b;
|
|
455
|
-
font-size: 12px;
|
|
456
|
-
max-width: 640px;
|
|
457
|
-
}
|
|
458
461
|
.sa-discovery__banner {
|
|
459
462
|
display: flex;
|
|
460
463
|
justify-content: space-between;
|
|
@@ -559,23 +562,7 @@ onMounted(() => {
|
|
|
559
562
|
.sa-discovery__group {
|
|
560
563
|
margin-bottom: 16px;
|
|
561
564
|
}
|
|
562
|
-
.sa-discovery__group-
|
|
563
|
-
display: flex;
|
|
564
|
-
align-items: center;
|
|
565
|
-
gap: 8px;
|
|
566
|
-
margin-bottom: 8px;
|
|
567
|
-
}
|
|
568
|
-
.sa-discovery__group-title {
|
|
569
|
-
font-size: 11px;
|
|
570
|
-
font-weight: 700;
|
|
571
|
-
text-transform: uppercase;
|
|
572
|
-
letter-spacing: 0.5px;
|
|
573
|
-
color: #475569;
|
|
574
|
-
display: flex;
|
|
575
|
-
align-items: center;
|
|
576
|
-
gap: 5px;
|
|
577
|
-
}
|
|
578
|
-
.sa-discovery__group-title--orphan {
|
|
565
|
+
.sa-discovery__group--orphan .sa-section__title {
|
|
579
566
|
color: #b45309;
|
|
580
567
|
}
|
|
581
568
|
.sa-discovery__group-count {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<q-btn flat icon="refresh" :label="common.reload" @click="applyFilter" />
|
|
9
|
-
</header>
|
|
2
|
+
<AdminPage class="sa-emh">
|
|
3
|
+
<AdminHero :title="resolvedTitle" :subtitle="msg.history.subtitle">
|
|
4
|
+
<template #actions>
|
|
5
|
+
<q-btn flat icon="refresh" :label="common.reload" @click="applyFilter" />
|
|
6
|
+
</template>
|
|
7
|
+
</AdminHero>
|
|
10
8
|
|
|
11
9
|
<div class="sa-emh__body">
|
|
12
10
|
<div class="sa-emh__filter">
|
|
@@ -206,18 +204,21 @@
|
|
|
206
204
|
|
|
207
205
|
<MfaPromptDialog
|
|
208
206
|
v-if="requireMfaForWrite"
|
|
209
|
-
:model-value="
|
|
210
|
-
:description="
|
|
211
|
-
:error="
|
|
207
|
+
:model-value="mfa.show.value"
|
|
208
|
+
:description="mfa.description.value"
|
|
209
|
+
:error="mfa.error.value"
|
|
212
210
|
:setup-hint="mfaSetupHint"
|
|
213
|
-
@update:model-value="
|
|
214
|
-
@confirm="
|
|
211
|
+
@update:model-value="mfa.onVisibility"
|
|
212
|
+
@confirm="mfa.onConfirm"
|
|
215
213
|
/>
|
|
216
|
-
</
|
|
214
|
+
</AdminPage>
|
|
217
215
|
</template>
|
|
218
216
|
|
|
219
217
|
<script setup lang="ts">
|
|
220
218
|
import { computed, reactive, ref } from 'vue';
|
|
219
|
+
import { useMfaPrompt } from '../vue/use-mfa-prompt.js';
|
|
220
|
+
import AdminHero from '../components/admin-page/AdminHero.vue';
|
|
221
|
+
import AdminPage from '../components/admin-page/AdminPage.vue';
|
|
221
222
|
import { useSuperAdminNotify } from '../quasar/notify.js';
|
|
222
223
|
import MfaPromptDialog from '../components/MfaPromptDialog.vue';
|
|
223
224
|
import { formatMessage } from '../client/i18n/format.js';
|
|
@@ -317,10 +318,7 @@ const confirmDeleteOpen = ref(false);
|
|
|
317
318
|
const pendingDeleteId = ref<string | null>(null);
|
|
318
319
|
|
|
319
320
|
// MFA loop analogous to PlatformEmailPage (promise-resolver pattern).
|
|
320
|
-
const
|
|
321
|
-
const mfaError = ref('');
|
|
322
|
-
const mfaDescription = ref('');
|
|
323
|
-
let pendingMfaResolve: ((code: string | null) => void) | null = null;
|
|
321
|
+
const mfa = useMfaPrompt();
|
|
324
322
|
|
|
325
323
|
// Sequence guard: with rapidly changing filters a stale (out-of-order)
|
|
326
324
|
// response must not overwrite the newest.
|
|
@@ -428,48 +426,25 @@ async function runWrite<T>(
|
|
|
428
426
|
}
|
|
429
427
|
}
|
|
430
428
|
for (;;) {
|
|
431
|
-
const code = await
|
|
429
|
+
const code = await mfa.prompt(label);
|
|
432
430
|
if (code === null) return { ok: false };
|
|
433
431
|
try {
|
|
434
432
|
const result = await invoke(code);
|
|
435
|
-
|
|
433
|
+
mfa.show.value = false;
|
|
436
434
|
return { ok: true, result };
|
|
437
435
|
} catch (err) {
|
|
438
436
|
const status = (err as { response?: { status?: number } })?.response?.status;
|
|
439
437
|
if (status === 401) {
|
|
440
|
-
|
|
438
|
+
mfa.error.value = shell.value.mfa.invalidCode;
|
|
441
439
|
continue;
|
|
442
440
|
}
|
|
443
|
-
|
|
441
|
+
mfa.show.value = false;
|
|
444
442
|
notify('negative', errMsg(err));
|
|
445
443
|
return { ok: false };
|
|
446
444
|
}
|
|
447
445
|
}
|
|
448
446
|
}
|
|
449
447
|
|
|
450
|
-
function promptMfa(description: string): Promise<string | null> {
|
|
451
|
-
return new Promise((resolve) => {
|
|
452
|
-
mfaDescription.value = description;
|
|
453
|
-
mfaError.value = '';
|
|
454
|
-
showMfa.value = true;
|
|
455
|
-
pendingMfaResolve = (code) => {
|
|
456
|
-
pendingMfaResolve = null;
|
|
457
|
-
resolve(code);
|
|
458
|
-
};
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function onMfaConfirm(code: string): void {
|
|
463
|
-
pendingMfaResolve?.(code);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function onMfaDialogVisibility(open: boolean): void {
|
|
467
|
-
showMfa.value = open;
|
|
468
|
-
if (!open && pendingMfaResolve) {
|
|
469
|
-
pendingMfaResolve(null);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
448
|
function statusColor(status: EmailHistoryStatus): string {
|
|
474
449
|
switch (status) {
|
|
475
450
|
case 'SENT':
|
|
@@ -521,32 +496,6 @@ function formatTs(iso: string | null | undefined): string {
|
|
|
521
496
|
</script>
|
|
522
497
|
|
|
523
498
|
<style scoped>
|
|
524
|
-
.sa-emh {
|
|
525
|
-
min-height: calc(100vh - 56px);
|
|
526
|
-
background: var(--sa-bg-app, #f1f5f9);
|
|
527
|
-
}
|
|
528
|
-
.sa-emh__head {
|
|
529
|
-
padding: 20px 28px 8px;
|
|
530
|
-
display: flex;
|
|
531
|
-
align-items: flex-start;
|
|
532
|
-
justify-content: space-between;
|
|
533
|
-
gap: 16px;
|
|
534
|
-
}
|
|
535
|
-
.sa-emh__title {
|
|
536
|
-
margin: 0;
|
|
537
|
-
font-family: var(--sa-font-head, system-ui, sans-serif);
|
|
538
|
-
font-weight: 700;
|
|
539
|
-
font-size: 22px;
|
|
540
|
-
color: var(--sa-heading, #0f172a);
|
|
541
|
-
}
|
|
542
|
-
.sa-emh__sub {
|
|
543
|
-
margin: 4px 0 0;
|
|
544
|
-
color: var(--sa-muted-dark, #475569);
|
|
545
|
-
font-size: 13.5px;
|
|
546
|
-
}
|
|
547
|
-
.sa-emh__body {
|
|
548
|
-
padding: 12px 28px 28px;
|
|
549
|
-
}
|
|
550
499
|
.sa-emh__filter {
|
|
551
500
|
display: flex;
|
|
552
501
|
gap: 10px;
|