@xleddyl/nuxt-cms 0.1.23 → 0.1.25
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 +16 -3
- package/dist/runtime/app/components/cms/BlocksField.vue +2 -9
- package/dist/runtime/app/components/cms/Button.vue +4 -2
- package/dist/runtime/app/components/cms/ConfirmModal.vue +5 -14
- package/dist/runtime/app/components/cms/Drawer.d.vue.ts +30 -0
- package/dist/runtime/app/components/cms/Drawer.vue +65 -0
- package/dist/runtime/app/components/cms/Drawer.vue.d.ts +30 -0
- package/dist/runtime/app/components/cms/DropdownMenu.d.vue.ts +0 -3
- package/dist/runtime/app/components/cms/DropdownMenu.vue +2 -7
- package/dist/runtime/app/components/cms/DropdownMenu.vue.d.ts +0 -3
- package/dist/runtime/app/components/cms/EmptyState.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/EmptyState.vue +6 -7
- package/dist/runtime/app/components/cms/EmptyState.vue.d.ts +1 -0
- package/dist/runtime/app/components/cms/EntryDrawer.d.vue.ts +21 -0
- package/dist/runtime/app/components/cms/EntryDrawer.vue +193 -0
- package/dist/runtime/app/components/cms/EntryDrawer.vue.d.ts +21 -0
- package/dist/runtime/app/components/cms/EntryForm.vue +19 -7
- package/dist/runtime/app/components/cms/FieldInput.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/FieldInput.vue +13 -27
- package/dist/runtime/app/components/cms/FieldInput.vue.d.ts +1 -0
- package/dist/runtime/app/components/cms/Form.vue +1 -1
- package/dist/runtime/app/components/cms/FormField.d.vue.ts +4 -5
- package/dist/runtime/app/components/cms/FormField.vue +9 -7
- package/dist/runtime/app/components/cms/FormField.vue.d.ts +4 -5
- package/dist/runtime/app/components/cms/JsonField.vue +3 -4
- package/dist/runtime/app/components/cms/LocaleSwitch.d.vue.ts +14 -0
- package/dist/runtime/app/components/cms/LocaleSwitch.vue +29 -0
- package/dist/runtime/app/components/cms/LocaleSwitch.vue.d.ts +14 -0
- package/dist/runtime/app/components/cms/MediaField.vue +7 -17
- package/dist/runtime/app/components/cms/MediaGallery.vue +48 -78
- package/dist/runtime/app/components/cms/MediaThumb.d.vue.ts +6 -0
- package/dist/runtime/app/components/cms/MediaThumb.vue +40 -0
- package/dist/runtime/app/components/cms/MediaThumb.vue.d.ts +6 -0
- package/dist/runtime/app/components/cms/Modal.d.vue.ts +1 -4
- package/dist/runtime/app/components/cms/Modal.vue +8 -7
- package/dist/runtime/app/components/cms/Modal.vue.d.ts +1 -4
- package/dist/runtime/app/components/cms/PageHeader.d.vue.ts +1 -1
- package/dist/runtime/app/components/cms/PageHeader.vue +6 -6
- package/dist/runtime/app/components/cms/PageHeader.vue.d.ts +1 -1
- package/dist/runtime/app/components/cms/RelationField.vue +0 -4
- package/dist/runtime/app/components/cms/SelectMenu.vue +23 -3
- package/dist/runtime/app/components/cms/SlugField.vue +1 -2
- package/dist/runtime/app/components/cms/Spinner.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/Spinner.vue +5 -0
- package/dist/runtime/app/components/cms/Spinner.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/StatusBadge.d.vue.ts +6 -0
- package/dist/runtime/app/components/cms/StatusBadge.vue +11 -0
- package/dist/runtime/app/components/cms/StatusBadge.vue.d.ts +6 -0
- package/dist/runtime/app/components/cms/Table.d.vue.ts +3 -6
- package/dist/runtime/app/components/cms/Table.vue +72 -27
- package/dist/runtime/app/components/cms/Table.vue.d.ts +3 -6
- package/dist/runtime/app/components/cms/Toaster.vue +4 -4
- package/dist/runtime/app/components/cms/TranslatableField.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/TranslatableField.vue +7 -44
- package/dist/runtime/app/components/cms/TranslatableField.vue.d.ts +1 -0
- package/dist/runtime/app/layouts/cms-admin.vue +10 -20
- package/dist/runtime/app/pages/admin-collection.vue +130 -103
- package/dist/runtime/app/pages/admin-entry.vue +6 -24
- package/dist/runtime/app/pages/admin-login.vue +12 -31
- package/dist/runtime/app/pages/admin-media.vue +8 -2
- package/dist/runtime/app/utils/ui.d.ts +0 -7
- package/dist/runtime/app/utils/ui.js +0 -5
- package/dist/runtime/assets/main.css +1 -1
- package/dist/runtime/server/api/collection.get.js +8 -3
- package/dist/runtime/server/utils/relations.d.ts +1 -0
- package/dist/runtime/server/utils/relations.js +25 -0
- package/dist/runtime/shared/index.d.ts +10 -3
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="cms-
|
|
2
|
+
<div class="cms-page" :class="{ 'is-fill': config.kind === 'collection' && !error }">
|
|
3
3
|
<CmsPageHeader :kicker="kicker" :title="config.label">
|
|
4
4
|
<CmsButton
|
|
5
5
|
v-if="config.kind === 'single'"
|
|
@@ -7,29 +7,7 @@
|
|
|
7
7
|
:form="FORM_ID"
|
|
8
8
|
label="Save"
|
|
9
9
|
:loading="saving"
|
|
10
|
-
class="rounded-full px-6"
|
|
11
10
|
/>
|
|
12
|
-
<div v-if="config.kind === 'collection'" class="flex items-center gap-2">
|
|
13
|
-
<CmsDropdownMenu
|
|
14
|
-
v-if="rows.length"
|
|
15
|
-
:items="columnItems"
|
|
16
|
-
:content="{ align: 'end' }"
|
|
17
|
-
:ui="{ content: 'w-48' }"
|
|
18
|
-
>
|
|
19
|
-
<CmsButton
|
|
20
|
-
label="Columns"
|
|
21
|
-
icon="view-columns"
|
|
22
|
-
variant="subtle"
|
|
23
|
-
class="rounded-full px-4"
|
|
24
|
-
/>
|
|
25
|
-
</CmsDropdownMenu>
|
|
26
|
-
<CmsButton
|
|
27
|
-
label="New entry"
|
|
28
|
-
icon="plus"
|
|
29
|
-
class="rounded-full px-4"
|
|
30
|
-
@click="openCreate"
|
|
31
|
-
/>
|
|
32
|
-
</div>
|
|
33
11
|
</CmsPageHeader>
|
|
34
12
|
|
|
35
13
|
<CmsEmptyState v-if="error" icon="exclamation-triangle" title="Could not load entries">
|
|
@@ -37,13 +15,12 @@
|
|
|
37
15
|
label="Retry"
|
|
38
16
|
icon="arrow-path"
|
|
39
17
|
variant="subtle"
|
|
40
|
-
class="mt-3 rounded-full px-4"
|
|
41
18
|
:loading="status === 'pending'"
|
|
42
19
|
@click="reload"
|
|
43
20
|
/>
|
|
44
21
|
</CmsEmptyState>
|
|
45
22
|
|
|
46
|
-
<div v-else-if="config.kind === 'single'" class="cms-card
|
|
23
|
+
<div v-else-if="config.kind === 'single'" class="cms-card cms-panel">
|
|
47
24
|
<CmsEntryForm
|
|
48
25
|
v-model="formState"
|
|
49
26
|
:fields="config.fields"
|
|
@@ -54,53 +31,56 @@
|
|
|
54
31
|
</div>
|
|
55
32
|
|
|
56
33
|
<template v-else>
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@select="onSelect"
|
|
72
|
-
>
|
|
73
|
-
<template v-if="drafts" #status-cell="{ row }">
|
|
74
|
-
<span
|
|
75
|
-
class="cms-badge"
|
|
76
|
-
:class="row.original.status === 'published' ? 'is-published' : 'is-draft'"
|
|
77
|
-
>
|
|
78
|
-
{{ row.original.status === "published" ? "Published" : "Draft" }}
|
|
79
|
-
</span>
|
|
80
|
-
</template>
|
|
81
|
-
<template #actions-cell="{ row }">
|
|
82
|
-
<div class="flex justify-end">
|
|
83
|
-
<CmsButton
|
|
84
|
-
icon="trash"
|
|
85
|
-
variant="ghost"
|
|
86
|
-
color="error"
|
|
87
|
-
size="xs"
|
|
88
|
-
class="rounded-full"
|
|
89
|
-
@click.stop="deleteRow(row.original)"
|
|
90
|
-
/>
|
|
91
|
-
</div>
|
|
92
|
-
</template>
|
|
93
|
-
</CmsTable>
|
|
34
|
+
<div class="cms-toolbar">
|
|
35
|
+
<CmsInput
|
|
36
|
+
v-if="total || searchTerm"
|
|
37
|
+
v-model="search"
|
|
38
|
+
icon="magnifying-glass"
|
|
39
|
+
placeholder="Search…"
|
|
40
|
+
class="flex-1"
|
|
41
|
+
/>
|
|
42
|
+
<div class="cms-toolbar-actions">
|
|
43
|
+
<CmsDropdownMenu v-if="rows.length" :items="columnItems" :content="{ align: 'end' }">
|
|
44
|
+
<CmsButton label="Columns" icon="view-columns" variant="subtle" />
|
|
45
|
+
</CmsDropdownMenu>
|
|
46
|
+
<CmsButton label="New entry" icon="plus" @click="openCreate" />
|
|
47
|
+
</div>
|
|
94
48
|
</div>
|
|
95
49
|
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
50
|
+
<CmsTable
|
|
51
|
+
v-if="rows.length"
|
|
52
|
+
v-model:column-visibility="columnVisibility"
|
|
53
|
+
:data="rows"
|
|
54
|
+
:columns="columns"
|
|
55
|
+
@select="onSelect"
|
|
56
|
+
@reorder="reorderColumns"
|
|
57
|
+
>
|
|
58
|
+
<template v-for="key in mediaKeys" #[`${key}-cell`]="{ row }" :key="key">
|
|
59
|
+
<CmsMediaThumb :value="row.original[key]" />
|
|
60
|
+
</template>
|
|
61
|
+
<template v-if="drafts" #status-cell="{ row }">
|
|
62
|
+
<CmsStatusBadge :published="row.original.status === 'published'" />
|
|
63
|
+
</template>
|
|
64
|
+
<template #actions-cell="{ row }">
|
|
65
|
+
<div class="cms-table-cell-actions">
|
|
66
|
+
<CmsButton
|
|
67
|
+
icon="trash"
|
|
68
|
+
variant="ghost"
|
|
69
|
+
color="error"
|
|
70
|
+
size="xs"
|
|
71
|
+
@click.stop="deleteRow(row.original)"
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</template>
|
|
75
|
+
</CmsTable>
|
|
76
|
+
|
|
77
|
+
<CmsSpinner v-else-if="status === 'pending'" />
|
|
99
78
|
|
|
100
79
|
<CmsEmptyState
|
|
101
80
|
v-else-if="searchTerm"
|
|
102
81
|
icon="magnifying-glass"
|
|
103
82
|
title="No matching entries"
|
|
83
|
+
fill
|
|
104
84
|
/>
|
|
105
85
|
|
|
106
86
|
<CmsEmptyState
|
|
@@ -108,19 +88,21 @@
|
|
|
108
88
|
icon="sparkles"
|
|
109
89
|
title="Nothing here yet"
|
|
110
90
|
body="Entries you create will show up here."
|
|
91
|
+
fill
|
|
111
92
|
>
|
|
112
|
-
<CmsButton
|
|
113
|
-
label="New entry"
|
|
114
|
-
icon="plus"
|
|
115
|
-
variant="subtle"
|
|
116
|
-
class="mt-3 rounded-full px-4"
|
|
117
|
-
@click="openCreate"
|
|
118
|
-
/>
|
|
93
|
+
<CmsButton label="New entry" icon="plus" variant="subtle" @click="openCreate" />
|
|
119
94
|
</CmsEmptyState>
|
|
120
95
|
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
96
|
+
<CmsPagination v-model:page="page" :total="total" :items-per-page="PAGE_SIZE" />
|
|
97
|
+
|
|
98
|
+
<CmsEntryDrawer
|
|
99
|
+
v-model:open="drawerOpen"
|
|
100
|
+
:collection="name"
|
|
101
|
+
:config="config"
|
|
102
|
+
:entry-id="drawerEntryId"
|
|
103
|
+
@saved="refresh"
|
|
104
|
+
@deleted="onDrawerDeleted"
|
|
105
|
+
/>
|
|
124
106
|
</template>
|
|
125
107
|
</div>
|
|
126
108
|
</template>
|
|
@@ -131,7 +113,6 @@ import {
|
|
|
131
113
|
computed,
|
|
132
114
|
createError,
|
|
133
115
|
definePageMeta,
|
|
134
|
-
navigateTo,
|
|
135
116
|
onMounted,
|
|
136
117
|
ref,
|
|
137
118
|
useFetch,
|
|
@@ -159,6 +140,7 @@ if (!config) {
|
|
|
159
140
|
throw createError({ statusCode: 404, statusMessage: "Unknown collection", fatal: true });
|
|
160
141
|
}
|
|
161
142
|
const fieldKeys = Object.keys(config.fields);
|
|
143
|
+
const mediaKeys = fieldKeys.filter((key) => config.fields[key].type === "media");
|
|
162
144
|
const drafts = config.kind === "collection" && !!config.drafts;
|
|
163
145
|
const formKeys = drafts ? [...fieldKeys, "status"] : fieldKeys;
|
|
164
146
|
const endpoint = `/api/cms/admin/${name}`;
|
|
@@ -187,18 +169,13 @@ function isList(value) {
|
|
|
187
169
|
}
|
|
188
170
|
const rows = computed(() => isList(data.value) ? data.value.items : []);
|
|
189
171
|
const total = computed(() => isList(data.value) ? data.value.total : 0);
|
|
172
|
+
const relations = computed(() => isList(data.value) ? data.value.relations ?? {} : {});
|
|
190
173
|
const reload = async () => {
|
|
191
174
|
await refresh();
|
|
192
175
|
if (config.kind === "single" && !error.value) {
|
|
193
176
|
formState.value = data.value && !isList(data.value) ? pickFields(data.value) : emptyState();
|
|
194
177
|
}
|
|
195
178
|
};
|
|
196
|
-
const tableUi = {
|
|
197
|
-
thead: "border-b border-(--cms-line)",
|
|
198
|
-
th: "cms-kicker px-5 py-3 font-normal",
|
|
199
|
-
td: "px-5 py-3.5 text-sm text-(--ui-text)",
|
|
200
|
-
tr: "__clickable hover:bg-(--cms-paper)/50"
|
|
201
|
-
};
|
|
202
179
|
const saving = ref(false);
|
|
203
180
|
const formState = ref({});
|
|
204
181
|
function emptyState() {
|
|
@@ -239,11 +216,22 @@ function stripHtml(html) {
|
|
|
239
216
|
function truncate(value) {
|
|
240
217
|
return value.length > 60 ? `${value.slice(0, 57)}\u2026` : value;
|
|
241
218
|
}
|
|
242
|
-
function
|
|
219
|
+
function localized(value) {
|
|
220
|
+
const record = value;
|
|
221
|
+
return record[contentI18n.defaultLocale] ?? Object.values(record)[0] ?? "";
|
|
222
|
+
}
|
|
223
|
+
function relationLabel(field, key, id) {
|
|
224
|
+
const title = relations.value[key]?.[String(id)];
|
|
225
|
+
if (title == null || title === "") return `#${id}`;
|
|
226
|
+
const target = cmsConfig[field.to];
|
|
227
|
+
const titleField = target?.titleField ? target.fields[target.titleField] : void 0;
|
|
228
|
+
const label = titleField && isTranslatableField(titleField) ? localized(title) : String(title);
|
|
229
|
+
return label || `#${id}`;
|
|
230
|
+
}
|
|
231
|
+
function displayValue(field, key, value) {
|
|
243
232
|
if (value == null || value === "") return "";
|
|
244
233
|
if (isTranslatableField(field)) {
|
|
245
|
-
const
|
|
246
|
-
const raw = record[contentI18n.defaultLocale] ?? Object.values(record)[0] ?? "";
|
|
234
|
+
const raw = localized(value);
|
|
247
235
|
return truncate(field.type === "richtext" ? stripHtml(raw) : raw);
|
|
248
236
|
}
|
|
249
237
|
switch (field.type) {
|
|
@@ -255,43 +243,76 @@ function displayValue(field, value) {
|
|
|
255
243
|
return truncate(JSON.stringify(value));
|
|
256
244
|
case "blocks":
|
|
257
245
|
return `${value.length} \u25A4`;
|
|
246
|
+
case "relation": {
|
|
247
|
+
const ids = Array.isArray(value) ? value : [value];
|
|
248
|
+
if (!ids.length) return "";
|
|
249
|
+
return truncate(ids.map((id) => relationLabel(field, key, id)).join(", "));
|
|
250
|
+
}
|
|
258
251
|
default:
|
|
259
252
|
return truncate(String(value));
|
|
260
253
|
}
|
|
261
254
|
}
|
|
255
|
+
const columnOrder = ref([...fieldKeys]);
|
|
256
|
+
const orderedKeys = computed(() => {
|
|
257
|
+
const known = columnOrder.value.filter((key) => fieldKeys.includes(key));
|
|
258
|
+
return [...known, ...fieldKeys.filter((key) => !known.includes(key))];
|
|
259
|
+
});
|
|
262
260
|
const columns = computed(() => [
|
|
263
|
-
...
|
|
261
|
+
...orderedKeys.value.map((key) => ({
|
|
264
262
|
id: key,
|
|
265
|
-
accessorFn: (row) => displayValue(config.fields[key], row[key]),
|
|
266
|
-
header: config.fields[key].label
|
|
263
|
+
accessorFn: (row) => displayValue(config.fields[key], key, row[key]),
|
|
264
|
+
header: config.fields[key].label,
|
|
265
|
+
reorderable: true
|
|
267
266
|
})),
|
|
268
267
|
...drafts ? [{ accessorKey: "status", header: "Status" }] : [],
|
|
269
268
|
{ id: "actions", header: "" }
|
|
270
269
|
]);
|
|
270
|
+
function reorderColumns(from, to) {
|
|
271
|
+
const next = [...orderedKeys.value];
|
|
272
|
+
const fromIndex = next.indexOf(from);
|
|
273
|
+
const toIndex = next.indexOf(to);
|
|
274
|
+
if (fromIndex === -1 || toIndex === -1) return;
|
|
275
|
+
next.splice(toIndex, 0, ...next.splice(fromIndex, 1));
|
|
276
|
+
columnOrder.value = next;
|
|
277
|
+
}
|
|
271
278
|
const DEFAULT_VISIBLE_COLUMNS = 4;
|
|
272
279
|
const columnStorageKey = `cms:columns:${name}`;
|
|
280
|
+
const columnOrderStorageKey = `cms:column-order:${name}`;
|
|
273
281
|
const columnVisibility = ref(
|
|
274
282
|
Object.fromEntries(fieldKeys.map((key, index) => [key, index < DEFAULT_VISIBLE_COLUMNS]))
|
|
275
283
|
);
|
|
284
|
+
function readStored(key) {
|
|
285
|
+
const stored = localStorage.getItem(key);
|
|
286
|
+
if (!stored) return void 0;
|
|
287
|
+
try {
|
|
288
|
+
return JSON.parse(stored);
|
|
289
|
+
} catch {
|
|
290
|
+
return void 0;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
276
293
|
onMounted(() => {
|
|
277
|
-
const
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
|
|
294
|
+
const storedVisibility = readStored(columnStorageKey);
|
|
295
|
+
if (storedVisibility) {
|
|
296
|
+
columnVisibility.value = Object.fromEntries(
|
|
297
|
+
fieldKeys.map((key) => [
|
|
298
|
+
key,
|
|
299
|
+
storedVisibility[key] ?? columnVisibility.value[key] ?? false
|
|
300
|
+
])
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
const storedOrder = readStored(columnOrderStorageKey);
|
|
304
|
+
if (Array.isArray(storedOrder)) {
|
|
305
|
+
columnOrder.value = storedOrder.filter((key) => typeof key === "string");
|
|
286
306
|
}
|
|
287
307
|
watch(
|
|
288
308
|
columnVisibility,
|
|
289
309
|
(value) => localStorage.setItem(columnStorageKey, JSON.stringify(value)),
|
|
290
310
|
{ deep: true }
|
|
291
311
|
);
|
|
312
|
+
watch(orderedKeys, (value) => localStorage.setItem(columnOrderStorageKey, JSON.stringify(value)));
|
|
292
313
|
});
|
|
293
314
|
const columnItems = computed(
|
|
294
|
-
() =>
|
|
315
|
+
() => orderedKeys.value.map((key) => ({
|
|
295
316
|
label: config.fields[key].label,
|
|
296
317
|
type: "checkbox",
|
|
297
318
|
checked: columnVisibility.value[key],
|
|
@@ -303,14 +324,20 @@ const columnItems = computed(
|
|
|
303
324
|
}
|
|
304
325
|
}))
|
|
305
326
|
);
|
|
306
|
-
const kicker =
|
|
307
|
-
|
|
308
|
-
);
|
|
327
|
+
const kicker = config.kind === "single" ? "single document" : void 0;
|
|
328
|
+
const drawerOpen = ref(false);
|
|
329
|
+
const drawerEntryId = ref(null);
|
|
309
330
|
function onSelect(_event, row) {
|
|
310
|
-
|
|
331
|
+
drawerEntryId.value = String(row.original.id);
|
|
332
|
+
drawerOpen.value = true;
|
|
311
333
|
}
|
|
312
334
|
function openCreate() {
|
|
313
|
-
|
|
335
|
+
drawerEntryId.value = null;
|
|
336
|
+
drawerOpen.value = true;
|
|
337
|
+
}
|
|
338
|
+
async function onDrawerDeleted() {
|
|
339
|
+
if (rows.value.length === 1 && page.value > 1) page.value -= 1;
|
|
340
|
+
else await refresh();
|
|
314
341
|
}
|
|
315
342
|
const confirmAction = useCmsConfirm();
|
|
316
343
|
async function deleteRow(row) {
|
|
@@ -1,42 +1,24 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="cms-
|
|
2
|
+
<div class="cms-page">
|
|
3
3
|
<CmsPageHeader :kicker="config.label" :title="isNew ? 'New entry' : 'Edit entry'">
|
|
4
4
|
<template v-if="drafts" #badge>
|
|
5
|
-
<
|
|
6
|
-
class="cms-badge pointer-events-none"
|
|
7
|
-
:class="published ? 'is-published' : 'is-draft'"
|
|
8
|
-
>
|
|
9
|
-
{{ published ? "Published" : "Draft" }}
|
|
10
|
-
</span>
|
|
5
|
+
<CmsStatusBadge :published="published" />
|
|
11
6
|
</template>
|
|
12
|
-
<div class="
|
|
13
|
-
<CmsButton
|
|
14
|
-
|
|
15
|
-
icon="arrow-left"
|
|
16
|
-
variant="subtle"
|
|
17
|
-
class="rounded-full px-4"
|
|
18
|
-
@click="goBack"
|
|
19
|
-
/>
|
|
20
|
-
<CmsButton
|
|
21
|
-
type="submit"
|
|
22
|
-
:form="FORM_ID"
|
|
23
|
-
label="Save"
|
|
24
|
-
:loading="saving"
|
|
25
|
-
class="rounded-full px-6"
|
|
26
|
-
/>
|
|
7
|
+
<div class="cms-actions">
|
|
8
|
+
<CmsButton label="Back" icon="arrow-left" variant="subtle" @click="goBack" />
|
|
9
|
+
<CmsButton type="submit" :form="FORM_ID" label="Save" :loading="saving" />
|
|
27
10
|
<CmsButton
|
|
28
11
|
v-if="drafts"
|
|
29
12
|
type="submit"
|
|
30
13
|
:form="FORM_ID"
|
|
31
14
|
:label="published ? 'Make draft' : 'Publish'"
|
|
32
15
|
:loading="saving"
|
|
33
|
-
class="rounded-full px-6"
|
|
34
16
|
@click="togglePublished"
|
|
35
17
|
/>
|
|
36
18
|
</div>
|
|
37
19
|
</CmsPageHeader>
|
|
38
20
|
|
|
39
|
-
<div class="cms-card
|
|
21
|
+
<div class="cms-card cms-panel">
|
|
40
22
|
<CmsEntryForm
|
|
41
23
|
v-model="formState"
|
|
42
24
|
:fields="config.fields"
|
|
@@ -1,50 +1,31 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="cms-scope cms-canvas cms-grain
|
|
3
|
-
<div class="
|
|
4
|
-
<header class="cms-
|
|
5
|
-
<h1 class="cms-
|
|
6
|
-
|
|
7
|
-
</h1>
|
|
8
|
-
<p class="text-sm text-(--ui-text-muted)">Sign in to manage your content.</p>
|
|
2
|
+
<div class="cms-scope cms-canvas cms-grain cms-auth">
|
|
3
|
+
<div class="cms-auth-panel">
|
|
4
|
+
<header class="cms-auth-header cms-rise">
|
|
5
|
+
<h1 class="cms-title cms-title-lg">Welcome back<span class="cms-accent">.</span></h1>
|
|
6
|
+
<p class="cms-subtitle">Sign in to manage your content.</p>
|
|
9
7
|
</header>
|
|
10
8
|
|
|
11
|
-
<div class="cms-card cms-
|
|
12
|
-
<CmsForm :state="state"
|
|
13
|
-
<CmsAlert
|
|
14
|
-
|
|
15
|
-
color="error"
|
|
16
|
-
variant="subtle"
|
|
17
|
-
:title="error"
|
|
18
|
-
class="rounded-xl"
|
|
19
|
-
/>
|
|
20
|
-
<CmsFormField label="Email" name="email" required :ui="CMS_FIELD_UI">
|
|
9
|
+
<div class="cms-card cms-auth-card cms-rise is-delayed">
|
|
10
|
+
<CmsForm :state="state" @submit="login">
|
|
11
|
+
<CmsAlert v-if="error" color="error" variant="subtle" :title="error" />
|
|
12
|
+
<CmsFormField label="Email" name="email" required>
|
|
21
13
|
<CmsInput
|
|
22
14
|
v-model="state.email"
|
|
23
15
|
type="email"
|
|
24
|
-
size="lg"
|
|
25
16
|
autocomplete="username"
|
|
26
17
|
placeholder="you@example.com"
|
|
27
|
-
class="w-full"
|
|
28
18
|
/>
|
|
29
19
|
</CmsFormField>
|
|
30
|
-
<CmsFormField label="Password" name="password" required
|
|
20
|
+
<CmsFormField label="Password" name="password" required>
|
|
31
21
|
<CmsInput
|
|
32
22
|
v-model="state.password"
|
|
33
23
|
type="password"
|
|
34
|
-
size="lg"
|
|
35
24
|
autocomplete="current-password"
|
|
36
25
|
placeholder="••••••••"
|
|
37
|
-
class="w-full"
|
|
38
26
|
/>
|
|
39
27
|
</CmsFormField>
|
|
40
|
-
<CmsButton
|
|
41
|
-
type="submit"
|
|
42
|
-
label="Sign in"
|
|
43
|
-
size="lg"
|
|
44
|
-
block
|
|
45
|
-
:loading="loading"
|
|
46
|
-
class="mt-1 rounded-full"
|
|
47
|
-
/>
|
|
28
|
+
<CmsButton type="submit" label="Sign in" block :loading="loading" />
|
|
48
29
|
</CmsForm>
|
|
49
30
|
</div>
|
|
50
31
|
</div>
|
|
@@ -53,7 +34,7 @@
|
|
|
53
34
|
|
|
54
35
|
<script setup>
|
|
55
36
|
import { definePageMeta, navigateTo, ref, useUserSession } from "#imports";
|
|
56
|
-
import {
|
|
37
|
+
import { errorMessage } from "../utils/ui";
|
|
57
38
|
definePageMeta({ layout: false });
|
|
58
39
|
const { loggedIn } = useUserSession();
|
|
59
40
|
if (loggedIn.value) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="cms-
|
|
3
|
-
<CmsPageHeader
|
|
2
|
+
<div class="cms-page">
|
|
3
|
+
<CmsPageHeader title="Media">
|
|
4
|
+
<template #badge>
|
|
5
|
+
<span class="cms-badge is-muted">{{ runtime.mediaStorage }}</span>
|
|
6
|
+
</template>
|
|
7
|
+
</CmsPageHeader>
|
|
4
8
|
|
|
5
9
|
<CmsMediaGallery />
|
|
6
10
|
</div>
|
|
@@ -8,5 +12,7 @@
|
|
|
8
12
|
|
|
9
13
|
<script setup>
|
|
10
14
|
import { definePageMeta } from "#imports";
|
|
15
|
+
import { useCmsRuntime } from "../composables/cms-runtime";
|
|
11
16
|
definePageMeta({ layout: "cms-admin", middleware: "cms-auth" });
|
|
17
|
+
const runtime = useCmsRuntime();
|
|
12
18
|
</script>
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import type { InjectionKey, Reactive } from 'vue';
|
|
2
|
-
export declare const CMS_FIELD_UI: {
|
|
3
|
-
label: string;
|
|
4
|
-
};
|
|
5
|
-
export declare const CMS_MODAL_UI: {
|
|
6
|
-
content: string;
|
|
7
|
-
title: string;
|
|
8
|
-
};
|
|
9
2
|
/** Shared reactive map of field-name -> error message, provided by CmsForm and
|
|
10
3
|
* read by CmsFormField to render inline validation errors. */
|
|
11
4
|
export declare const CMS_FORM_ERRORS: InjectionKey<Reactive<Record<string, string>>>;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
export const CMS_FIELD_UI = { label: "text-sm font-medium text-(--ui-text-toned)" };
|
|
2
|
-
export const CMS_MODAL_UI = {
|
|
3
|
-
content: "rounded-2xl sm:max-w-3xl",
|
|
4
|
-
title: "cms-display text-xl font-medium text-(--ui-text-highlighted)"
|
|
5
|
-
};
|
|
6
1
|
export const CMS_FORM_ERRORS = Symbol("cms-form-errors");
|
|
7
2
|
export function errorMessage(error) {
|
|
8
3
|
const err = error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600&family=Hanken+Grotesk:ital,wght@0,300..800;1,300..800&family=Fragment+Mono:ital@0;1&display=swap");@layer theme, base, components, utilities;@import "tailwindcss/theme.css" layer(theme);.cms-scope{@import "tailwindcss/preflight.css" layer(base);@import "tailwindcss/utilities.css" layer(utilities) source(none)}@source "../app";.cms-scope,body:has(.cms-scope){--cms-paper:#f5f5f5;--cms-surface:#fff;--cms-field:#f0f0f0;--cms-ink:#171717;--cms-line:#e5e5e5;--cms-line-strong:#d4d4d4;--cms-fern:#171717;--cms-sage:#ebebeb;--cms-sage-ink:#171717;--cms-shadow-soft:none;color-scheme:light;--ui-text-dimmed:#a3a3a3;--ui-text-muted:#737373;--ui-text-toned:#525252;--ui-text:#404040;--ui-text-highlighted:var(--cms-ink);--ui-text-inverted:#fff;--ui-bg:var(--cms-surface);--ui-bg-muted:var(--cms-paper);--ui-bg-elevated:var(--cms-field);--ui-bg-accented:#e0e0e0;--ui-bg-inverted:var(--cms-ink);--ui-border:var(--cms-line);--ui-border-muted:var(--cms-line);--ui-border-accented:var(--cms-line-strong);--ui-border-inverted:var(--cms-ink);--ui-primary:var(--cms-ink);--ui-error:#dc2626;--ui-radius:0.4rem;color:var(--ui-text);font-family:Hanken Grotesk,ui-sans-serif,system-ui,sans-serif;margin:0;padding:0;-webkit-font-smoothing:antialiased}body:has(.cms-scope) ::-moz-selection{background:var(--cms-sage);color:var(--cms-sage-ink)}body:has(.cms-scope) ::selection{background:var(--cms-sage);color:var(--cms-sage-ink)}.cms-display{font-family:Poppins,ui-sans-serif,system-ui,sans-serif;letter-spacing:-.02em}.cms-kicker{color:var(--ui-text-dimmed);font-family:Fragment Mono,ui-monospace,monospace;font-size:.875rem;letter-spacing:.16em;line-height:1.4;text-transform:uppercase}.cms-scope thead .cms-kicker{font-size:.6875rem;letter-spacing:.1em}body:has(.cms-scope) :is(button,a,[role=button],[role=menuitem],[role=option],.__clickable):not(:disabled,[aria-disabled=true]){@apply cursor-pointer transition-all duration-200 select-none}body:has(.cms-scope) :is(button,a,[role=button],[role=menuitem],[role=option],.__clickable):not(:disabled,[aria-disabled=true]):active{@apply transition-none;scale:.99}.cms-canvas{background-color:var(--cms-paper)}.cms-grain:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='a'%3E%3CfeTurbulence baseFrequency='.8' numOctaves='2' stitchTiles='stitch' type='fractalNoise'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");content:"";inset:0;mix-blend-mode:multiply;opacity:.035;pointer-events:none;position:fixed;z-index:50}.cms-card{background:var(--cms-surface);border:1px solid var(--cms-line);border-radius:1rem;box-shadow:var(--cms-shadow-soft)}.cms-empty{background:rgba(253,252,250,.5);border:1.5px dashed var(--cms-line-strong);border-radius:1rem}.cms-navlink{align-items:center;border-radius:.75rem;color:var(--ui-text-toned);display:flex;font-size:.875rem;font-weight:500;gap:.625rem;padding:.5rem .75rem;transition:background-color .15s ease,color .15s ease}.cms-navlink:hover{background:rgba(38,33,25,.05);color:var(--ui-text-highlighted)}.cms-navlink.is-active{background:var(--cms-sage);color:var(--cms-sage-ink);font-weight:600}.cms-navlink.is-active .cms-navlink-icon{color:var(--cms-fern)}.cms-badge{border:1px solid transparent;border-radius:9999px;font-family:Fragment Mono,ui-monospace,monospace;font-size:.6875rem;letter-spacing:.1em;padding:.1875rem .5rem;text-transform:uppercase;transition:background-color .15s ease,border-color .15s ease}.cms-badge.is-published{background:var(--cms-sage);color:var(--cms-sage-ink)}.cms-badge.is-draft{background:transparent;border:1px dashed var(--cms-line-strong);color:var(--ui-text-muted)}.cms-dropzone{align-items:center;background:rgba(253,252,250,.5);border:1.5px dashed var(--cms-line-strong);border-radius:1rem;color:var(--ui-text-muted);cursor:pointer;display:flex;flex-direction:column;gap:.5rem;justify-content:center;padding:2rem 1.5rem;transition:border-color .15s ease,background-color .15s ease,color .15s ease;width:100%}.cms-dropzone.is-over,.cms-dropzone:hover{background:var(--cms-sage);border-color:var(--cms-fern);color:var(--cms-sage-ink)}.cms-dropzone.is-dense{border-radius:.75rem;flex-direction:row;gap:.625rem;padding:.875rem 1.25rem}.cms-dropzone.is-busy{opacity:.7;pointer-events:none}.cms-pill{background:var(--cms-surface);border:1px solid var(--cms-line);border-radius:9999px;color:var(--ui-text-dimmed);cursor:pointer;font-family:Fragment Mono,ui-monospace,monospace;font-size:.875rem;letter-spacing:.12em;padding:.3rem .8rem;transition:background-color .15s ease,color .15s ease}.cms-pill:hover{background:rgba(38,33,25,.05);color:var(--ui-text)}.cms-pill.is-active{background:var(--cms-sage);color:var(--cms-sage-ink)}@keyframes cms-rise{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}.cms-rise{animation:cms-rise .55s cubic-bezier(.22,1,.36,1) both}@media (prefers-reduced-motion:reduce){.cms-rise{animation:none}}.cms-richtext{border:1px solid var(--cms-line);border-radius:.75rem;overflow:hidden}.cms-richtext-toolbar{align-items:center;border-bottom:1px solid var(--cms-line);display:flex;flex-wrap:wrap;gap:2px;padding:4px}.cms-richtext-body{font-size:.875rem;min-height:9rem;outline:none;padding:.75rem 1rem}.cms-richtext-body h2{font-size:1.25rem;font-weight:600;margin:.75em 0 .35em}.cms-richtext-body h3{font-size:1.1rem;font-weight:600;margin:.75em 0 .35em}.cms-richtext-body p{margin:.35em 0}.cms-richtext-body ul{list-style:disc;padding-left:1.25rem}.cms-richtext-body ol{list-style:decimal;padding-left:1.25rem}.cms-richtext-body blockquote{border-left:3px solid var(--cms-line);color:var(--ui-text-muted);padding-left:.75rem}.cms-btn{align-items:center;border:1px solid transparent;border-radius:var(--ui-radius);display:inline-flex;font-weight:500;gap:.375rem;justify-content:space-between;line-height:1;transition:background-color .15s ease,border-color .15s ease,color .15s ease,opacity .15s ease;white-space:nowrap}.cms-btn:disabled,.cms-btn[aria-disabled=true]{opacity:.5;pointer-events:none}.cms-btn.is-block{width:100%}.cms-btn-xs{font-size:.875rem;padding:.25rem .5rem}.cms-btn-sm{font-size:.875rem;padding:.375rem .625rem}.cms-btn-md{font-size:.875rem;padding:.5rem .875rem}.cms-btn-lg{font-size:.9375rem;padding:.625rem 1rem}.cms-btn-primary.cms-btn-solid{background:var(--ui-primary);color:var(--ui-text-inverted)}.cms-btn-primary.cms-btn-solid:hover{background:color-mix(in oklab,var(--ui-primary) 88%,#000)}.cms-btn-primary.cms-btn-subtle{background:var(--cms-sage);color:var(--cms-sage-ink)}.cms-btn-primary.cms-btn-subtle:hover{background:#e0e0e0}.cms-btn-primary.cms-btn-soft{background:var(--cms-sage);color:var(--cms-sage-ink)}.cms-btn-neutral.cms-btn-solid{background:var(--ui-bg-inverted);color:var(--ui-text-inverted)}.cms-btn-neutral.cms-btn-subtle{background:var(--ui-bg-elevated);color:var(--ui-text-highlighted)}.cms-btn-neutral.cms-btn-subtle:hover{background:var(--ui-bg-accented)}.cms-btn-neutral.cms-btn-soft{background:var(--ui-bg-elevated);color:var(--ui-text-highlighted)}.cms-btn-neutral.cms-btn-ghost{color:var(--ui-text-toned)}.cms-btn-neutral.cms-btn-ghost:hover{background:rgba(38,33,25,.06);color:var(--ui-text-highlighted)}.cms-btn-error.cms-btn-solid{background:var(--ui-error);color:#fff}.cms-btn-error.cms-btn-solid:hover{background:color-mix(in oklab,var(--ui-error) 88%,#000)}.cms-btn-error.cms-btn-subtle{background:color-mix(in oklab,var(--ui-error) 12%,transparent);color:var(--ui-error)}.cms-btn-error.cms-btn-ghost{color:var(--ui-error)}.cms-btn-error.cms-btn-ghost:hover{background:color-mix(in oklab,var(--ui-error) 10%,transparent)}.cms-btn-success.cms-btn-solid{background:var(--cms-fern);color:var(--ui-text-inverted)}.cms-field{background:var(--ui-bg);border:1px solid var(--cms-line-strong);border-radius:var(--ui-radius);color:var(--ui-text-highlighted);transition:border-color .15s ease,box-shadow .15s ease;width:100%}.cms-field::-moz-placeholder{color:var(--ui-text-dimmed)}.cms-field::placeholder{color:var(--ui-text-dimmed)}.cms-field:focus,.cms-field:focus-within{border-color:var(--ui-primary);box-shadow:0 0 0 3px color-mix(in oklab,var(--ui-primary) 18%,transparent);outline:none}.cms-field.is-error{border-color:var(--ui-error)}.cms-field-sm{font-size:.875rem;padding:.375rem .625rem}.cms-field-md{font-size:.875rem;padding:.5rem .75rem}.cms-field-lg{font-size:.9375rem;padding:.625rem .875rem}.cms-input-wrap{align-items:center;display:flex;position:relative}.cms-input-wrap .cms-field{width:100%}.cms-input-lead,.cms-input-trail{align-items:center;color:var(--ui-text-dimmed);display:flex;position:absolute}.cms-input-lead{left:.625rem}.cms-input-trail{right:.375rem}.cms-input-wrap.has-lead .cms-field{padding-left:2rem}.cms-switch{align-items:center;background:var(--cms-line-strong);border-radius:9999px;display:inline-flex;flex-shrink:0;height:1.25rem;position:relative;transition:background-color .15s ease;width:2.25rem}.cms-switch.is-on{background:var(--ui-primary)}.cms-switch:after{background:#fff;border-radius:9999px;content:"";height:.875rem;left:.1875rem;position:absolute;transition:transform .15s ease;width:.875rem}.cms-switch.is-on:after{transform:translateX(1rem)}.cms-overlay{align-items:center;backdrop-filter:blur(2px);background:rgba(38,33,25,.32);display:flex;inset:0;justify-content:center;overflow-y:auto;padding:2rem 1rem;position:fixed;z-index:60}.cms-modal{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:1rem;box-shadow:var(--cms-shadow-soft);max-width:32rem;width:100%}.cms-modal-header{padding:1.25rem 1.5rem 0}.cms-modal-body{padding:1.25rem 1.5rem 1.5rem}.cms-menu{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:.75rem;box-shadow:var(--cms-shadow-soft);margin-top:.375rem;min-width:11rem;padding:.25rem;position:absolute;z-index:50}.cms-menu.align-end{right:0}.cms-menu-item{align-items:center;border-radius:.5rem;color:var(--ui-text);cursor:pointer;display:flex;font-size:.875rem;gap:.5rem;padding:.4rem .6rem;text-align:left;width:100%}.cms-menu-item:hover{background:var(--ui-bg-elevated)}.cms-menu-check{color:var(--ui-primary);margin-left:auto}.cms-popover,.cms-selectmenu-panel{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:.75rem;box-shadow:var(--cms-shadow-soft);margin-top:.375rem;position:absolute;z-index:50}.cms-selectmenu-panel{left:0;max-height:15rem;overflow-y:auto;padding:.25rem;right:0}.cms-tag{align-items:center;background:var(--ui-bg-elevated);border-radius:9999px;display:inline-flex;font-size:.875rem;gap:.25rem;padding:.125rem .5rem}.cms-table{border-collapse:collapse;text-align:left;width:100%}.cms-alert{border-radius:.75rem;font-size:.875rem;padding:.75rem 1rem}.cms-alert-error{background:color-mix(in oklab,var(--ui-error) 10%,transparent);color:var(--ui-error)}.cms-toaster{bottom:1rem;display:flex;flex-direction:column;gap:.5rem;max-width:calc(100vw - 2rem);position:fixed;right:1rem;width:20rem;z-index:80}.cms-toast{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:.75rem;box-shadow:var(--cms-shadow-soft);padding:.75rem .875rem}.cms-toast.is-success{border-left:3px solid var(--cms-fern)}.cms-toast.is-error{border-left:3px solid var(--ui-error)}.cms-pagination{align-items:center;display:flex;gap:.25rem}.cms-page-btn{border-radius:.5rem;color:var(--ui-text-toned);font-size:.875rem;height:2rem;min-width:2rem;padding:0 .5rem}.cms-page-btn:hover:not(:disabled){background:var(--ui-bg-elevated)}.cms-page-btn.is-active{background:var(--cms-sage);color:var(--cms-sage-ink);font-weight:600}.cms-page-btn:disabled{opacity:.4}
|
|
1
|
+
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600&family=Hanken+Grotesk:ital,wght@0,300..800;1,300..800&family=Fragment+Mono:ital@0;1&display=swap");@layer theme, base, components, utilities;@import "tailwindcss/theme.css" layer(theme) theme(inline);.cms-scope{@import "tailwindcss/preflight.css" layer(base);@import "tailwindcss/utilities.css" layer(utilities) source(none)}@source "../app";.cms-scope,body:has(.cms-scope){--cms-paper:#f5f5f5;--cms-surface:#fff;--cms-field:#f0f0f0;--cms-ink:#171717;--cms-line:#e5e5e5;--cms-line-strong:#d4d4d4;--cms-fern:#171717;--cms-sage:#ebebeb;--cms-sage-ink:#171717;--cms-shadow-soft:none;color-scheme:light;--ui-text-dimmed:#a3a3a3;--ui-text-muted:#737373;--ui-text-toned:#525252;--ui-text:#404040;--ui-text-highlighted:var(--cms-ink);--ui-text-inverted:#fff;--ui-bg:var(--cms-surface);--ui-bg-muted:var(--cms-paper);--ui-bg-elevated:var(--cms-field);--ui-bg-accented:#e0e0e0;--ui-bg-inverted:var(--cms-ink);--ui-border:var(--cms-line);--ui-border-muted:var(--cms-line);--ui-border-accented:var(--cms-line-strong);--ui-border-inverted:var(--cms-ink);--ui-primary:var(--cms-ink);--ui-error:#dc2626;--ui-radius:0.4rem;color:var(--ui-text);font-family:Hanken Grotesk,ui-sans-serif,system-ui,sans-serif;margin:0;padding:0;-webkit-font-smoothing:antialiased}body:has(.cms-scope) ::-moz-selection{background:var(--cms-sage);color:var(--cms-sage-ink)}body:has(.cms-scope) ::selection{background:var(--cms-sage);color:var(--cms-sage-ink)}.cms-display,.cms-title{font-family:Poppins,ui-sans-serif,system-ui,sans-serif;letter-spacing:-.02em}.cms-title{color:var(--ui-text-highlighted);font-weight:500}.cms-title-sm{font-size:1.25rem;line-height:1.4}.cms-title-md{font-size:2rem;line-height:1}.cms-title-lg{font-size:2.25rem;line-height:1.2}.cms-subtitle{color:var(--ui-text-muted);font-size:.875rem}.cms-accent{color:var(--cms-fern)}.cms-kicker{color:var(--ui-text-dimmed);font-family:Fragment Mono,ui-monospace,monospace;font-size:.875rem;letter-spacing:.16em;line-height:1.4;text-transform:uppercase}body:has(.cms-scope) :is(button,a,[role=button],[role=menuitem],[role=option],.__clickable):not(:disabled,[aria-disabled=true]){@apply cursor-pointer transition-all duration-200 select-none}body:has(.cms-scope) :is(button,a,[role=button],[role=menuitem],[role=option],.__clickable):not(:disabled,[aria-disabled=true]):active{@apply transition-none;scale:.99}.cms-canvas{background-color:var(--cms-paper)}.cms-grain:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='a'%3E%3CfeTurbulence baseFrequency='.8' numOctaves='2' stitchTiles='stitch' type='fractalNoise'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");content:"";inset:0;mix-blend-mode:multiply;opacity:.035;pointer-events:none;position:fixed;z-index:50}.cms-card{background:var(--cms-surface);border:1px solid var(--cms-line);border-radius:1rem;box-shadow:var(--cms-shadow-soft)}.cms-panel{padding:1.75rem}.cms-shell{display:flex;min-height:100vh}.cms-sidebar{border-right:1px solid var(--cms-line);display:flex;flex-direction:column;flex-shrink:0;gap:1.75rem;height:100vh;padding:1.75rem 1rem;position:sticky;top:0;width:16rem}.cms-sidebar-brand{color:var(--ui-text-highlighted);font-family:Poppins,ui-sans-serif,system-ui,sans-serif;font-size:1.375rem;font-weight:600;letter-spacing:-.02em;padding-inline:.75rem}.cms-sidebar-nav{display:flex;flex:1;flex-direction:column;gap:1.5rem;overflow-y:auto}.cms-sidebar-group{display:flex;flex-direction:column;gap:.25rem}.cms-sidebar-group>.cms-kicker{margin-bottom:.5rem;padding-inline:.75rem}.cms-sidebar-footer{border-top:1px solid var(--cms-line);display:flex;flex-direction:column;gap:.75rem;padding:1.25rem .75rem 0}.cms-main{flex:1;height:100vh;min-width:0;overflow-y:auto;padding:2.5rem}.cms-main-inner{margin-inline:auto;max-width:64rem;min-height:100%;width:100%}.cms-main-inner,.cms-page{display:flex;flex-direction:column}.cms-page{gap:1.75rem}.cms-page.is-fill{flex:1;min-height:0}.cms-page-header{align-items:flex-end;display:flex;gap:1rem;justify-content:space-between}.cms-page-heading{display:flex;flex-direction:column;gap:.5rem}.cms-toolbar{align-items:center;display:flex;gap:1.5rem}.cms-toolbar-actions{align-items:center;display:flex;flex-shrink:0;gap:.5rem;margin-left:auto}.cms-actions{align-items:center;display:flex;gap:.75rem}.cms-actions.is-end{justify-content:flex-end}.cms-spinner{color:var(--ui-text-dimmed);flex:1;justify-content:center;padding-block:2.5rem}.cms-empty,.cms-spinner{align-items:center;display:flex}.cms-empty{background:rgba(253,252,250,.5);border:1.5px dashed var(--cms-line-strong);border-radius:1rem;flex-direction:column;gap:.5rem;padding:3.5rem;text-align:center}.cms-empty.is-fill{flex:1;justify-content:center}.cms-empty-icon{color:var(--cms-fern);margin-bottom:.25rem}.cms-empty>.cms-btn{margin-top:.75rem}.cms-auth{align-items:center;display:flex;justify-content:center;min-height:100vh;padding:1.5rem}.cms-auth-panel{gap:2rem;max-width:24rem;width:100%}.cms-auth-header,.cms-auth-panel{display:flex;flex-direction:column}.cms-auth-header{align-items:center;gap:.75rem;text-align:center}.cms-auth-card{padding:1.75rem}.cms-navlink{align-items:center;border-radius:.75rem;color:var(--ui-text-toned);display:flex;font-size:.875rem;font-weight:500;gap:.625rem;padding:.5rem .75rem;transition:background-color .15s ease,color .15s ease}.cms-navlink:hover{background:rgba(38,33,25,.05);color:var(--ui-text-highlighted)}.cms-navlink.is-active{background:var(--cms-sage);color:var(--cms-sage-ink);font-weight:600}.cms-navlink-icon{color:var(--ui-text-dimmed)}.cms-navlink.is-active .cms-navlink-icon{color:var(--cms-fern)}.cms-badge{align-items:center;border:1px solid transparent;border-radius:9999px;display:inline-flex;font-family:Fragment Mono,ui-monospace,monospace;font-size:.6875rem;letter-spacing:.1em;padding:.1875rem .5rem;pointer-events:none;text-transform:uppercase;transition:background-color .15s ease,border-color .15s ease;white-space:nowrap}.cms-badge.is-published{background:var(--cms-sage);color:var(--cms-sage-ink)}.cms-badge.is-muted{background:var(--cms-field);border-color:var(--cms-line);color:var(--ui-text-muted)}.cms-badge.is-draft{background:transparent;border:1px dashed var(--cms-line-strong);color:var(--ui-text-muted)}.cms-dropzone{align-items:center;background:rgba(253,252,250,.5);border:1.5px dashed var(--cms-line-strong);border-radius:1rem;color:var(--ui-text-muted);cursor:pointer;display:flex;flex-direction:column;gap:.5rem;justify-content:center;padding:2rem 1.5rem;transition:border-color .15s ease,background-color .15s ease,color .15s ease;width:100%}.cms-dropzone.is-over,.cms-dropzone:hover{background:var(--cms-sage);border-color:var(--cms-fern);color:var(--cms-sage-ink)}.cms-dropzone.is-dense{border-radius:.75rem;flex-direction:row;gap:.625rem;padding:.875rem 1.25rem}.cms-dropzone.is-busy{opacity:.7;pointer-events:none}.cms-pill{align-items:center;background:var(--cms-surface);border:1px solid var(--cms-line);border-radius:9999px;color:var(--ui-text-dimmed);cursor:pointer;display:inline-flex;flex:none;font-family:Fragment Mono,ui-monospace,monospace;font-size:.875rem;gap:.35rem;letter-spacing:.12em;padding:.3rem .8rem;transition:background-color .15s ease,color .15s ease;white-space:nowrap}.cms-pill:hover{background:rgba(38,33,25,.05);color:var(--ui-text)}.cms-pill.is-active{background:var(--cms-sage);color:var(--cms-sage-ink)}@keyframes cms-rise{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}.cms-rise{animation:cms-rise .55s cubic-bezier(.22,1,.36,1) both}.cms-rise.is-delayed{animation-delay:90ms}@media (prefers-reduced-motion:reduce){.cms-rise{animation:none}}.cms-richtext{border:1px solid var(--cms-line);border-radius:.75rem;overflow:hidden}.cms-richtext-toolbar{align-items:center;border-bottom:1px solid var(--cms-line);display:flex;flex-wrap:wrap;gap:2px;padding:4px}.cms-richtext-body{font-size:.875rem;min-height:9rem;outline:none;padding:.75rem 1rem}.cms-richtext-body h2{font-size:1.25rem;font-weight:600;margin:.75em 0 .35em}.cms-richtext-body h3{font-size:1.1rem;font-weight:600;margin:.75em 0 .35em}.cms-richtext-body p{margin:.35em 0}.cms-richtext-body ul{list-style:disc;padding-left:1.25rem}.cms-richtext-body ol{list-style:decimal;padding-left:1.25rem}.cms-richtext-body blockquote{border-left:3px solid var(--cms-line);color:var(--ui-text-muted);padding-left:.75rem}.cms-btn{align-items:center;border:1px solid transparent;border-radius:9999px;display:inline-flex;font-weight:500;gap:.375rem;justify-content:space-between;line-height:1;min-height:var(--cms-btn-size);transition:background-color .15s ease,border-color .15s ease,color .15s ease,opacity .15s ease;white-space:nowrap}.cms-btn:disabled,.cms-btn[aria-disabled=true]{opacity:.5;pointer-events:none}.cms-btn.is-block{width:100%}.cms-btn.is-icon{justify-content:center;padding-inline:0;width:var(--cms-btn-size)}.cms-btn-xs{--cms-btn-size:1.75rem;font-size:.875rem;padding:.25rem .75rem}.cms-btn-sm{--cms-btn-size:2rem;font-size:.875rem;padding:.25rem .875rem}.cms-btn-md{--cms-btn-size:2.25rem;font-size:.875rem;padding:.25rem 1.125rem}.cms-btn-lg{--cms-btn-size:2.625rem;font-size:.9375rem;padding:.375rem 1.375rem}.cms-btn-primary.cms-btn-solid{background:var(--ui-primary);color:var(--ui-text-inverted)}.cms-btn-primary.cms-btn-solid:hover{background:color-mix(in oklab,var(--ui-primary) 88%,#000)}.cms-btn-primary.cms-btn-subtle{background:var(--cms-sage);color:var(--cms-sage-ink)}.cms-btn-primary.cms-btn-subtle:hover{background:#e0e0e0}.cms-btn-primary.cms-btn-soft{background:var(--cms-sage);color:var(--cms-sage-ink)}.cms-btn-neutral.cms-btn-solid{background:var(--ui-bg-inverted);color:var(--ui-text-inverted)}.cms-btn-neutral.cms-btn-subtle{background:var(--ui-bg-elevated);color:var(--ui-text-highlighted)}.cms-btn-neutral.cms-btn-subtle:hover{background:var(--ui-bg-accented)}.cms-btn-neutral.cms-btn-soft{background:var(--ui-bg-elevated);color:var(--ui-text-highlighted)}.cms-btn-neutral.cms-btn-ghost{color:var(--ui-text-toned)}.cms-btn-neutral.cms-btn-ghost:hover{background:rgba(38,33,25,.06);color:var(--ui-text-highlighted)}.cms-btn-error.cms-btn-solid{background:var(--ui-error);color:#fff}.cms-btn-error.cms-btn-solid:hover{background:color-mix(in oklab,var(--ui-error) 88%,#000)}.cms-btn-error.cms-btn-subtle{background:color-mix(in oklab,var(--ui-error) 12%,transparent);color:var(--ui-error)}.cms-btn-error.cms-btn-ghost{color:var(--ui-error)}.cms-btn-error.cms-btn-ghost:hover{background:color-mix(in oklab,var(--ui-error) 10%,transparent)}.cms-btn-success.cms-btn-solid{background:var(--cms-fern);color:var(--ui-text-inverted)}.cms-form{gap:1.25rem}.cms-form,.cms-form-field{display:flex;flex-direction:column}.cms-form-field{gap:.375rem}.cms-form-label-row{align-items:center;display:flex;gap:.75rem;justify-content:space-between;min-height:1.75rem}.cms-form-label{color:var(--ui-text-toned);font-size:.875rem;font-weight:500}.cms-form-error,.cms-form-required{color:var(--ui-error)}.cms-form-error{font-size:.875rem}.cms-form-actions{align-items:center;display:flex;gap:.75rem;justify-content:flex-end;padding-top:.25rem}.cms-field{background:var(--ui-bg);border:1px solid var(--cms-line-strong);border-radius:var(--ui-radius);color:var(--ui-text-highlighted);line-height:1.25;transition:border-color .15s ease,box-shadow .15s ease;width:100%}textarea.cms-field{line-height:1.5;min-height:0;padding-bottom:.625rem;padding-top:.625rem;resize:vertical}.cms-field::-moz-placeholder{color:var(--ui-text-dimmed)}.cms-field::placeholder{color:var(--ui-text-dimmed)}.cms-field:focus,.cms-field:focus-within{border-color:var(--ui-primary);box-shadow:0 0 0 3px color-mix(in oklab,var(--ui-primary) 18%,transparent);outline:none}.cms-field.is-error{border-color:var(--ui-error)}.cms-field-sm{font-size:.875rem;min-height:2rem;padding:.25rem .625rem}.cms-field-md{font-size:.875rem;min-height:2.25rem;padding:.25rem .75rem}.cms-field-lg{font-size:.9375rem;min-height:2.625rem;padding:.375rem .875rem}.cms-select-chevron{align-items:center;cursor:pointer;display:inline-flex;transition:transform .18s ease,color .15s ease}.cms-select-chevron:hover{color:var(--ui-text-muted)}.cms-select-chevron.is-open{transform:rotate(180deg)}.cms-locale-switch{align-items:center;background:var(--cms-paper);border:1px solid var(--cms-line);border-radius:999px;display:inline-flex;gap:2px;padding:2px}.cms-locale-option{border-radius:999px;color:var(--ui-text-muted);cursor:pointer;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.6875rem;letter-spacing:.06em;line-height:1.4;padding:.125rem .5rem;text-transform:uppercase;transition:background-color .15s ease,color .15s ease}.cms-locale-option:hover:not(.is-active){color:var(--ui-text-highlighted)}.cms-locale-option.is-empty{color:var(--ui-text-dimmed)}.cms-locale-option.is-active{background:var(--ui-bg);box-shadow:0 1px 2px rgba(38,33,25,.1);color:var(--ui-text-highlighted)}.cms-input-wrap{align-items:center;display:flex;position:relative;width:100%}.cms-input-wrap .cms-field{width:100%}.cms-input-lead,.cms-input-trail{align-items:center;color:var(--ui-text-dimmed);display:flex;position:absolute}.cms-input-lead{left:.625rem}.cms-input-trail{right:.375rem}.cms-input-wrap.has-lead .cms-field{padding-left:2rem}.cms-switch{align-items:center;background:var(--cms-line-strong);border-radius:9999px;display:inline-flex;flex-shrink:0;height:1.25rem;position:relative;transition:background-color .15s ease;width:2.25rem}.cms-switch.is-on{background:var(--ui-primary)}.cms-switch:after{background:#fff;border-radius:9999px;content:"";height:.875rem;left:.1875rem;position:absolute;transition:transform .15s ease;width:.875rem}.cms-switch.is-on:after{transform:translateX(1rem)}.cms-overlay{align-items:center;backdrop-filter:blur(2px);background:rgba(38,33,25,.32);display:flex;inset:0;justify-content:center;overflow-y:auto;padding:2rem 1rem;position:fixed;z-index:60}.cms-modal{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:1rem;box-shadow:var(--cms-shadow-soft);max-width:32rem;width:100%}.cms-modal.is-sm{max-width:28rem}.cms-modal.is-lg{max-width:48rem}.cms-modal-header{padding:1.25rem 1.5rem 0}.cms-modal-body{padding:1.25rem 1.5rem 1.5rem}.cms-overlay-drawer{align-items:stretch;justify-content:flex-end;overflow:hidden;padding:0;z-index:55}.cms-drawer{background:var(--ui-bg);border-left:1px solid var(--cms-line);box-shadow:-18px 0 48px rgba(38,33,25,.12);display:flex;flex-direction:column;height:100%;max-width:46rem;width:100%}.cms-drawer-header{align-items:center;border-bottom:1px solid var(--cms-line);display:flex;gap:1rem;justify-content:space-between;padding:1.25rem 1.75rem}.cms-drawer-body{flex:1;min-height:0;overflow-y:auto;padding:1.75rem}.cms-drawer-footer{align-items:center;border-top:1px solid var(--cms-line);display:flex;gap:1rem;justify-content:space-between;padding:1.25rem 1.75rem}.cms-drawer-enter-active,.cms-drawer-leave-active{transition:opacity .3s ease}.cms-drawer-enter-active .cms-drawer,.cms-drawer-leave-active .cms-drawer{transition:transform .36s cubic-bezier(.32,.72,0,1);will-change:transform}.cms-drawer-enter-from,.cms-drawer-leave-to{opacity:0}.cms-drawer-enter-from .cms-drawer,.cms-drawer-leave-to .cms-drawer{transform:translateX(100%)}@media (prefers-reduced-motion:reduce){.cms-drawer-enter-active,.cms-drawer-enter-active .cms-drawer,.cms-drawer-leave-active,.cms-drawer-leave-active .cms-drawer{transition-duration:.01ms}}.cms-menu{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:.75rem;box-shadow:var(--cms-shadow-soft);margin-top:.375rem;min-width:12rem;padding:.25rem;position:absolute;z-index:50}.cms-menu.align-end{right:0}.cms-menu-item{align-items:center;border-radius:.5rem;color:var(--ui-text);cursor:pointer;display:flex;font-size:.875rem;gap:.5rem;padding:.4rem .6rem;text-align:left;width:100%}.cms-menu-item:hover{background:var(--ui-bg-elevated)}.cms-menu-check{color:var(--ui-primary);margin-left:auto}.cms-popover{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:.75rem;box-shadow:var(--cms-shadow-soft);margin-top:.375rem;position:absolute;z-index:50}.cms-selectmenu-input{background:transparent;color:var(--ui-text-highlighted);flex:1;min-width:4rem;outline:none}.cms-selectmenu-affix{color:var(--ui-text-dimmed);flex-shrink:0}.cms-selectmenu-panel{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:.75rem;box-shadow:var(--cms-shadow-soft);left:0;margin-top:.375rem;max-height:15rem;overflow-y:auto;padding:.25rem;position:absolute;right:0;z-index:50}.cms-tag{align-items:center;background:var(--ui-bg-elevated);border-radius:9999px;display:inline-flex;font-size:.875rem;gap:.25rem;padding:.125rem .5rem}.cms-media-gallery{display:flex;flex-direction:column;gap:1.25rem}.cms-media-filters{align-items:center;display:flex;flex-wrap:wrap;gap:.25rem}.cms-media-filter-separator{background:var(--cms-line);flex:none;height:1rem;margin-inline:.25rem;width:1px}.cms-media-grid{display:grid;gap:1rem;grid-template-columns:repeat(2,minmax(0,1fr))}@media (min-width:640px){.cms-media-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width:1024px){.cms-media-grid:not(.is-compact){grid-template-columns:repeat(4,minmax(0,1fr))}}.cms-media-tile{overflow:hidden;text-align:left}.cms-media-tile.is-selectable:hover{border-color:var(--cms-line-strong)}.cms-media-preview{align-items:center;aspect-ratio:1;background:var(--cms-field);color:var(--ui-text-dimmed);display:flex;justify-content:center;position:relative}.cms-media-preview>img,.cms-media-preview>video{height:100%;inset:0;-o-object-fit:cover;object-fit:cover;position:absolute;width:100%}.cms-media-actions{display:flex;gap:.25rem;opacity:0;position:absolute;right:.5rem;top:.5rem;transition:opacity .15s ease}.cms-media-actions:focus-within,.cms-media-tile:hover .cms-media-actions{opacity:1}.cms-media-info{background:linear-gradient(0deg,rgba(0,0,0,.7),transparent);bottom:0;color:#fff;font-family:Fragment Mono,ui-monospace,monospace;font-size:.75rem;left:0;letter-spacing:.1em;line-height:1.4;opacity:0;padding:1.5rem .75rem .5rem;pointer-events:none;position:absolute;right:0;text-transform:uppercase;transition:opacity .15s ease}.cms-media-tile:focus-visible .cms-media-info,.cms-media-tile:hover .cms-media-info{opacity:1}.cms-media-meta{display:flex;flex-direction:column;gap:.25rem;padding:.625rem .875rem}.cms-media-name{font-size:.875rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cms-media-preview-large{background:var(--cms-field);max-height:14rem;-o-object-fit:cover;object-fit:cover;width:100%}.cms-media-bar{align-items:center;display:flex;gap:.625rem;padding:.75rem 1rem}.cms-media-bar-icon{color:var(--ui-text-dimmed);flex-shrink:0}.cms-media-bar-name{flex:1;font-size:.875rem;font-weight:500;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cms-media-thumb{background:var(--cms-field);border-radius:.375rem;height:2.25rem;-o-object-fit:cover;object-fit:cover;width:2.25rem}.cms-media-thumb.is-fallback{align-items:center;color:var(--ui-text-dimmed);display:flex;justify-content:center}.cms-media-thumb-empty{color:var(--ui-text-dimmed)}.cms-table-shell{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.cms-table-scroll{flex:1;min-height:0;overflow:auto}.cms-table{border-collapse:collapse;min-width:-moz-max-content;min-width:max-content;text-align:left;width:100%}.cms-table td,.cms-table th{white-space:nowrap}.cms-table thead{background:var(--cms-surface);position:sticky;top:0;z-index:10}.cms-table th{box-shadow:inset 0 -1px 0 var(--cms-line);color:var(--ui-text-dimmed);font-family:Fragment Mono,ui-monospace,monospace;font-size:.6875rem;font-weight:400;letter-spacing:.1em;line-height:1.4;padding:.75rem 1.25rem;text-transform:uppercase}.cms-table td{color:var(--ui-text);font-size:.875rem;padding:.875rem 1.25rem}.cms-table tbody tr{border-bottom:1px solid var(--cms-line)}.cms-table tbody tr:last-child{border-bottom:0}.cms-table tbody tr:hover{background:color-mix(in oklab,var(--cms-paper) 50%,transparent)}.cms-table-cell-actions{display:flex;justify-content:flex-end}.cms-table th[draggable=true]{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.cms-table th[draggable=true]:active{cursor:grabbing}.cms-table th.is-dragging{opacity:.4}.cms-table th.is-drop-target{background:var(--cms-paper);box-shadow:inset 0 -1px 0 var(--cms-line),inset 0 0 0 2px var(--cms-line-strong)}.cms-alert{border-radius:.75rem;font-size:.875rem;padding:.75rem 1rem}.cms-alert-error{background:color-mix(in oklab,var(--ui-error) 10%,transparent);color:var(--ui-error)}.cms-toaster{bottom:1rem;display:flex;flex-direction:column;gap:.5rem;max-width:calc(100vw - 2rem);position:fixed;right:1rem;width:20rem;z-index:80}.cms-toast{background:var(--ui-bg);border:1px solid var(--cms-line);border-radius:.75rem;box-shadow:var(--cms-shadow-soft);padding:.75rem .875rem}.cms-toast-row{align-items:flex-start;display:flex;gap:.75rem}.cms-toast-title{color:var(--ui-text-highlighted);font-size:.875rem;font-weight:500}.cms-toast-description{color:var(--ui-text-muted);font-size:.875rem;margin-top:.125rem}.cms-toast-dismiss{color:var(--ui-text-dimmed);flex-shrink:0}.cms-toast-dismiss:hover{color:var(--ui-text)}.cms-toast.is-success{border-left:3px solid var(--cms-fern)}.cms-toast.is-error{border-left:3px solid var(--ui-error)}.cms-pagination{align-items:center;display:flex;gap:.25rem;justify-content:center}.cms-page-btn{border-radius:.5rem;color:var(--ui-text-toned);font-size:.875rem;height:2rem;min-width:2rem;padding:0 .5rem}.cms-page-btn:hover:not(:disabled){background:var(--ui-bg-elevated)}.cms-page-btn.is-active{background:var(--cms-sage);color:var(--cms-sage-ink);font-weight:600}.cms-page-btn:disabled{opacity:.4}
|
|
@@ -3,7 +3,7 @@ import { defineEventHandler, getValidatedQuery } from "h3";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { useDb } from "#cms-db";
|
|
5
5
|
import { getRegistryEntry, idColumn, tableColumns } from "../utils/registry.js";
|
|
6
|
-
import { attachManyToMany } from "../utils/relations.js";
|
|
6
|
+
import { attachManyToMany, relationTitles } from "../utils/relations.js";
|
|
7
7
|
import { requireAdmin } from "../utils/require-admin.js";
|
|
8
8
|
const querySchema = z.object({
|
|
9
9
|
limit: z.coerce.number().int().min(1).max(100).default(50),
|
|
@@ -40,6 +40,11 @@ export default defineEventHandler(async (event) => {
|
|
|
40
40
|
const base = selection ? db.select(selection).from(table) : db.select().from(table);
|
|
41
41
|
const items = await (where ? base.where(where) : base).orderBy(desc(orderBy)).limit(limit).offset(offset);
|
|
42
42
|
const [counted] = await (where ? db.select({ total: count() }).from(table).where(where) : db.select({ total: count() }).from(table));
|
|
43
|
-
if (
|
|
44
|
-
|
|
43
|
+
if (light) return { items, total: counted?.total ?? 0, relations: {} };
|
|
44
|
+
await attachManyToMany(db, name, entry, items);
|
|
45
|
+
return {
|
|
46
|
+
items,
|
|
47
|
+
total: counted?.total ?? 0,
|
|
48
|
+
relations: await relationTitles(db, entry, items)
|
|
49
|
+
};
|
|
45
50
|
});
|
|
@@ -9,5 +9,6 @@ export declare function splitRelationValues(entry: CmsEntry, body: Row): {
|
|
|
9
9
|
};
|
|
10
10
|
export declare function assertRelationTargets(entry: CmsEntry, lists: Record<string, string[]>): Promise<void>;
|
|
11
11
|
export declare function saveManyToMany(db: CmsDb, name: string, sourceId: string, lists: Record<string, string[]>): Promise<void>;
|
|
12
|
+
export declare function relationTitles(db: CmsDb, entry: CmsEntry, rows: Row[]): Promise<Record<string, Record<string, unknown>>>;
|
|
12
13
|
export declare function attachManyToMany<T extends Row>(db: CmsDb, name: string, entry: CmsEntry, rows: T[]): Promise<T[]>;
|
|
13
14
|
export {};
|