@saasicat/ui-vue 0.7.0 → 0.9.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/{chunk-A3W2N26K.js → chunk-66BUSVC5.js} +68 -217
- package/dist/{chunk-X5SQ5HMB.js → chunk-TBLXKSTR.js} +0 -137
- package/dist/{chunk-QZOSDCNP.js → chunk-TY2WYKME.js} +1 -1
- package/dist/client/index.cjs +180 -463
- package/dist/client/index.d.cts +32 -57
- package/dist/client/index.d.ts +32 -57
- package/dist/client/index.js +4 -6
- package/dist/index.cjs +273 -580
- package/dist/index.d.cts +10 -55
- package/dist/index.d.ts +10 -55
- package/dist/index.js +76 -101
- package/dist/{messages-7b0P8W75.d.ts → messages-BIYw32u1.d.cts} +0 -67
- package/dist/{messages-7b0P8W75.d.cts → messages-BIYw32u1.d.ts} +0 -67
- package/dist/quasar/index.cjs +0 -136
- 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-B3v3-SWZ.d.ts → use-super-admin-i18n-CWNu_W3u.d.ts} +1 -1
- package/dist/{use-super-admin-i18n-DMktRVEt.d.cts → use-super-admin-i18n-DL_qI1BY.d.cts} +1 -1
- package/package.json +5 -5
- package/src/client/admin-resource-client.ts +112 -0
- package/src/client/i18n/messages/nav.ts +0 -2
- package/src/client/i18n/messages/plan-versions.ts +0 -143
- package/src/client/index.ts +1 -2
- package/src/client/nav-builder.ts +9 -4
- package/src/index.ts +0 -1
- package/src/pages-standard/AdminLayout.vue +1 -1
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +1 -1
- package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +1 -1
- package/src/pages-standard/plan-versions/catalog-history.types.ts +37 -0
- package/src/pages-standard/plan-versions/format.ts +2 -38
- package/src/client/plan-versions-catalog.ts +0 -380
- package/src/pages-standard/PlanVersionsPage.vue +0 -220
- package/src/pages-standard/plan-versions/PlanVersionHeader.vue +0 -309
- package/src/pages-standard/plan-versions/PlanVersionStatusBadge.vue +0 -48
- package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +0 -326
- package/src/pages-standard/plan-versions/PlanVersionsKpi.vue +0 -65
- package/src/pages-standard/plan-versions/PlanVersionsList.vue +0 -315
- package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +0 -261
- package/src/pages-standard/plan-versions/types.ts +0 -33
- package/src/vue/use-plan-versions.ts +0 -83
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="sa-pv">
|
|
3
|
-
<div v-if="loading" class="sa-pv__loading">
|
|
4
|
-
<q-spinner size="32px" /> {{ msg.page.loadingCatalog }}
|
|
5
|
-
</div>
|
|
6
|
-
|
|
7
|
-
<template v-else>
|
|
8
|
-
<PlanVersionsTimeline
|
|
9
|
-
:snapshots="snapshots"
|
|
10
|
-
:selected-id="selectedId"
|
|
11
|
-
:compare-id="compareId"
|
|
12
|
-
@select="onSelect"
|
|
13
|
-
@set-compare="onSetCompare"
|
|
14
|
-
@clear-compare="compareId = null"
|
|
15
|
-
/>
|
|
16
|
-
|
|
17
|
-
<main class="sa-pv__main">
|
|
18
|
-
<PlanVersionHeader
|
|
19
|
-
v-if="selected"
|
|
20
|
-
:snapshot="selected"
|
|
21
|
-
:view-mode="viewMode"
|
|
22
|
-
:compare-snapshot="compare"
|
|
23
|
-
@view-change="viewMode = $event"
|
|
24
|
-
@publish="emit('publish')"
|
|
25
|
-
@clear-compare="compareId = null"
|
|
26
|
-
/>
|
|
27
|
-
|
|
28
|
-
<div class="sa-pv__body">
|
|
29
|
-
<template v-if="selected">
|
|
30
|
-
<PlanVersionsDiff
|
|
31
|
-
v-if="compare"
|
|
32
|
-
:from="compare"
|
|
33
|
-
:to="selected"
|
|
34
|
-
:plan-accents="planAccents"
|
|
35
|
-
:field-labels="diffFieldLabels"
|
|
36
|
-
/>
|
|
37
|
-
<template v-else-if="viewMode === 'list'">
|
|
38
|
-
<slot name="list" :snapshot="selected">
|
|
39
|
-
<PlanVersionsList
|
|
40
|
-
:snapshot="selected"
|
|
41
|
-
:expected-plan-ids="expectedPlanIds"
|
|
42
|
-
:quota-columns="quotaColumns"
|
|
43
|
-
@edit-plan-draft="emit('editPlanDraft', $event)"
|
|
44
|
-
@create-plan-draft="emit('createPlanDraft', $event)"
|
|
45
|
-
/>
|
|
46
|
-
</slot>
|
|
47
|
-
</template>
|
|
48
|
-
<template v-else-if="viewMode === 'matrix'">
|
|
49
|
-
<slot name="matrix" :snapshot="selected">
|
|
50
|
-
<PlanVersionsMatrix
|
|
51
|
-
:snapshot="selected"
|
|
52
|
-
:feature-registry="featureRegistry ?? {}"
|
|
53
|
-
:feature-groups="featureGroups"
|
|
54
|
-
/>
|
|
55
|
-
</slot>
|
|
56
|
-
</template>
|
|
57
|
-
<template v-else>
|
|
58
|
-
<slot name="audit" :snapshot="selected">
|
|
59
|
-
<PlanVersionsAudit
|
|
60
|
-
v-if="loadAudit"
|
|
61
|
-
:snapshot="selected"
|
|
62
|
-
:load-audit="loadAudit"
|
|
63
|
-
:action-meta="auditActionMeta"
|
|
64
|
-
/>
|
|
65
|
-
<div v-else class="sa-pv__no-audit">
|
|
66
|
-
{{ msg.page.noAuditLoader }}
|
|
67
|
-
</div>
|
|
68
|
-
</slot>
|
|
69
|
-
</template>
|
|
70
|
-
</template>
|
|
71
|
-
</div>
|
|
72
|
-
</main>
|
|
73
|
-
</template>
|
|
74
|
-
</div>
|
|
75
|
-
</template>
|
|
76
|
-
|
|
77
|
-
<script setup lang="ts">
|
|
78
|
-
import { computed, ref, watch } from 'vue';
|
|
79
|
-
import type { CatalogSnapshot, RawCatalogData } from '../client/plan-versions-catalog.js';
|
|
80
|
-
import { buildSnapshots } from '../client/plan-versions-catalog.js';
|
|
81
|
-
import PlanVersionsTimeline from './plan-versions/PlanVersionsTimeline.vue';
|
|
82
|
-
import PlanVersionHeader from './plan-versions/PlanVersionHeader.vue';
|
|
83
|
-
import PlanVersionsList, { type QuotaColumnConfig } from './plan-versions/PlanVersionsList.vue';
|
|
84
|
-
import PlanVersionsMatrix from './plan-versions/PlanVersionsMatrix.vue';
|
|
85
|
-
import PlanVersionsAudit from './plan-versions/PlanVersionsAudit.vue';
|
|
86
|
-
import PlanVersionsDiff from './plan-versions/PlanVersionsDiff.vue';
|
|
87
|
-
import type { PlanVersionViewMode } from './plan-versions/types.js';
|
|
88
|
-
import { useSaMessages, useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
|
|
89
|
-
|
|
90
|
-
// Platform standard page: plan versions.
|
|
91
|
-
//
|
|
92
|
-
// Composes the plan-versions subcomponents into the complete page. Consumers
|
|
93
|
-
// pass `data` (RawCatalogData) through and receive events for the app-
|
|
94
|
-
// specific edit/create flows (DraftDialog/PublishModal are intentionally
|
|
95
|
-
// not part of the platform — they hang off the app feature catalog).
|
|
96
|
-
//
|
|
97
|
-
// Slot API for richer app-specific renderings:
|
|
98
|
-
// - `#list`: replaces the default PlanVersionsList (e.g. with feature
|
|
99
|
-
// grouping per plan + additional quota columns).
|
|
100
|
-
// - `#matrix`, `#audit`: analogous.
|
|
101
|
-
|
|
102
|
-
interface FeatureGroupsConfig {
|
|
103
|
-
order: readonly string[];
|
|
104
|
-
byKey: Record<string, string>;
|
|
105
|
-
fallbackGroup?: string;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
interface AuditRow {
|
|
109
|
-
id: string;
|
|
110
|
-
createdAt: string;
|
|
111
|
-
action: string;
|
|
112
|
-
entity: string;
|
|
113
|
-
entityId: string;
|
|
114
|
-
changes: Record<string, unknown> | null;
|
|
115
|
-
user?: { email: string; firstName?: string; lastName?: string } | null;
|
|
116
|
-
userEmail?: string | null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const props = withDefaults(
|
|
120
|
-
defineProps<{
|
|
121
|
-
data: RawCatalogData;
|
|
122
|
-
loading?: boolean;
|
|
123
|
-
/** Plan IDs in display order (e.g. BASIC,STANDARD,PROFESSIONAL,…). */
|
|
124
|
-
planSortOrder?: readonly string[];
|
|
125
|
-
/** Expected plan IDs for the "missing packages" warning. */
|
|
126
|
-
expectedPlanIds?: readonly string[];
|
|
127
|
-
/** Accent color per plan ID (for diff cards). */
|
|
128
|
-
planAccents?: Record<string, string>;
|
|
129
|
-
/** Feature metadata (label/icon/plannedOnly) for the matrix. */
|
|
130
|
-
featureRegistry?: Record<string, { label?: string; icon?: string; plannedOnly?: boolean }>;
|
|
131
|
-
/** UX grouping of the features in the matrix. */
|
|
132
|
-
featureGroups?: FeatureGroupsConfig;
|
|
133
|
-
/** Audit loader (optional; without it disables the audit view). */
|
|
134
|
-
loadAudit?: () => Promise<AuditRow[]>;
|
|
135
|
-
/** App-specific audit action meta map. */
|
|
136
|
-
auditActionMeta?: Record<string, { icon: string; color: string; label: string }>;
|
|
137
|
-
/** Field label overrides for the diff display. */
|
|
138
|
-
diffFieldLabels?: Record<string, string>;
|
|
139
|
-
/**
|
|
140
|
-
* Optional quota columns in the list view (e.g.
|
|
141
|
-
* users/members/storage/seats). Passed through to
|
|
142
|
-
* PlanVersionsList. Default: none — old display (only package/price/features).
|
|
143
|
-
*/
|
|
144
|
-
quotaColumns?: ReadonlyArray<QuotaColumnConfig>;
|
|
145
|
-
}>(),
|
|
146
|
-
{
|
|
147
|
-
loading: false,
|
|
148
|
-
},
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
const emit = defineEmits<{
|
|
152
|
-
(e: 'publish'): void;
|
|
153
|
-
(e: 'editPlanDraft', draftId: string): void;
|
|
154
|
-
(e: 'createPlanDraft', planId: string): void;
|
|
155
|
-
}>();
|
|
156
|
-
|
|
157
|
-
const msg = useSaMessages('planVersions');
|
|
158
|
-
const { locale } = useSuperAdminI18n();
|
|
159
|
-
|
|
160
|
-
const selectedId = ref<string>('drafts');
|
|
161
|
-
const compareId = ref<string | null>(null);
|
|
162
|
-
const viewMode = ref<PlanVersionViewMode>('list');
|
|
163
|
-
|
|
164
|
-
const snapshots = computed<CatalogSnapshot[]>(() =>
|
|
165
|
-
buildSnapshots(props.data, { planSortOrder: props.planSortOrder, locale: locale.value }),
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
const selected = computed<CatalogSnapshot | null>(
|
|
169
|
-
() => snapshots.value.find((s) => s.id === selectedId.value) ?? null,
|
|
170
|
-
);
|
|
171
|
-
const compare = computed<CatalogSnapshot | null>(() =>
|
|
172
|
-
compareId.value ? (snapshots.value.find((s) => s.id === compareId.value) ?? null) : null,
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
watch(snapshots, (snaps) => {
|
|
176
|
-
if (!snaps.find((s) => s.id === selectedId.value)) {
|
|
177
|
-
selectedId.value = snaps[0]?.id ?? 'drafts';
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
function onSelect(id: string): void {
|
|
182
|
-
selectedId.value = id;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function onSetCompare(id: string): void {
|
|
186
|
-
if (id !== selectedId.value) compareId.value = id;
|
|
187
|
-
}
|
|
188
|
-
</script>
|
|
189
|
-
|
|
190
|
-
<style scoped>
|
|
191
|
-
.sa-pv {
|
|
192
|
-
display: flex;
|
|
193
|
-
min-height: calc(100vh - 56px);
|
|
194
|
-
background: var(--sa-bg-app, #f1f5f9);
|
|
195
|
-
min-width: 0;
|
|
196
|
-
}
|
|
197
|
-
.sa-pv__main {
|
|
198
|
-
flex: 1;
|
|
199
|
-
display: flex;
|
|
200
|
-
flex-direction: column;
|
|
201
|
-
min-width: 0;
|
|
202
|
-
overflow: hidden;
|
|
203
|
-
}
|
|
204
|
-
.sa-pv__body {
|
|
205
|
-
flex: 1;
|
|
206
|
-
overflow-y: auto;
|
|
207
|
-
}
|
|
208
|
-
.sa-pv__loading {
|
|
209
|
-
display: flex;
|
|
210
|
-
align-items: center;
|
|
211
|
-
gap: 12px;
|
|
212
|
-
padding: 32px;
|
|
213
|
-
color: var(--sa-muted, #64748b);
|
|
214
|
-
}
|
|
215
|
-
.sa-pv__no-audit {
|
|
216
|
-
padding: 32px;
|
|
217
|
-
color: var(--sa-muted, #64748b);
|
|
218
|
-
font-style: italic;
|
|
219
|
-
}
|
|
220
|
-
</style>
|
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<header class="sa-pv-header">
|
|
3
|
-
<div class="sa-pv-header__row">
|
|
4
|
-
<div class="sa-pv-header__col">
|
|
5
|
-
<div class="sa-pv-header__chips">
|
|
6
|
-
<span class="sa-pv-header__label">{{ snapshot.label }}</span>
|
|
7
|
-
<PlanVersionStatusBadge :status="snapshot.status" />
|
|
8
|
-
<template v-if="compareSnapshot">
|
|
9
|
-
<q-icon name="compare_arrows" size="16px" class="sa-pv-header__cmp-icon" />
|
|
10
|
-
<span class="sa-pv-header__label sa-pv-header__label--cmp">{{
|
|
11
|
-
compareSnapshot.label
|
|
12
|
-
}}</span>
|
|
13
|
-
<button
|
|
14
|
-
type="button"
|
|
15
|
-
class="sa-pv-header__cmp-btn"
|
|
16
|
-
@click="emit('clearCompare')"
|
|
17
|
-
>
|
|
18
|
-
<q-icon name="close" size="14px" /> {{ msg.compareEnd }}
|
|
19
|
-
</button>
|
|
20
|
-
</template>
|
|
21
|
-
</div>
|
|
22
|
-
<h1 class="sa-pv-header__title">{{ snapshot.title }}</h1>
|
|
23
|
-
<p class="sa-pv-header__desc">{{ snapshot.description }}</p>
|
|
24
|
-
|
|
25
|
-
<div class="sa-pv-header__meta">
|
|
26
|
-
<span
|
|
27
|
-
v-if="snapshot.publishedAt"
|
|
28
|
-
class="sa-pv-header__meta-item sa-pv-header__meta-item--ok"
|
|
29
|
-
>
|
|
30
|
-
<q-icon name="rocket_launch" size="14px" />
|
|
31
|
-
{{ publishedOnLabel }}
|
|
32
|
-
</span>
|
|
33
|
-
<span
|
|
34
|
-
v-if="snapshot.kind === 'drafts'"
|
|
35
|
-
class="sa-pv-header__meta-item sa-pv-header__meta-item--warn"
|
|
36
|
-
>
|
|
37
|
-
<q-icon name="edit_note" size="14px" />
|
|
38
|
-
<strong>{{ snapshot.draftCount }}</strong> {{ msg.header.openDrafts }}
|
|
39
|
-
</span>
|
|
40
|
-
<span
|
|
41
|
-
v-if="snapshot.regressionCount > 0"
|
|
42
|
-
class="sa-pv-header__meta-item sa-pv-header__meta-item--bad"
|
|
43
|
-
>
|
|
44
|
-
<q-icon name="trending_down" size="14px" />
|
|
45
|
-
<strong>{{ snapshot.regressionCount }}</strong> {{ regressionLabel }}
|
|
46
|
-
</span>
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
|
|
50
|
-
<div class="sa-pv-header__actions">
|
|
51
|
-
<button
|
|
52
|
-
v-if="canPublish"
|
|
53
|
-
type="button"
|
|
54
|
-
class="sa-pv-header__publish"
|
|
55
|
-
@click="emit('publish')"
|
|
56
|
-
>
|
|
57
|
-
<q-icon name="rocket_launch" size="16px" />
|
|
58
|
-
{{ msg.header.startPublishFlow }}
|
|
59
|
-
</button>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<div class="sa-pv-header__view-row">
|
|
64
|
-
<div
|
|
65
|
-
class="sa-pv-header__switcher"
|
|
66
|
-
:class="{ 'sa-pv-header__switcher--disabled': !!compareSnapshot }"
|
|
67
|
-
>
|
|
68
|
-
<button
|
|
69
|
-
v-for="m in resolvedModes"
|
|
70
|
-
:key="m.id"
|
|
71
|
-
type="button"
|
|
72
|
-
class="sa-pv-header__mode-btn"
|
|
73
|
-
:class="{ 'sa-pv-header__mode-btn--active': viewMode === m.id }"
|
|
74
|
-
@click="emit('viewChange', m.id)"
|
|
75
|
-
>
|
|
76
|
-
<q-icon :name="m.icon" size="14px" />
|
|
77
|
-
{{ m.label }}
|
|
78
|
-
</button>
|
|
79
|
-
</div>
|
|
80
|
-
<span v-if="compareSnapshot" class="sa-pv-header__cmp-hint">
|
|
81
|
-
<q-icon name="compare_arrows" size="13px" />
|
|
82
|
-
{{ msg.header.compareModeHint }}
|
|
83
|
-
</span>
|
|
84
|
-
</div>
|
|
85
|
-
</header>
|
|
86
|
-
</template>
|
|
87
|
-
|
|
88
|
-
<script setup lang="ts">
|
|
89
|
-
import { computed } from 'vue';
|
|
90
|
-
import type { CatalogSnapshot } from '../../client/plan-versions-catalog.js';
|
|
91
|
-
import { formatMessage } from '../../client/i18n/format.js';
|
|
92
|
-
import { useSaMessages, useSuperAdminI18n } from '../../vue/use-super-admin-i18n.js';
|
|
93
|
-
import { formatDate } from './format.js';
|
|
94
|
-
import PlanVersionStatusBadge from './PlanVersionStatusBadge.vue';
|
|
95
|
-
import type { PlanVersionViewMode } from './types.js';
|
|
96
|
-
|
|
97
|
-
const props = defineProps<{
|
|
98
|
-
snapshot: CatalogSnapshot;
|
|
99
|
-
viewMode: PlanVersionViewMode;
|
|
100
|
-
compareSnapshot: CatalogSnapshot | null;
|
|
101
|
-
/** Overrides the localized default view switcher. */
|
|
102
|
-
modes?: ReadonlyArray<{ id: PlanVersionViewMode; label: string; icon: string }>;
|
|
103
|
-
}>();
|
|
104
|
-
|
|
105
|
-
const emit = defineEmits<{
|
|
106
|
-
(e: 'viewChange', mode: PlanVersionViewMode): void;
|
|
107
|
-
(e: 'publish'): void;
|
|
108
|
-
(e: 'clearCompare'): void;
|
|
109
|
-
}>();
|
|
110
|
-
|
|
111
|
-
const msg = useSaMessages('planVersions');
|
|
112
|
-
const { locale } = useSuperAdminI18n();
|
|
113
|
-
|
|
114
|
-
const canPublish = computed(
|
|
115
|
-
() => props.snapshot.kind === 'drafts' && props.snapshot.draftCount > 0,
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
const publishedOnLabel = computed(() =>
|
|
119
|
-
formatMessage(msg.value.header.publishedOn, {
|
|
120
|
-
date: formatDate(props.snapshot.publishedAt ?? undefined, locale.value),
|
|
121
|
-
}),
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const regressionLabel = computed(() =>
|
|
125
|
-
props.snapshot.regressionCount === 1
|
|
126
|
-
? msg.value.header.regressionOne
|
|
127
|
-
: msg.value.header.regressionMany,
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const resolvedModes = computed(
|
|
131
|
-
() =>
|
|
132
|
-
props.modes ?? [
|
|
133
|
-
{ id: 'list' as const, label: msg.value.header.modeList, icon: 'list' },
|
|
134
|
-
{ id: 'matrix' as const, label: msg.value.header.modeMatrix, icon: 'grid_view' },
|
|
135
|
-
{ id: 'audit' as const, label: msg.value.header.modeAudit, icon: 'history' },
|
|
136
|
-
],
|
|
137
|
-
);
|
|
138
|
-
</script>
|
|
139
|
-
|
|
140
|
-
<style scoped>
|
|
141
|
-
.sa-pv-header {
|
|
142
|
-
padding: 20px 28px 18px;
|
|
143
|
-
border-bottom: 1px solid var(--sa-border, #e2e8f0);
|
|
144
|
-
background: var(--sa-bg-surface, #ffffff);
|
|
145
|
-
}
|
|
146
|
-
.sa-pv-header__row {
|
|
147
|
-
display: flex;
|
|
148
|
-
align-items: flex-start;
|
|
149
|
-
gap: 20px;
|
|
150
|
-
}
|
|
151
|
-
.sa-pv-header__col {
|
|
152
|
-
flex: 1;
|
|
153
|
-
min-width: 0;
|
|
154
|
-
}
|
|
155
|
-
.sa-pv-header__chips {
|
|
156
|
-
display: flex;
|
|
157
|
-
align-items: center;
|
|
158
|
-
gap: 10px;
|
|
159
|
-
margin-bottom: 6px;
|
|
160
|
-
flex-wrap: wrap;
|
|
161
|
-
}
|
|
162
|
-
.sa-pv-header__label {
|
|
163
|
-
font-family: var(--sa-font-mono, ui-monospace, monospace);
|
|
164
|
-
font-size: 13px;
|
|
165
|
-
font-weight: 700;
|
|
166
|
-
color: var(--sa-heading, #0f172a);
|
|
167
|
-
background: #f1f5f9;
|
|
168
|
-
padding: 3px 10px;
|
|
169
|
-
border-radius: 6px;
|
|
170
|
-
}
|
|
171
|
-
.sa-pv-header__label--cmp {
|
|
172
|
-
background: #fef3c7;
|
|
173
|
-
}
|
|
174
|
-
.sa-pv-header__cmp-icon {
|
|
175
|
-
color: var(--sa-muted, #64748b);
|
|
176
|
-
}
|
|
177
|
-
.sa-pv-header__cmp-btn {
|
|
178
|
-
background: transparent;
|
|
179
|
-
border: none;
|
|
180
|
-
cursor: pointer;
|
|
181
|
-
color: var(--sa-muted, #64748b);
|
|
182
|
-
font-size: 11px;
|
|
183
|
-
font-weight: 600;
|
|
184
|
-
display: inline-flex;
|
|
185
|
-
align-items: center;
|
|
186
|
-
gap: 4px;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.sa-pv-header__title {
|
|
190
|
-
margin: 0;
|
|
191
|
-
font-family: var(--sa-font-head, system-ui, sans-serif);
|
|
192
|
-
font-weight: 700;
|
|
193
|
-
font-size: 22px;
|
|
194
|
-
color: var(--sa-heading, #0f172a);
|
|
195
|
-
letter-spacing: -0.01em;
|
|
196
|
-
}
|
|
197
|
-
.sa-pv-header__desc {
|
|
198
|
-
margin: 6px 0 0;
|
|
199
|
-
color: var(--sa-muted-dark, #475569);
|
|
200
|
-
font-size: 13.5px;
|
|
201
|
-
line-height: 1.55;
|
|
202
|
-
max-width: 760px;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.sa-pv-header__meta {
|
|
206
|
-
display: flex;
|
|
207
|
-
flex-wrap: wrap;
|
|
208
|
-
gap: 16px;
|
|
209
|
-
margin-top: 14px;
|
|
210
|
-
font-size: 12px;
|
|
211
|
-
color: var(--sa-muted, #64748b);
|
|
212
|
-
}
|
|
213
|
-
.sa-pv-header__meta-item {
|
|
214
|
-
display: inline-flex;
|
|
215
|
-
align-items: center;
|
|
216
|
-
gap: 5px;
|
|
217
|
-
}
|
|
218
|
-
.sa-pv-header__meta-item strong {
|
|
219
|
-
color: var(--sa-body, #1e293b);
|
|
220
|
-
font-weight: 600;
|
|
221
|
-
}
|
|
222
|
-
.sa-pv-header__meta-item--ok :deep(.q-icon) {
|
|
223
|
-
color: var(--sa-positive, #047857);
|
|
224
|
-
}
|
|
225
|
-
.sa-pv-header__meta-item--warn :deep(.q-icon) {
|
|
226
|
-
color: var(--sa-warning, #b45309);
|
|
227
|
-
}
|
|
228
|
-
.sa-pv-header__meta-item--bad :deep(.q-icon) {
|
|
229
|
-
color: var(--sa-negative, #dc2626);
|
|
230
|
-
}
|
|
231
|
-
.sa-pv-header__meta-item--bad strong {
|
|
232
|
-
color: var(--sa-negative, #dc2626);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.sa-pv-header__actions {
|
|
236
|
-
display: flex;
|
|
237
|
-
gap: 8px;
|
|
238
|
-
flex-shrink: 0;
|
|
239
|
-
}
|
|
240
|
-
.sa-pv-header__publish {
|
|
241
|
-
background: var(--sa-primary, #3f6bff);
|
|
242
|
-
color: #fff;
|
|
243
|
-
border: none;
|
|
244
|
-
border-radius: 8px;
|
|
245
|
-
padding: 10px 18px;
|
|
246
|
-
font-size: 13px;
|
|
247
|
-
font-weight: 600;
|
|
248
|
-
cursor: pointer;
|
|
249
|
-
display: inline-flex;
|
|
250
|
-
align-items: center;
|
|
251
|
-
gap: 6px;
|
|
252
|
-
box-shadow: 0 1px 3px rgba(63, 107, 255, 0.3);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.sa-pv-header__view-row {
|
|
256
|
-
display: flex;
|
|
257
|
-
align-items: center;
|
|
258
|
-
gap: 12px;
|
|
259
|
-
margin-top: 18px;
|
|
260
|
-
flex-wrap: wrap;
|
|
261
|
-
}
|
|
262
|
-
.sa-pv-header__switcher {
|
|
263
|
-
display: inline-flex;
|
|
264
|
-
background: #f1f5f9;
|
|
265
|
-
padding: 3px;
|
|
266
|
-
border-radius: 9px;
|
|
267
|
-
}
|
|
268
|
-
.sa-pv-header__switcher--disabled {
|
|
269
|
-
opacity: 0.5;
|
|
270
|
-
pointer-events: none;
|
|
271
|
-
}
|
|
272
|
-
.sa-pv-header__mode-btn {
|
|
273
|
-
background: transparent;
|
|
274
|
-
color: var(--sa-body, #1e293b);
|
|
275
|
-
border: none;
|
|
276
|
-
border-radius: 7px;
|
|
277
|
-
padding: 6px 12px;
|
|
278
|
-
font-size: 12.5px;
|
|
279
|
-
font-weight: 600;
|
|
280
|
-
cursor: pointer;
|
|
281
|
-
display: inline-flex;
|
|
282
|
-
align-items: center;
|
|
283
|
-
gap: 5px;
|
|
284
|
-
}
|
|
285
|
-
.sa-pv-header__mode-btn :deep(.q-icon) {
|
|
286
|
-
color: var(--sa-muted, #64748b);
|
|
287
|
-
}
|
|
288
|
-
.sa-pv-header__mode-btn--active {
|
|
289
|
-
background: #fff;
|
|
290
|
-
color: var(--sa-primary, #3f6bff);
|
|
291
|
-
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.1);
|
|
292
|
-
}
|
|
293
|
-
.sa-pv-header__mode-btn--active :deep(.q-icon) {
|
|
294
|
-
color: var(--sa-primary, #3f6bff);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
.sa-pv-header__cmp-hint {
|
|
298
|
-
font-size: 11.5px;
|
|
299
|
-
font-weight: 600;
|
|
300
|
-
color: var(--sa-warning, #b45309);
|
|
301
|
-
display: inline-flex;
|
|
302
|
-
align-items: center;
|
|
303
|
-
gap: 4px;
|
|
304
|
-
background: var(--sa-warning-soft, rgba(245, 158, 11, 0.1));
|
|
305
|
-
padding: 4px 10px;
|
|
306
|
-
border-radius: 6px;
|
|
307
|
-
border: 1px solid var(--sa-amber-border, rgba(245, 158, 11, 0.3));
|
|
308
|
-
}
|
|
309
|
-
</style>
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<span class="sa-pv-status-badge" :class="`sa-pv-status-badge--${status.toLowerCase()}`">
|
|
3
|
-
<q-icon :name="cfg.icon" size="12px" />
|
|
4
|
-
{{ cfg.label }}
|
|
5
|
-
</span>
|
|
6
|
-
</template>
|
|
7
|
-
|
|
8
|
-
<script setup lang="ts">
|
|
9
|
-
import { computed } from 'vue';
|
|
10
|
-
|
|
11
|
-
type Status = 'DRAFT' | 'ACTIVE' | 'ARCHIVED';
|
|
12
|
-
const props = defineProps<{ status: Status }>();
|
|
13
|
-
|
|
14
|
-
const STATUS_CFG: Record<Status, { label: string; icon: string }> = {
|
|
15
|
-
DRAFT: { label: 'DRAFT', icon: 'edit_note' },
|
|
16
|
-
ACTIVE: { label: 'AKTIV', icon: 'check_circle' },
|
|
17
|
-
ARCHIVED: { label: 'ARCHIV', icon: 'archive' },
|
|
18
|
-
};
|
|
19
|
-
const cfg = computed(() => STATUS_CFG[props.status]);
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
<style scoped>
|
|
23
|
-
.sa-pv-status-badge {
|
|
24
|
-
display: inline-flex;
|
|
25
|
-
align-items: center;
|
|
26
|
-
gap: 4px;
|
|
27
|
-
padding: 3px 9px;
|
|
28
|
-
border-radius: 6px;
|
|
29
|
-
font-size: 10.5px;
|
|
30
|
-
font-weight: 700;
|
|
31
|
-
letter-spacing: 0.06em;
|
|
32
|
-
}
|
|
33
|
-
.sa-pv-status-badge--draft {
|
|
34
|
-
background: #fef3c7;
|
|
35
|
-
color: #92400e;
|
|
36
|
-
}
|
|
37
|
-
.sa-pv-status-badge--active {
|
|
38
|
-
background: #d1fae5;
|
|
39
|
-
color: #065f46;
|
|
40
|
-
}
|
|
41
|
-
.sa-pv-status-badge--archived {
|
|
42
|
-
background: #f1f5f9;
|
|
43
|
-
color: #64748b;
|
|
44
|
-
}
|
|
45
|
-
.sa-pv-status-badge :deep(.q-icon) {
|
|
46
|
-
color: inherit;
|
|
47
|
-
}
|
|
48
|
-
</style>
|