rimelight-components 2.1.80 → 2.1.82
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/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/page/PagePropertiesEditor.vue +153 -15
- package/dist/runtime/components/page/PageVersionSelector.d.vue.ts +25 -0
- package/dist/runtime/components/page/PageVersionSelector.vue +279 -0
- package/dist/runtime/components/page/PageVersionSelector.vue.d.ts +25 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { readdirSync } from 'node:fs';
|
|
|
4
4
|
import { basename } from 'node:path';
|
|
5
5
|
|
|
6
6
|
const name = "rimelight-components";
|
|
7
|
-
const version = "2.1.
|
|
7
|
+
const version = "2.1.82";
|
|
8
8
|
const homepage = "https://rimelight.com/tools/rimelight-components";
|
|
9
9
|
|
|
10
10
|
const defaultOptions = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed } from "vue";
|
|
2
|
+
import { computed, ref, reactive } from "vue";
|
|
3
3
|
import { useI18n } from "vue-i18n";
|
|
4
4
|
import { usePageRegistry, useInfobox, useRC } from "../../composables";
|
|
5
5
|
import { getLocalizedContent } from "../../utils";
|
|
@@ -61,6 +61,55 @@ const updateTextArray = (schema, vals) => {
|
|
|
61
61
|
en: str
|
|
62
62
|
}));
|
|
63
63
|
};
|
|
64
|
+
const isLinkModalOpen = ref(false);
|
|
65
|
+
const editingLinkIndex = ref(null);
|
|
66
|
+
const linkDraft = reactive({
|
|
67
|
+
label: "",
|
|
68
|
+
to: "",
|
|
69
|
+
icon: "",
|
|
70
|
+
color: "neutral",
|
|
71
|
+
variant: "link"
|
|
72
|
+
});
|
|
73
|
+
const openLinkModal = (index = null) => {
|
|
74
|
+
editingLinkIndex.value = index;
|
|
75
|
+
if (index !== null && page.value.links?.[index]) {
|
|
76
|
+
const link = page.value.links[index];
|
|
77
|
+
linkDraft.label = link.label;
|
|
78
|
+
linkDraft.to = link.to;
|
|
79
|
+
linkDraft.icon = link.icon;
|
|
80
|
+
linkDraft.color = link.color || "neutral";
|
|
81
|
+
linkDraft.variant = link.variant || "link";
|
|
82
|
+
} else {
|
|
83
|
+
linkDraft.label = "";
|
|
84
|
+
linkDraft.to = "";
|
|
85
|
+
linkDraft.icon = "";
|
|
86
|
+
linkDraft.color = "neutral";
|
|
87
|
+
linkDraft.variant = "link";
|
|
88
|
+
}
|
|
89
|
+
isLinkModalOpen.value = true;
|
|
90
|
+
};
|
|
91
|
+
const saveLink = () => {
|
|
92
|
+
if (!linkDraft.label || !linkDraft.to) return;
|
|
93
|
+
if (!page.value.links) page.value.links = [];
|
|
94
|
+
const newLink = {
|
|
95
|
+
label: linkDraft.label,
|
|
96
|
+
to: linkDraft.to,
|
|
97
|
+
icon: linkDraft.icon,
|
|
98
|
+
color: linkDraft.color,
|
|
99
|
+
variant: linkDraft.variant
|
|
100
|
+
};
|
|
101
|
+
if (editingLinkIndex.value !== null) {
|
|
102
|
+
page.value.links[editingLinkIndex.value] = newLink;
|
|
103
|
+
} else {
|
|
104
|
+
page.value.links.push(newLink);
|
|
105
|
+
}
|
|
106
|
+
isLinkModalOpen.value = false;
|
|
107
|
+
};
|
|
108
|
+
const removeLink = (index) => {
|
|
109
|
+
if (page.value.links) {
|
|
110
|
+
page.value.links.splice(index, 1);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
64
113
|
</script>
|
|
65
114
|
|
|
66
115
|
<template>
|
|
@@ -87,6 +136,16 @@ const updateTextArray = (schema, vals) => {
|
|
|
87
136
|
:class="titleInput({ class: rc.titleInput })"
|
|
88
137
|
/>
|
|
89
138
|
|
|
139
|
+
<UInput
|
|
140
|
+
v-model="page.slug"
|
|
141
|
+
variant="subtle"
|
|
142
|
+
placeholder="page-slug"
|
|
143
|
+
size="xs"
|
|
144
|
+
prefix="/"
|
|
145
|
+
:ui="{ base: 'text-center text-dimmed font-mono' }"
|
|
146
|
+
class="w-full opacity-60 hover:opacity-100 focus-within:opacity-100 transition-opacity"
|
|
147
|
+
/>
|
|
148
|
+
|
|
90
149
|
<span :class="type({ class: rc.type })">{{ t(getTypeLabelKey(page.type)) }}</span>
|
|
91
150
|
|
|
92
151
|
<div v-if="page.tags?.length" :class="tags({ class: rc.tags })">
|
|
@@ -221,20 +280,99 @@ const updateTextArray = (schema, vals) => {
|
|
|
221
280
|
</template>
|
|
222
281
|
</UCard>
|
|
223
282
|
<div :class="links({ class: rc.links })">
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
283
|
+
<div class="flex items-center justify-between mb-xs">
|
|
284
|
+
<h6>Links</h6>
|
|
285
|
+
<UButton
|
|
286
|
+
icon="lucide:plus"
|
|
287
|
+
size="xs"
|
|
288
|
+
variant="ghost"
|
|
289
|
+
color="primary"
|
|
290
|
+
@click="openLinkModal()"
|
|
291
|
+
/>
|
|
292
|
+
</div>
|
|
293
|
+
|
|
294
|
+
<div v-if="page.links?.length" class="flex flex-col gap-xs">
|
|
295
|
+
<div
|
|
296
|
+
v-for="(linkItem, index) in page.links"
|
|
297
|
+
:key="index"
|
|
298
|
+
class="flex items-center justify-between group/link"
|
|
299
|
+
>
|
|
300
|
+
<UButton
|
|
301
|
+
:label="linkItem.label"
|
|
302
|
+
:icon="linkItem.icon"
|
|
303
|
+
:to="linkItem.to"
|
|
304
|
+
:target="linkItem.to ? '_blank' : void 0"
|
|
305
|
+
:external="!!linkItem.to"
|
|
306
|
+
:variant="linkItem.variant || 'link'"
|
|
307
|
+
:color="linkItem.color || 'neutral'"
|
|
308
|
+
size="sm"
|
|
309
|
+
:ui="{ base: 'pl-0' }"
|
|
310
|
+
/>
|
|
311
|
+
<div class="flex items-center opacity-0 group-hover/link:opacity-100 transition-opacity">
|
|
312
|
+
<UButton
|
|
313
|
+
icon="lucide:pencil"
|
|
314
|
+
size="xs"
|
|
315
|
+
variant="ghost"
|
|
316
|
+
color="neutral"
|
|
317
|
+
@click="openLinkModal(index)"
|
|
318
|
+
/>
|
|
319
|
+
<UButton
|
|
320
|
+
icon="lucide:trash-2"
|
|
321
|
+
size="xs"
|
|
322
|
+
variant="ghost"
|
|
323
|
+
color="error"
|
|
324
|
+
@click="removeLink(index)"
|
|
325
|
+
/>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
<p v-else class="text-xs text-dimmed italic">No links added yet.</p>
|
|
330
|
+
|
|
331
|
+
<!-- Link management modal -->
|
|
332
|
+
<UModal v-model:open="isLinkModalOpen" :title="editingLinkIndex !== null ? 'Edit Link' : 'Add Link'">
|
|
333
|
+
<template #content>
|
|
334
|
+
<UCard>
|
|
335
|
+
<div class="flex flex-col gap-sm">
|
|
336
|
+
<UFormField label="Label">
|
|
337
|
+
<UInput v-model="linkDraft.label" placeholder="Check my GitHub" class="w-full" />
|
|
338
|
+
</UFormField>
|
|
339
|
+
<UFormField label="URL (to)">
|
|
340
|
+
<UInput v-model="linkDraft.to" placeholder="https://github.com/..." class="w-full" />
|
|
341
|
+
</UFormField>
|
|
342
|
+
<UFormField label="Icon">
|
|
343
|
+
<UInput v-model="linkDraft.icon" placeholder="lucide:github" class="w-full" />
|
|
344
|
+
</UFormField>
|
|
345
|
+
<div class="grid grid-cols-2 gap-sm">
|
|
346
|
+
<UFormField label="Color">
|
|
347
|
+
<USelect
|
|
348
|
+
v-model="linkDraft.color"
|
|
349
|
+
:items="['primary', 'secondary', 'neutral', 'error', 'warning', 'success', 'info']"
|
|
350
|
+
class="w-full"
|
|
351
|
+
/>
|
|
352
|
+
</UFormField>
|
|
353
|
+
<UFormField label="Variant">
|
|
354
|
+
<USelect
|
|
355
|
+
v-model="linkDraft.variant"
|
|
356
|
+
:items="['solid', 'outline', 'subtle', 'soft', 'ghost', 'link']"
|
|
357
|
+
class="w-full"
|
|
358
|
+
/>
|
|
359
|
+
</UFormField>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
<template #footer>
|
|
364
|
+
<div class="flex justify-end gap-sm">
|
|
365
|
+
<UButton label="Cancel" variant="ghost" color="neutral" @click="isLinkModalOpen = false" />
|
|
366
|
+
<UButton
|
|
367
|
+
:label="editingLinkIndex !== null ? 'Update Link' : 'Add Link'"
|
|
368
|
+
color="primary"
|
|
369
|
+
@click="saveLink"
|
|
370
|
+
/>
|
|
371
|
+
</div>
|
|
372
|
+
</template>
|
|
373
|
+
</UCard>
|
|
374
|
+
</template>
|
|
375
|
+
</UModal>
|
|
238
376
|
</div>
|
|
239
377
|
</aside>
|
|
240
378
|
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PageVersion } from "../../types/index.js";
|
|
2
|
+
export interface PageVersionSelectorProps {
|
|
3
|
+
pageId: string;
|
|
4
|
+
currentVersionId?: string | null;
|
|
5
|
+
isAdmin?: boolean;
|
|
6
|
+
rc?: {
|
|
7
|
+
root?: string;
|
|
8
|
+
button?: string;
|
|
9
|
+
popover?: string;
|
|
10
|
+
versionItem?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<PageVersionSelectorProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"update:currentVersionId": (value: string | null) => any;
|
|
15
|
+
"version-selected": (version: PageVersion) => any;
|
|
16
|
+
"version-approved": (version: PageVersion) => any;
|
|
17
|
+
"version-reverted": (version: PageVersion) => any;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<PageVersionSelectorProps> & Readonly<{
|
|
19
|
+
"onUpdate:currentVersionId"?: ((value: string | null) => any) | undefined;
|
|
20
|
+
"onVersion-selected"?: ((version: PageVersion) => any) | undefined;
|
|
21
|
+
"onVersion-approved"?: ((version: PageVersion) => any) | undefined;
|
|
22
|
+
"onVersion-reverted"?: ((version: PageVersion) => any) | undefined;
|
|
23
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
|
+
declare const _default: typeof __VLS_export;
|
|
25
|
+
export default _default;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { formatDistanceToNow } from "date-fns";
|
|
3
|
+
import { useToast } from "#imports";
|
|
4
|
+
import { computed, ref, watch } from "vue";
|
|
5
|
+
import { useRC, $api } from "../../composables";
|
|
6
|
+
import { useI18n } from "vue-i18n";
|
|
7
|
+
import { tv } from "../../internal/tv";
|
|
8
|
+
const {
|
|
9
|
+
pageId,
|
|
10
|
+
currentVersionId,
|
|
11
|
+
isAdmin = false,
|
|
12
|
+
rc: rcProp
|
|
13
|
+
} = defineProps({
|
|
14
|
+
pageId: { type: String, required: true },
|
|
15
|
+
currentVersionId: { type: [String, null], required: false },
|
|
16
|
+
isAdmin: { type: Boolean, required: false },
|
|
17
|
+
rc: { type: Object, required: false }
|
|
18
|
+
});
|
|
19
|
+
const emit = defineEmits(["update:currentVersionId", "version-selected", "version-approved", "version-reverted"]);
|
|
20
|
+
const { rc } = useRC("PageVersionSelector", rcProp);
|
|
21
|
+
const { t } = useI18n();
|
|
22
|
+
const toast = useToast();
|
|
23
|
+
const versions = ref([]);
|
|
24
|
+
const isLoading = ref(false);
|
|
25
|
+
const isApproving = ref(null);
|
|
26
|
+
const isReverting = ref(null);
|
|
27
|
+
const isOpen = ref(false);
|
|
28
|
+
const pageVersionSelectorStyles = tv({
|
|
29
|
+
slots: {
|
|
30
|
+
root: "",
|
|
31
|
+
button: "",
|
|
32
|
+
popover: "w-80 p-2",
|
|
33
|
+
versionItem: "px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer rounded"
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const {
|
|
37
|
+
root,
|
|
38
|
+
button: buttonClass,
|
|
39
|
+
popover,
|
|
40
|
+
versionItem
|
|
41
|
+
} = pageVersionSelectorStyles();
|
|
42
|
+
const pendingVersions = computed(() => {
|
|
43
|
+
return versions.value.filter((v) => v.status === "pending");
|
|
44
|
+
});
|
|
45
|
+
const selectedVersionId = computed({
|
|
46
|
+
get: () => currentVersionId,
|
|
47
|
+
set: (value) => emit("update:currentVersionId", value || null)
|
|
48
|
+
});
|
|
49
|
+
const fetchVersions = async () => {
|
|
50
|
+
if (!pageId) return;
|
|
51
|
+
isLoading.value = true;
|
|
52
|
+
try {
|
|
53
|
+
versions.value = await $api(`/api/pages/id/${pageId}/versions`);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error("Failed to fetch versions:", error);
|
|
56
|
+
try {
|
|
57
|
+
toast.add({ color: "error", title: "Failed to load versions" });
|
|
58
|
+
} catch (e) {
|
|
59
|
+
}
|
|
60
|
+
} finally {
|
|
61
|
+
isLoading.value = false;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const selectVersion = (version) => {
|
|
65
|
+
selectedVersionId.value = version.id;
|
|
66
|
+
emit("version-selected", version);
|
|
67
|
+
isOpen.value = false;
|
|
68
|
+
};
|
|
69
|
+
const approveVersion = async (version) => {
|
|
70
|
+
if (!isAdmin) return;
|
|
71
|
+
isApproving.value = version.id;
|
|
72
|
+
try {
|
|
73
|
+
const result = await $api(`/api/pages/versions/${version.id}/approve`, {
|
|
74
|
+
method: "POST"
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
toast.add({
|
|
78
|
+
color: "success",
|
|
79
|
+
title: "Version approved successfully",
|
|
80
|
+
description: result?.message || "The page has been updated with the approved version"
|
|
81
|
+
});
|
|
82
|
+
} catch (e) {
|
|
83
|
+
}
|
|
84
|
+
emit("version-approved", version);
|
|
85
|
+
await fetchVersions();
|
|
86
|
+
if (selectedVersionId.value === version.id) {
|
|
87
|
+
selectedVersionId.value = null;
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error("Failed to approve version:", error);
|
|
91
|
+
try {
|
|
92
|
+
toast.add({
|
|
93
|
+
color: "error",
|
|
94
|
+
title: "Failed to approve version",
|
|
95
|
+
description: error.message || "An error occurred"
|
|
96
|
+
});
|
|
97
|
+
} catch (e) {
|
|
98
|
+
}
|
|
99
|
+
} finally {
|
|
100
|
+
isApproving.value = null;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const revertVersion = async (version) => {
|
|
104
|
+
if (!isAdmin) return;
|
|
105
|
+
isReverting.value = version.id;
|
|
106
|
+
try {
|
|
107
|
+
const result = await $api(`/api/pages/versions/${version.id}/revert`, {
|
|
108
|
+
method: "POST"
|
|
109
|
+
});
|
|
110
|
+
try {
|
|
111
|
+
toast.add({
|
|
112
|
+
color: "success",
|
|
113
|
+
title: "Version reverted successfully",
|
|
114
|
+
description: result?.message || "The page has been reverted to this version."
|
|
115
|
+
});
|
|
116
|
+
} catch (e) {
|
|
117
|
+
}
|
|
118
|
+
emit("version-reverted", version);
|
|
119
|
+
await fetchVersions();
|
|
120
|
+
selectedVersionId.value = null;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error("Failed to revert version:", error);
|
|
123
|
+
try {
|
|
124
|
+
toast.add({
|
|
125
|
+
color: "error",
|
|
126
|
+
title: "Failed to revert version",
|
|
127
|
+
description: error.message || "An error occurred"
|
|
128
|
+
});
|
|
129
|
+
} catch (e) {
|
|
130
|
+
}
|
|
131
|
+
} finally {
|
|
132
|
+
isReverting.value = null;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const formatDate = (date) => {
|
|
136
|
+
if (!date) return "";
|
|
137
|
+
const d = typeof date === "string" ? new Date(date) : date;
|
|
138
|
+
return formatDistanceToNow(d, { addSuffix: true });
|
|
139
|
+
};
|
|
140
|
+
const getStatusColor = (status) => {
|
|
141
|
+
switch (status) {
|
|
142
|
+
case "approved":
|
|
143
|
+
return "success";
|
|
144
|
+
case "pending":
|
|
145
|
+
return "warning";
|
|
146
|
+
case "rejected":
|
|
147
|
+
return "error";
|
|
148
|
+
default:
|
|
149
|
+
return "neutral";
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
watch(() => pageId, () => {
|
|
153
|
+
if (pageId) fetchVersions();
|
|
154
|
+
}, { immediate: true });
|
|
155
|
+
</script>
|
|
156
|
+
|
|
157
|
+
<template>
|
|
158
|
+
<UPopover v-model:open="isOpen" :popper="{ placement: 'bottom-start' }" :class="root({ class: rc.root })">
|
|
159
|
+
<UButton
|
|
160
|
+
:loading="isLoading"
|
|
161
|
+
color="neutral"
|
|
162
|
+
icon="lucide:git-branch"
|
|
163
|
+
label="Versions"
|
|
164
|
+
size="xs"
|
|
165
|
+
variant="ghost"
|
|
166
|
+
:class="buttonClass({ class: rc.button })"
|
|
167
|
+
/>
|
|
168
|
+
|
|
169
|
+
<template #content>
|
|
170
|
+
<div :class="popover({ class: rc.popover })">
|
|
171
|
+
<div class="px-3 py-2 border-b border-gray-200 dark:border-gray-800">
|
|
172
|
+
<h3 class="text-sm font-semibold">Page Versions</h3>
|
|
173
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">View and manage page versions</p>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="max-h-96 overflow-y-auto">
|
|
177
|
+
<div
|
|
178
|
+
:class="[
|
|
179
|
+
versionItem({ class: rc.versionItem }),
|
|
180
|
+
{ 'bg-primary-50 dark:bg-primary-900/20': !selectedVersionId }
|
|
181
|
+
]"
|
|
182
|
+
@click="selectedVersionId = null;
|
|
183
|
+
isOpen = false"
|
|
184
|
+
>
|
|
185
|
+
<div class="flex items-center justify-between">
|
|
186
|
+
<div class="flex-1">
|
|
187
|
+
<div class="flex items-center gap-2">
|
|
188
|
+
<UBadge color="success" size="xs">Live</UBadge>
|
|
189
|
+
<span class="text-sm font-medium">Current Version</span>
|
|
190
|
+
</div>
|
|
191
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
|
192
|
+
The published version of this page
|
|
193
|
+
</p>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<div v-if="isLoading" class="px-3 py-4 text-center">
|
|
199
|
+
<USkeleton class="h-4 w-full mb-2" />
|
|
200
|
+
<USkeleton class="h-3 w-3/4" />
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<div
|
|
204
|
+
v-else-if="versions.length === 0"
|
|
205
|
+
class="px-3 py-4 text-center text-sm text-gray-500"
|
|
206
|
+
>
|
|
207
|
+
No versions yet
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div v-else class="divide-y divide-gray-200 dark:divide-gray-800">
|
|
211
|
+
<div
|
|
212
|
+
v-for="version in versions"
|
|
213
|
+
:key="version.id"
|
|
214
|
+
:class="[
|
|
215
|
+
versionItem({ class: rc.versionItem }),
|
|
216
|
+
{ 'bg-primary-50 dark:bg-primary-900/20': selectedVersionId === version.id }
|
|
217
|
+
]"
|
|
218
|
+
@click="selectVersion(version)"
|
|
219
|
+
>
|
|
220
|
+
<div class="flex items-start justify-between gap-2">
|
|
221
|
+
<div class="flex-1 min-w-0">
|
|
222
|
+
<div class="flex items-center gap-2 mb-1">
|
|
223
|
+
<UBadge :color="getStatusColor(version.status)" size="xs">
|
|
224
|
+
{{ version.status }}
|
|
225
|
+
</UBadge>
|
|
226
|
+
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
227
|
+
{{ formatDate(version.createdAt) }}
|
|
228
|
+
</span>
|
|
229
|
+
</div>
|
|
230
|
+
<p class="text-xs text-gray-600 dark:text-gray-300 truncate">
|
|
231
|
+
{{ version.title?.en || version.title || "Untitled" }}
|
|
232
|
+
</p>
|
|
233
|
+
<p
|
|
234
|
+
v-if="version.approvedBy && version.approvedAt"
|
|
235
|
+
class="text-xs text-gray-500 dark:text-gray-400 mt-1"
|
|
236
|
+
>
|
|
237
|
+
Approved {{ formatDate(version.approvedAt) }}
|
|
238
|
+
</p>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div v-if="isAdmin" class="shrink-0 flex items-center gap-1">
|
|
242
|
+
<UButton
|
|
243
|
+
v-if="version.status === 'pending'"
|
|
244
|
+
:loading="isApproving === version.id"
|
|
245
|
+
color="success"
|
|
246
|
+
icon="lucide:check"
|
|
247
|
+
size="xs"
|
|
248
|
+
title="Approve version"
|
|
249
|
+
variant="ghost"
|
|
250
|
+
@click.stop="approveVersion(version)"
|
|
251
|
+
/>
|
|
252
|
+
<UButton
|
|
253
|
+
v-if="selectedVersionId === version.id"
|
|
254
|
+
:loading="isReverting === version.id"
|
|
255
|
+
color="warning"
|
|
256
|
+
icon="lucide:rotate-ccw"
|
|
257
|
+
size="xs"
|
|
258
|
+
title="Revert to this version"
|
|
259
|
+
variant="ghost"
|
|
260
|
+
@click.stop="revertVersion(version)"
|
|
261
|
+
/>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<div
|
|
269
|
+
v-if="pendingVersions.length > 0 && isAdmin"
|
|
270
|
+
class="px-3 py-2 border-t border-gray-200 dark:border-gray-800 mt-2"
|
|
271
|
+
>
|
|
272
|
+
<p class="text-xs text-gray-500 dark:text-gray-400">
|
|
273
|
+
{{ pendingVersions.length }} pending version{{ pendingVersions.length !== 1 ? "s" : "" }} awaiting approval
|
|
274
|
+
</p>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
</template>
|
|
278
|
+
</UPopover>
|
|
279
|
+
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PageVersion } from "../../types/index.js";
|
|
2
|
+
export interface PageVersionSelectorProps {
|
|
3
|
+
pageId: string;
|
|
4
|
+
currentVersionId?: string | null;
|
|
5
|
+
isAdmin?: boolean;
|
|
6
|
+
rc?: {
|
|
7
|
+
root?: string;
|
|
8
|
+
button?: string;
|
|
9
|
+
popover?: string;
|
|
10
|
+
versionItem?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<PageVersionSelectorProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"update:currentVersionId": (value: string | null) => any;
|
|
15
|
+
"version-selected": (version: PageVersion) => any;
|
|
16
|
+
"version-approved": (version: PageVersion) => any;
|
|
17
|
+
"version-reverted": (version: PageVersion) => any;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<PageVersionSelectorProps> & Readonly<{
|
|
19
|
+
"onUpdate:currentVersionId"?: ((value: string | null) => any) | undefined;
|
|
20
|
+
"onVersion-selected"?: ((version: PageVersion) => any) | undefined;
|
|
21
|
+
"onVersion-approved"?: ((version: PageVersion) => any) | undefined;
|
|
22
|
+
"onVersion-reverted"?: ((version: PageVersion) => any) | undefined;
|
|
23
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
|
+
declare const _default: typeof __VLS_export;
|
|
25
|
+
export default _default;
|