@questpie/admin 3.2.7 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -6
- package/dist/client/blocks/block-renderer.d.mts +2 -2
- package/dist/client/builder/admin-types.d.mts +3 -3
- package/dist/client/builder/types/action-types.d.mts +1 -1
- package/dist/client/builder/types/collection-types.d.mts +59 -2
- package/dist/client/components/blocks/block-editor-provider.mjs +13 -0
- package/dist/client/components/fields/array-field.mjs +105 -122
- package/dist/client/components/fields/asset-preview-field.mjs +1 -1
- package/dist/client/components/fields/blocks-field/blocks-field.mjs +1 -1
- package/dist/client/components/fields/boolean-field.mjs +1 -1
- package/dist/client/components/fields/date-field.mjs +1 -1
- package/dist/client/components/fields/datetime-field.mjs +1 -1
- package/dist/client/components/fields/email-field.mjs +1 -1
- package/dist/client/components/fields/field-wrapper.mjs +44 -15
- package/dist/client/components/fields/number-field.mjs +1 -1
- package/dist/client/components/fields/object-array-field.mjs +179 -149
- package/dist/client/components/fields/object-field.mjs +96 -87
- package/dist/client/components/fields/relation-picker.mjs +1 -1
- package/dist/client/components/fields/relation-select.mjs +1 -1
- package/dist/client/components/fields/rich-text-editor/index.mjs +1 -1
- package/dist/client/components/fields/select-field.mjs +1 -1
- package/dist/client/components/fields/text-field.mjs +1 -1
- package/dist/client/components/fields/textarea-field.mjs +1 -1
- package/dist/client/components/fields/time-field.mjs +1 -1
- package/dist/client/components/fields/upload-field.mjs +1 -1
- package/dist/client/components/history-sidebar.mjs +10 -4
- package/dist/client/components/structured-diff.mjs +367 -0
- package/dist/client/components/ui/sidebar.mjs +1 -1
- package/dist/client/hooks/use-field-options.mjs +34 -15
- package/dist/client/hooks/use-transition-stage.mjs +2 -2
- package/dist/client/modules/admin.d.mts +3 -0
- package/dist/client/modules/admin.mjs +3 -0
- package/dist/client/preview/block-scope-context.d.mts +2 -2
- package/dist/client/preview/preview-banner.d.mts +2 -2
- package/dist/client/preview/preview-field.d.mts +4 -4
- package/dist/client/utils/auto-expand-fields.mjs +1 -1
- package/dist/client/views/collection/auto-form-fields.mjs +23 -19
- package/dist/client/views/collection/cells/complex-cells.mjs +1 -1
- package/dist/client/views/collection/columns/build-columns.mjs +1 -1
- package/dist/client/views/collection/columns/column-defaults.mjs +17 -4
- package/dist/client/views/collection/field-renderer.mjs +19 -7
- package/dist/client/views/collection/form-view.mjs +10 -6
- package/dist/client/views/collection/list-view.mjs +830 -0
- package/dist/client/views/collection/outline.mjs +363 -0
- package/dist/client/views/collection/table-view.mjs +25 -16
- package/dist/client/views/globals/global-form-view.mjs +47 -27
- package/dist/client/views/layout/admin-layout.d.mts +15 -1
- package/dist/client/views/layout/admin-layout.mjs +95 -31
- package/dist/client/views/layout/admin-sidebar.mjs +2 -2
- package/dist/client.d.mts +6 -6
- package/dist/client.mjs +1 -1
- package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
- package/dist/factories.d.mts +19 -0
- package/dist/factories.mjs +11 -0
- package/dist/fields.d.mts +4 -0
- package/dist/fields.mjs +5 -0
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +1 -1
- package/dist/modules/admin.d.mts +10 -0
- package/dist/modules/admin.mjs +9 -0
- package/dist/modules/audit.d.mts +5 -0
- package/dist/modules/audit.mjs +5 -0
- package/dist/server/augmentation/form-layout.d.mts +57 -2
- package/dist/server/augmentation/index.d.mts +3 -1
- package/dist/server/augmentation/shell.d.mts +48 -0
- package/dist/server/augmentation.d.mts +2 -1
- package/dist/server/codegen/admin-client-template.mjs +11 -4
- package/dist/server/fields/blocks.d.mts +9 -2
- package/dist/server/fields/blocks.mjs +1 -1
- package/dist/server/fields/index.d.mts +2 -2
- package/dist/server/fields/index.mjs +2 -2
- package/dist/server/fields/rich-text.d.mts +9 -2
- package/dist/server/fields/rich-text.mjs +1 -1
- package/dist/server/i18n/messages/cs.mjs +8 -0
- package/dist/server/i18n/messages/de.mjs +8 -0
- package/dist/server/i18n/messages/en.mjs +8 -0
- package/dist/server/i18n/messages/es.mjs +8 -0
- package/dist/server/i18n/messages/fr.mjs +8 -0
- package/dist/server/i18n/messages/pl.mjs +8 -0
- package/dist/server/i18n/messages/pt.mjs +8 -0
- package/dist/server/i18n/messages/sk.mjs +8 -0
- package/dist/server/modules/admin/.generated/module.d.mts +24 -19
- package/dist/server/modules/admin/.generated/module.mjs +5 -1
- package/dist/server/modules/admin/.generated/registries.d.mts +6 -4
- package/dist/server/modules/admin/client/.generated/module.d.mts +70 -70
- package/dist/server/modules/admin/client/.generated/module.mjs +3 -1
- package/dist/server/modules/admin/client/views/collection-form.d.mts +6 -0
- package/dist/server/modules/admin/client/views/collection-table.d.mts +6 -0
- package/dist/server/modules/admin/client/views/global-form.d.mts +6 -0
- package/dist/server/modules/admin/client/views/list-view.d.mts +6 -0
- package/dist/server/modules/admin/client/views/list-view.mjs +10 -0
- package/dist/server/modules/admin/collections/account.d.mts +50 -50
- package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
- package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
- package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
- package/dist/server/modules/admin/collections/apikey.d.mts +39 -39
- package/dist/server/modules/admin/collections/assets.d.mts +39 -39
- package/dist/server/modules/admin/collections/session.d.mts +42 -42
- package/dist/server/modules/admin/collections/user.d.mts +63 -63
- package/dist/server/modules/admin/collections/verification.d.mts +36 -36
- package/dist/server/modules/admin/dto/admin-config.dto.mjs +17 -0
- package/dist/server/modules/admin/index.d.mts +30 -31
- package/dist/server/modules/admin/routes/admin-config.d.mts +2 -17
- package/dist/server/modules/admin/routes/admin-config.mjs +21 -5
- package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
- package/dist/server/modules/admin/routes/execute-action.mjs +18 -12
- package/dist/server/modules/admin/routes/i18n-helpers.d.mts +4 -0
- package/dist/server/modules/admin/routes/locales.d.mts +2 -2
- package/dist/server/modules/admin/routes/preview.d.mts +24 -19
- package/dist/server/modules/admin/routes/preview.mjs +83 -62
- package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
- package/dist/server/modules/admin/routes/route-helpers.mjs +36 -1
- package/dist/server/modules/admin/routes/setup.d.mts +7 -14
- package/dist/server/modules/admin/routes/setup.mjs +16 -3
- package/dist/server/modules/admin/routes/translations.d.mts +4 -4
- package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
- package/dist/server/modules/admin/views/list-view.d.mts +8 -0
- package/dist/server/modules/admin/views/list-view.mjs +7 -0
- package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +41 -41
- package/dist/server/modules/audit/.generated/module.d.mts +6 -6
- package/dist/server/modules/audit/collections/audit-log.d.mts +87 -80
- package/dist/server/modules/audit/collections/audit-log.mjs +7 -2
- package/dist/server/modules/audit/config/localize-title.mjs +67 -0
- package/dist/server/modules/audit/index.d.mts +3 -2
- package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +2 -2
- package/dist/server/modules/audit/log-audit-entry.d.mts +85 -0
- package/dist/server/modules/audit/log-audit-entry.mjs +125 -0
- package/dist/server/plugin.d.mts +1 -1
- package/dist/server/plugin.mjs +31 -31
- package/dist/server.d.mts +6 -4
- package/dist/server.mjs +9 -8
- package/dist/shared/preview-utils.d.mts +4 -4
- package/dist/shared/preview-utils.mjs +5 -7
- package/package.json +13 -3
- package/dist/client/hooks/use-audit-history.mjs +0 -38
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
//#region src/client/views/collection/outline.ts
|
|
2
|
+
const DEFAULT_MAX_DEPTH = 12;
|
|
3
|
+
const NO_VALUE = "No value";
|
|
4
|
+
function getId(value) {
|
|
5
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
6
|
+
if (value && typeof value === "object") {
|
|
7
|
+
const id = value.id;
|
|
8
|
+
if (typeof id === "string" || typeof id === "number") return String(id);
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function getPathValue(source, path) {
|
|
13
|
+
if (!path) return source;
|
|
14
|
+
const parts = path.split(".").filter(Boolean);
|
|
15
|
+
let current = source;
|
|
16
|
+
for (const part of parts) {
|
|
17
|
+
if (!current || typeof current !== "object") return void 0;
|
|
18
|
+
current = current[part];
|
|
19
|
+
}
|
|
20
|
+
return current;
|
|
21
|
+
}
|
|
22
|
+
function defaultLabelForValue(value) {
|
|
23
|
+
if (value === null || value === void 0 || value === "") return NO_VALUE;
|
|
24
|
+
if (Array.isArray(value)) return value.length ? value.map((item) => defaultLabelForValue(item)).join(", ") : NO_VALUE;
|
|
25
|
+
if (typeof value === "object") {
|
|
26
|
+
const record = value;
|
|
27
|
+
return String(record.title ?? record.name ?? record.label ?? record.id ?? NO_VALUE);
|
|
28
|
+
}
|
|
29
|
+
return String(value);
|
|
30
|
+
}
|
|
31
|
+
function stableGroupKey(value) {
|
|
32
|
+
if (value === null || value === void 0 || value === "") return "__empty__";
|
|
33
|
+
if (Array.isArray(value)) return value.map(stableGroupKey).join(",");
|
|
34
|
+
if (typeof value === "object") return getId(value) ?? JSON.stringify(value);
|
|
35
|
+
return String(value);
|
|
36
|
+
}
|
|
37
|
+
function shouldExpand(key, depth, outline, collapsedKeys) {
|
|
38
|
+
if (collapsedKeys.has(key)) return false;
|
|
39
|
+
const defaultExpanded = outline?.defaultExpanded ?? true;
|
|
40
|
+
if (defaultExpanded === true) return true;
|
|
41
|
+
if (defaultExpanded === false) return false;
|
|
42
|
+
return depth === 0;
|
|
43
|
+
}
|
|
44
|
+
function compareByOrder(a, b, order) {
|
|
45
|
+
if (Array.isArray(order)) {
|
|
46
|
+
const ai = order.indexOf(a.key);
|
|
47
|
+
const bi = order.indexOf(b.key);
|
|
48
|
+
if (ai !== -1 || bi !== -1) {
|
|
49
|
+
if (ai === -1) return 1;
|
|
50
|
+
if (bi === -1) return -1;
|
|
51
|
+
return ai - bi;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const result = a.label.localeCompare(b.label);
|
|
55
|
+
return order === "desc" ? -result : result;
|
|
56
|
+
}
|
|
57
|
+
function matchesWhere(edge, where) {
|
|
58
|
+
if (!where) return true;
|
|
59
|
+
return Object.entries(where).every(([key, expected]) => {
|
|
60
|
+
const actual = getPathValue(edge, key);
|
|
61
|
+
if (expected && typeof expected === "object" && !Array.isArray(expected)) {
|
|
62
|
+
if ("in" in expected) return (expected.in ?? []).map(String).includes(String(actual));
|
|
63
|
+
}
|
|
64
|
+
return String(actual) === String(expected);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function isRecord(value) {
|
|
68
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
69
|
+
}
|
|
70
|
+
function buildFieldRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
71
|
+
const groups = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const doc of docs) {
|
|
73
|
+
const value = getPathValue(doc, level.field);
|
|
74
|
+
const key = stableGroupKey(value);
|
|
75
|
+
const group = groups.get(key);
|
|
76
|
+
if (group) {
|
|
77
|
+
group.docs.push(doc);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
groups.set(key, {
|
|
81
|
+
key,
|
|
82
|
+
label: ctx.labelForValue(value, level.labelField ?? level.field),
|
|
83
|
+
value,
|
|
84
|
+
docs: [doc]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return Array.from(groups.values()).sort((a, b) => compareByOrder(a, b, level.order)).flatMap((group) => {
|
|
88
|
+
const rowKey = `${scopeKey}/field:${level.field}:${group.key}`;
|
|
89
|
+
const expanded = shouldExpand(rowKey, depth, ctx.outline, ctx.collapsedKeys);
|
|
90
|
+
return [{
|
|
91
|
+
kind: "group",
|
|
92
|
+
key: rowKey,
|
|
93
|
+
label: group.label,
|
|
94
|
+
depth,
|
|
95
|
+
count: group.docs.length,
|
|
96
|
+
expandable: true,
|
|
97
|
+
collapsed: !expanded
|
|
98
|
+
}, ...expanded ? buildLevelRows(group.docs, depth + 1, levelIndex + 1, ctx, rowKey) : []];
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function buildRelationFieldRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
102
|
+
const fieldPath = level.field ? `${level.relation}.${level.field}` : level.relation;
|
|
103
|
+
const groups = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const doc of docs) {
|
|
105
|
+
const relationValue = getPathValue(doc, level.relation);
|
|
106
|
+
const value = level.field ? getPathValue(relationValue, level.field) : relationValue;
|
|
107
|
+
const key = stableGroupKey(value);
|
|
108
|
+
const group = groups.get(key);
|
|
109
|
+
if (group) {
|
|
110
|
+
group.docs.push(doc);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
groups.set(key, {
|
|
114
|
+
key,
|
|
115
|
+
label: ctx.labelForValue(value, level.labelField ?? fieldPath),
|
|
116
|
+
docs: [doc]
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return Array.from(groups.values()).sort((a, b) => compareByOrder(a, b, level.order)).flatMap((group) => {
|
|
120
|
+
const rowKey = `${scopeKey}/relation-field:${fieldPath}:${group.key}`;
|
|
121
|
+
const expanded = shouldExpand(rowKey, depth, ctx.outline, ctx.collapsedKeys);
|
|
122
|
+
return [{
|
|
123
|
+
kind: "group",
|
|
124
|
+
key: rowKey,
|
|
125
|
+
label: group.label,
|
|
126
|
+
depth,
|
|
127
|
+
count: group.docs.length,
|
|
128
|
+
expandable: true,
|
|
129
|
+
collapsed: !expanded
|
|
130
|
+
}, ...expanded ? buildLevelRows(group.docs, depth + 1, levelIndex + 1, ctx, rowKey) : []];
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function getRepeatMaxDepth(repeat, fallback) {
|
|
134
|
+
if (!repeat) return 1;
|
|
135
|
+
if (typeof repeat === "object") return repeat.maxDepth ?? fallback;
|
|
136
|
+
return fallback;
|
|
137
|
+
}
|
|
138
|
+
function buildEdgeRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
139
|
+
const docsById = /* @__PURE__ */ new Map();
|
|
140
|
+
for (const doc of docs) {
|
|
141
|
+
const id = getId(doc);
|
|
142
|
+
if (id) docsById.set(id, doc);
|
|
143
|
+
}
|
|
144
|
+
const edgeDocs = (ctx.edgesByCollection[level.collection] ?? []).filter((edge) => matchesWhere(edge, level.where));
|
|
145
|
+
if (ctx.outline?.preserveMatchingBranches !== false) for (let pass = 0; pass < ctx.maxDepth; pass++) {
|
|
146
|
+
let changed = false;
|
|
147
|
+
for (const edge of edgeDocs) {
|
|
148
|
+
const parentValue = getPathValue(edge, level.parentField);
|
|
149
|
+
const childValue = getPathValue(edge, level.childField);
|
|
150
|
+
const parentId = getId(parentValue);
|
|
151
|
+
const childId = getId(childValue);
|
|
152
|
+
if (!parentId || !childId || !docsById.has(childId)) continue;
|
|
153
|
+
if (docsById.has(parentId) || !isRecord(parentValue)) continue;
|
|
154
|
+
docsById.set(parentId, parentValue);
|
|
155
|
+
changed = true;
|
|
156
|
+
}
|
|
157
|
+
if (!changed) break;
|
|
158
|
+
}
|
|
159
|
+
const outlineDocs = Array.from(docsById.values());
|
|
160
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
161
|
+
const childIds = /* @__PURE__ */ new Set();
|
|
162
|
+
const edgeSeen = /* @__PURE__ */ new Set();
|
|
163
|
+
for (const edge of edgeDocs) {
|
|
164
|
+
const parentId = getId(getPathValue(edge, level.parentField));
|
|
165
|
+
const childId = getId(getPathValue(edge, level.childField));
|
|
166
|
+
if (!parentId || !childId || parentId === childId) continue;
|
|
167
|
+
if (!docsById.has(childId)) continue;
|
|
168
|
+
const dedupeKey = `${parentId}:${childId}:${JSON.stringify(level.where ?? {})}`;
|
|
169
|
+
if (edgeSeen.has(dedupeKey)) continue;
|
|
170
|
+
edgeSeen.add(dedupeKey);
|
|
171
|
+
childIds.add(childId);
|
|
172
|
+
const children = childrenByParent.get(parentId) ?? [];
|
|
173
|
+
children.push({
|
|
174
|
+
childId,
|
|
175
|
+
edge
|
|
176
|
+
});
|
|
177
|
+
childrenByParent.set(parentId, children);
|
|
178
|
+
}
|
|
179
|
+
const roots = outlineDocs.filter((doc) => {
|
|
180
|
+
const id = getId(doc);
|
|
181
|
+
return !id || !childIds.has(id) || !docsById.has(id);
|
|
182
|
+
});
|
|
183
|
+
const maxRepeatDepth = getRepeatMaxDepth(level.repeat, ctx.maxDepth);
|
|
184
|
+
const rows = [];
|
|
185
|
+
const visited = /* @__PURE__ */ new Set();
|
|
186
|
+
function pushChildRows(parentId, nextDepth, branchDepth, ancestorIds) {
|
|
187
|
+
const children = childrenByParent.get(parentId) ?? [];
|
|
188
|
+
if (children.length === 0) return;
|
|
189
|
+
if (branchDepth >= maxRepeatDepth) return;
|
|
190
|
+
if (level.groupByEdgeField) {
|
|
191
|
+
const groups = /* @__PURE__ */ new Map();
|
|
192
|
+
for (const child of children) {
|
|
193
|
+
const value = getPathValue(child.edge, level.groupByEdgeField);
|
|
194
|
+
const key = stableGroupKey(value);
|
|
195
|
+
const group = groups.get(key);
|
|
196
|
+
if (group) group.children.push(child);
|
|
197
|
+
else groups.set(key, {
|
|
198
|
+
label: ctx.labelForValue(value, level.groupByEdgeField),
|
|
199
|
+
children: [child]
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
for (const [key, group] of groups) {
|
|
203
|
+
const rowKey = `${scopeKey}/edge-group:${level.collection}:${parentId}:${key}`;
|
|
204
|
+
const expanded = shouldExpand(rowKey, nextDepth, ctx.outline, ctx.collapsedKeys);
|
|
205
|
+
rows.push({
|
|
206
|
+
kind: "group",
|
|
207
|
+
key: rowKey,
|
|
208
|
+
label: group.label,
|
|
209
|
+
depth: nextDepth,
|
|
210
|
+
count: group.children.length,
|
|
211
|
+
expandable: true,
|
|
212
|
+
collapsed: !expanded
|
|
213
|
+
});
|
|
214
|
+
if (!expanded) continue;
|
|
215
|
+
for (const child of group.children) pushEdgeRecord(child.childId, nextDepth + 1, branchDepth + 1, ancestorIds);
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
for (const child of children) pushEdgeRecord(child.childId, nextDepth, branchDepth + 1, ancestorIds);
|
|
220
|
+
}
|
|
221
|
+
function pushEdgeRecord(id, rowDepth, branchDepth, ancestorIds) {
|
|
222
|
+
const doc = docsById.get(id);
|
|
223
|
+
if (!doc || ancestorIds.has(id)) return;
|
|
224
|
+
const rowKey = `${scopeKey}/record:${id}`;
|
|
225
|
+
const hasChildren = (childrenByParent.get(id) ?? []).some((child) => docsById.has(child.childId));
|
|
226
|
+
const expanded = hasChildren && shouldExpand(rowKey, rowDepth, ctx.outline, ctx.collapsedKeys);
|
|
227
|
+
rows.push({
|
|
228
|
+
kind: "record",
|
|
229
|
+
key: rowKey,
|
|
230
|
+
id,
|
|
231
|
+
doc,
|
|
232
|
+
depth: rowDepth,
|
|
233
|
+
expandable: hasChildren,
|
|
234
|
+
collapsed: hasChildren ? !expanded : void 0
|
|
235
|
+
});
|
|
236
|
+
visited.add(id);
|
|
237
|
+
if (hasChildren && expanded) {
|
|
238
|
+
const nextAncestors = new Set(ancestorIds);
|
|
239
|
+
nextAncestors.add(id);
|
|
240
|
+
pushChildRows(id, rowDepth + 1, branchDepth, nextAncestors);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
for (const doc of roots) {
|
|
244
|
+
const id = getId(doc);
|
|
245
|
+
if (!id) {
|
|
246
|
+
rows.push({
|
|
247
|
+
kind: "record",
|
|
248
|
+
key: `${scopeKey}/record:${rows.length}`,
|
|
249
|
+
id: String(rows.length),
|
|
250
|
+
doc,
|
|
251
|
+
depth
|
|
252
|
+
});
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
pushEdgeRecord(id, depth, 0, /* @__PURE__ */ new Set());
|
|
256
|
+
}
|
|
257
|
+
for (const doc of outlineDocs) {
|
|
258
|
+
const id = getId(doc);
|
|
259
|
+
if (id && !visited.has(id)) pushEdgeRecord(id, depth, 0, /* @__PURE__ */ new Set());
|
|
260
|
+
}
|
|
261
|
+
if (levelIndex + 1 >= ctx.levels.length) return rows;
|
|
262
|
+
return rows.flatMap((row) => row.kind === "record" ? [row, ...buildLevelRows([row.doc], row.depth + 1, levelIndex + 1, ctx, row.key)] : [row]);
|
|
263
|
+
}
|
|
264
|
+
function createPathNode(key, label) {
|
|
265
|
+
return {
|
|
266
|
+
key,
|
|
267
|
+
label,
|
|
268
|
+
children: /* @__PURE__ */ new Map(),
|
|
269
|
+
docs: []
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function countPathNode(node) {
|
|
273
|
+
let count = node.docs.length;
|
|
274
|
+
for (const child of node.children.values()) count += countPathNode(child);
|
|
275
|
+
return count;
|
|
276
|
+
}
|
|
277
|
+
function buildPathRows(level, docs, depth, levelIndex, ctx, scopeKey) {
|
|
278
|
+
const root = createPathNode("path:root", "Root");
|
|
279
|
+
const separator = level.separator ?? "/";
|
|
280
|
+
const maxPathDepth = getRepeatMaxDepth(level.repeat, ctx.maxDepth);
|
|
281
|
+
for (const doc of docs) {
|
|
282
|
+
const segments = String(getPathValue(doc, level.field) ?? "").trim().split(separator).filter(Boolean).slice(0, maxPathDepth);
|
|
283
|
+
const folderSegments = level.syntheticFolders && segments.length > 0 ? segments.slice(0, Math.max(segments.length - 1, 0)) : segments;
|
|
284
|
+
let node = root;
|
|
285
|
+
let currentPath = "";
|
|
286
|
+
for (const segment of folderSegments) {
|
|
287
|
+
currentPath = currentPath ? `${currentPath}${separator}${segment}` : segment;
|
|
288
|
+
let child = node.children.get(segment);
|
|
289
|
+
if (!child) {
|
|
290
|
+
child = createPathNode(`${scopeKey}/path:${level.field}:${currentPath}`, segment);
|
|
291
|
+
node.children.set(segment, child);
|
|
292
|
+
}
|
|
293
|
+
node = child;
|
|
294
|
+
}
|
|
295
|
+
node.docs.push(doc);
|
|
296
|
+
}
|
|
297
|
+
function walk(node, rowDepth) {
|
|
298
|
+
const rows = [];
|
|
299
|
+
for (const child of Array.from(node.children.values()).sort((a, b) => a.label.localeCompare(b.label))) {
|
|
300
|
+
const expanded = shouldExpand(child.key, rowDepth, ctx.outline, ctx.collapsedKeys);
|
|
301
|
+
rows.push({
|
|
302
|
+
kind: "synthetic",
|
|
303
|
+
key: child.key,
|
|
304
|
+
label: child.label,
|
|
305
|
+
depth: rowDepth,
|
|
306
|
+
count: countPathNode(child),
|
|
307
|
+
expandable: true,
|
|
308
|
+
collapsed: !expanded
|
|
309
|
+
});
|
|
310
|
+
if (expanded) rows.push(...walk(child, rowDepth + 1));
|
|
311
|
+
}
|
|
312
|
+
for (const doc of node.docs) {
|
|
313
|
+
const id = getId(doc) ?? `${node.key}:${rows.length}`;
|
|
314
|
+
rows.push({
|
|
315
|
+
kind: "record",
|
|
316
|
+
key: `${scopeKey}/record:${id}`,
|
|
317
|
+
id,
|
|
318
|
+
doc,
|
|
319
|
+
depth: rowDepth
|
|
320
|
+
});
|
|
321
|
+
if (levelIndex + 1 < ctx.levels.length) rows.push(...buildLevelRows([doc], rowDepth + 1, levelIndex + 1, ctx, `${scopeKey}/record:${id}`));
|
|
322
|
+
}
|
|
323
|
+
return rows;
|
|
324
|
+
}
|
|
325
|
+
return walk(root, depth);
|
|
326
|
+
}
|
|
327
|
+
function buildLevelRows(docs, depth, levelIndex, ctx, scopeKey = "root") {
|
|
328
|
+
if (docs.length === 0) return [];
|
|
329
|
+
if (depth > ctx.maxDepth || levelIndex >= ctx.levels.length) return docs.map((doc, index) => {
|
|
330
|
+
const id = getId(doc) ?? `${depth}:${index}`;
|
|
331
|
+
return {
|
|
332
|
+
kind: "record",
|
|
333
|
+
key: `${scopeKey}/record:${id}`,
|
|
334
|
+
id,
|
|
335
|
+
doc,
|
|
336
|
+
depth
|
|
337
|
+
};
|
|
338
|
+
});
|
|
339
|
+
const level = ctx.levels[levelIndex];
|
|
340
|
+
if (level.kind === "field") return buildFieldRows(level, docs, depth, levelIndex, ctx, scopeKey);
|
|
341
|
+
if (level.kind === "relation-field") return buildRelationFieldRows(level, docs, depth, levelIndex, ctx, scopeKey);
|
|
342
|
+
if (level.kind === "edge") return buildEdgeRows(level, docs, depth, levelIndex, ctx, scopeKey);
|
|
343
|
+
return buildPathRows(level, docs, depth, levelIndex, ctx, scopeKey);
|
|
344
|
+
}
|
|
345
|
+
function buildOutlineRows({ docs, outline, edgesByCollection = {}, collapsedKeys, labelForValue = defaultLabelForValue, maxDepth }) {
|
|
346
|
+
const levels = outline?.levels?.filter(Boolean) ?? [];
|
|
347
|
+
const ctx = {
|
|
348
|
+
levels,
|
|
349
|
+
outline,
|
|
350
|
+
edgesByCollection,
|
|
351
|
+
collapsedKeys: new Set(collapsedKeys ?? []),
|
|
352
|
+
labelForValue,
|
|
353
|
+
maxDepth: maxDepth ?? outline?.maxDepth ?? DEFAULT_MAX_DEPTH
|
|
354
|
+
};
|
|
355
|
+
if (levels.length === 0) return buildLevelRows(docs, 0, 0, {
|
|
356
|
+
...ctx,
|
|
357
|
+
levels: []
|
|
358
|
+
});
|
|
359
|
+
return buildLevelRows(docs, 0, 0, ctx);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
//#endregion
|
|
363
|
+
export { buildOutlineRows };
|
|
@@ -7,10 +7,10 @@ import { flattenOptions } from "../../components/primitives/types.mjs";
|
|
|
7
7
|
import { resolveOptionLabelForValue } from "../../components/primitives/option-label.mjs";
|
|
8
8
|
import { Button } from "../../components/ui/button.mjs";
|
|
9
9
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../components/ui/select.mjs";
|
|
10
|
+
import { LocaleSwitcher } from "../../components/locale-switcher.mjs";
|
|
10
11
|
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from "../../components/ui/sheet.mjs";
|
|
11
12
|
import { sanitizeFilename } from "../../components/fields/field-utils.mjs";
|
|
12
13
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../../components/ui/tooltip.mjs";
|
|
13
|
-
import { LocaleSwitcher } from "../../components/locale-switcher.mjs";
|
|
14
14
|
import { Checkbox } from "../../components/ui/checkbox.mjs";
|
|
15
15
|
import { createActionRegistryProxy } from "../../builder/types/action-registry.mjs";
|
|
16
16
|
import { ActionButton } from "../../components/actions/action-button.mjs";
|
|
@@ -52,7 +52,7 @@ import { DndContext, DragOverlay, KeyboardSensor, PointerSensor, closestCenter,
|
|
|
52
52
|
import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
53
53
|
import { CSS } from "@dnd-kit/utilities";
|
|
54
54
|
import { toast } from "sonner";
|
|
55
|
-
import { flexRender, getCoreRowModel,
|
|
55
|
+
import { flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
|
56
56
|
|
|
57
57
|
//#region src/client/views/collection/table-view.tsx
|
|
58
58
|
/**
|
|
@@ -252,11 +252,14 @@ function mapListSchemaToConfig(list) {
|
|
|
252
252
|
if (list.columns?.length) config.columns = list.columns;
|
|
253
253
|
if (list.defaultSort) config.defaultSort = list.defaultSort;
|
|
254
254
|
if (list.orderable) config.orderable = list.orderable;
|
|
255
|
-
if (list.searchable
|
|
255
|
+
if (Array.isArray(list.searchable) && list.searchable.length) {
|
|
256
256
|
config.searchFields = list.searchable;
|
|
257
257
|
config.searchable = true;
|
|
258
|
-
}
|
|
258
|
+
} else if (typeof list.searchable === "boolean") config.searchable = list.searchable;
|
|
259
|
+
if (list.filterable?.length) config.filterable = list.filterable;
|
|
259
260
|
if (list.grouping?.fields?.length) config.grouping = list.grouping;
|
|
261
|
+
if (list.layout) config.layout = list.layout;
|
|
262
|
+
if (list.outline?.levels?.length) config.outline = list.outline;
|
|
260
263
|
config.actions = mapListActionsToDefinitions(list.actions);
|
|
261
264
|
return config;
|
|
262
265
|
}
|
|
@@ -422,15 +425,6 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
422
425
|
resolvedListConfig,
|
|
423
426
|
collectionMeta
|
|
424
427
|
]);
|
|
425
|
-
const expandedFields = useMemo(() => autoExpandFields({
|
|
426
|
-
fields: resolvedFields,
|
|
427
|
-
list: resolvedListConfig,
|
|
428
|
-
relations: collectionMeta?.relations
|
|
429
|
-
}), [
|
|
430
|
-
resolvedFields,
|
|
431
|
-
resolvedListConfig,
|
|
432
|
-
collectionMeta?.relations
|
|
433
|
-
]);
|
|
434
428
|
const [isSheetOpen, setIsSheetOpen] = useSidebarSearchParam("view-options", { legacyKey: "viewOptions" });
|
|
435
429
|
const [searchTerm, setSearchTerm] = useState("");
|
|
436
430
|
const [isSearchPanelOpen, setIsSearchPanelOpen] = useState(false);
|
|
@@ -479,6 +473,18 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
479
473
|
groupBy: defaultGroupBy
|
|
480
474
|
}, collection, user?.id);
|
|
481
475
|
const effectiveRealtime = viewState.config.realtime ?? resolvedRealtime;
|
|
476
|
+
const visibleColumnsForExpansion = useMemo(() => viewState.config.visibleColumns.length > 0 ? viewState.config.visibleColumns : defaultColumns, [viewState.config.visibleColumns, defaultColumns]);
|
|
477
|
+
const expandedFields = useMemo(() => autoExpandFields({
|
|
478
|
+
fields: resolvedFields,
|
|
479
|
+
list: resolvedListConfig,
|
|
480
|
+
visibleColumns: visibleColumnsForExpansion,
|
|
481
|
+
relations: collectionMeta?.relations
|
|
482
|
+
}), [
|
|
483
|
+
resolvedFields,
|
|
484
|
+
resolvedListConfig,
|
|
485
|
+
visibleColumnsForExpansion,
|
|
486
|
+
collectionMeta?.relations
|
|
487
|
+
]);
|
|
482
488
|
const isKnownSortField = React.useCallback((field) => !!field && (field === "_title" || !!resolvedFields?.[field]), [resolvedFields]);
|
|
483
489
|
const hasOrderField = isKnownSortField(orderField);
|
|
484
490
|
const canUseOrderableSort = isOrderableEnabled && hasOrderField;
|
|
@@ -489,7 +495,10 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
489
495
|
field: orderField,
|
|
490
496
|
direction: orderDirection
|
|
491
497
|
};
|
|
492
|
-
return
|
|
498
|
+
return {
|
|
499
|
+
field: "createdAt",
|
|
500
|
+
direction: "desc"
|
|
501
|
+
};
|
|
493
502
|
}, [
|
|
494
503
|
viewState.config.sortConfig,
|
|
495
504
|
resolvedListConfig?.defaultSort,
|
|
@@ -875,7 +884,7 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
875
884
|
data: filteredItems,
|
|
876
885
|
columns: visibleColumnDefs,
|
|
877
886
|
getCoreRowModel: getCoreRowModel(),
|
|
878
|
-
|
|
887
|
+
manualSorting: true,
|
|
879
888
|
onSortingChange: handleSortingChange,
|
|
880
889
|
enableRowSelection: true,
|
|
881
890
|
onRowSelectionChange: setRowSelection,
|
|
@@ -1537,4 +1546,4 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1537
1546
|
}
|
|
1538
1547
|
|
|
1539
1548
|
//#endregion
|
|
1540
|
-
export { TableView as default };
|
|
1549
|
+
export { UploadCollectionButton, TableView as default, mapListSchemaToConfig, stringifyGroupValue };
|
|
@@ -2,11 +2,11 @@ import { useResolveText, useTranslation } from "../../i18n/hooks.mjs";
|
|
|
2
2
|
import { useSafeContentLocales } from "../../runtime/content-locales-provider.mjs";
|
|
3
3
|
import { useScopedLocale } from "../../runtime/locale-scope.mjs";
|
|
4
4
|
import { Button } from "../../components/ui/button.mjs";
|
|
5
|
-
import { Badge } from "../../components/ui/badge.mjs";
|
|
6
|
-
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../../components/ui/dialog.mjs";
|
|
7
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../components/ui/dropdown-menu.mjs";
|
|
8
6
|
import { LocaleSwitcher } from "../../components/locale-switcher.mjs";
|
|
9
7
|
import { Label } from "../../components/ui/label.mjs";
|
|
8
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../../components/ui/dialog.mjs";
|
|
9
|
+
import { Badge } from "../../components/ui/badge.mjs";
|
|
10
10
|
import { Skeleton } from "../../components/ui/skeleton.mjs";
|
|
11
11
|
import { Checkbox } from "../../components/ui/checkbox.mjs";
|
|
12
12
|
import { DateTimeInput } from "../../components/primitives/date-input.mjs";
|
|
@@ -23,11 +23,10 @@ import { useTransitionStage } from "../../hooks/use-transition-stage.mjs";
|
|
|
23
23
|
import { detectManyToManyRelations, hasManyToManyRelations } from "../../utils/detect-relations.mjs";
|
|
24
24
|
import { shouldHandleAdminShortcut } from "../../utils/keyboard-shortcuts.mjs";
|
|
25
25
|
import { AdminViewHeader } from "../layout/admin-view-layout.mjs";
|
|
26
|
-
import { useGlobalAuditHistory } from "../../hooks/use-audit-history.mjs";
|
|
27
26
|
import { Icon } from "@iconify/react";
|
|
28
27
|
import * as React from "react";
|
|
29
28
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
30
|
-
import { FormProvider, useForm } from "react-hook-form";
|
|
29
|
+
import { FormProvider, useForm, useFormState } from "react-hook-form";
|
|
31
30
|
import { toast } from "sonner";
|
|
32
31
|
import { QuestpieClientError } from "questpie/client";
|
|
33
32
|
|
|
@@ -97,6 +96,31 @@ function GlobalFormViewSkeleton() {
|
|
|
97
96
|
]
|
|
98
97
|
});
|
|
99
98
|
}
|
|
99
|
+
const GlobalFormDirtyRefBridge = React.memo(function GlobalFormDirtyRefBridge$1({ control, onDirtyChange }) {
|
|
100
|
+
const { isDirty } = useFormState({ control });
|
|
101
|
+
React.useEffect(() => {
|
|
102
|
+
onDirtyChange(isDirty);
|
|
103
|
+
}, [isDirty, onDirtyChange]);
|
|
104
|
+
return null;
|
|
105
|
+
});
|
|
106
|
+
const GlobalSaveButton = React.memo(function GlobalSaveButton$1({ control, isMutationPending, t }) {
|
|
107
|
+
const { isSubmitting } = useFormState({ control });
|
|
108
|
+
const isSubmittingNow = isMutationPending || isSubmitting;
|
|
109
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
110
|
+
type: "submit",
|
|
111
|
+
size: "sm",
|
|
112
|
+
disabled: isSubmittingNow,
|
|
113
|
+
className: "gap-2",
|
|
114
|
+
children: isSubmittingNow ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Icon, {
|
|
115
|
+
icon: "ph:spinner-gap",
|
|
116
|
+
className: "size-4 animate-spin"
|
|
117
|
+
}), t("common.loading")] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Icon, {
|
|
118
|
+
icon: "ph:check",
|
|
119
|
+
width: 16,
|
|
120
|
+
height: 16
|
|
121
|
+
}), t("common.save")] })
|
|
122
|
+
});
|
|
123
|
+
});
|
|
100
124
|
/**
|
|
101
125
|
* GlobalFormView - Default form-based edit view for globals
|
|
102
126
|
*/
|
|
@@ -136,7 +160,6 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
136
160
|
id: globalData?.id,
|
|
137
161
|
limit: 50
|
|
138
162
|
}, { enabled: isHistoryOpen && !!globalSchema?.options?.versioning });
|
|
139
|
-
const { data: auditData, isLoading: auditLoading } = useGlobalAuditHistory(globalName, { limit: 50 }, { enabled: isHistoryOpen });
|
|
140
163
|
const workflowConfig = globalSchema?.options?.workflow;
|
|
141
164
|
const workflowEnabled = !!workflowConfig?.enabled;
|
|
142
165
|
/** Lightweight versions query (limit: 1) to read the current stage. */
|
|
@@ -167,6 +190,10 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
167
190
|
defaultValues: globalData ?? {},
|
|
168
191
|
resolver
|
|
169
192
|
});
|
|
193
|
+
const formIsDirtyRef = React.useRef(false);
|
|
194
|
+
const handleFormDirtyChange = React.useCallback((isDirty) => {
|
|
195
|
+
formIsDirtyRef.current = isDirty;
|
|
196
|
+
}, []);
|
|
170
197
|
/** Execute the confirmed workflow transition (immediate or scheduled). */
|
|
171
198
|
const confirmTransition = React.useCallback(() => {
|
|
172
199
|
if (!transitionTarget) return;
|
|
@@ -215,7 +242,7 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
215
242
|
});
|
|
216
243
|
const handleContentLocaleChange = React.useCallback((nextLocale) => {
|
|
217
244
|
if (nextLocale === prevLocaleRef.current) return;
|
|
218
|
-
if (
|
|
245
|
+
if (formIsDirtyRef.current && !localeChangeDialog.open) {
|
|
219
246
|
skipResetRef.current = true;
|
|
220
247
|
localeSnapshotRef.current = form.getValues();
|
|
221
248
|
setLocaleChangeDialog({
|
|
@@ -229,7 +256,6 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
229
256
|
setContentLocale(nextLocale);
|
|
230
257
|
}, [
|
|
231
258
|
form,
|
|
232
|
-
form.formState.isDirty,
|
|
233
259
|
localeChangeDialog.open,
|
|
234
260
|
setContentLocale
|
|
235
261
|
]);
|
|
@@ -316,7 +342,6 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
316
342
|
document.addEventListener("keydown", handleKeyDown);
|
|
317
343
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
318
344
|
}, [form, onSubmit]);
|
|
319
|
-
const isSubmitting = updateMutation.isPending || form.formState.isSubmitting;
|
|
320
345
|
const confirmRevertVersion = React.useCallback(async () => {
|
|
321
346
|
if (!pendingRevertVersion) return;
|
|
322
347
|
const payload = {};
|
|
@@ -361,10 +386,14 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
361
386
|
})
|
|
362
387
|
});
|
|
363
388
|
if (dataLoading) return /* @__PURE__ */ jsx(GlobalFormViewSkeleton, {});
|
|
364
|
-
const globalLabel = resolveText(resolvedConfig?.label ?? schemaFields
|
|
389
|
+
const globalLabel = resolveText(resolvedConfig?.label ?? schemaFields["_globalLabel"], globalName);
|
|
365
390
|
return /* @__PURE__ */ jsxs(FormProvider, {
|
|
366
391
|
...form,
|
|
367
392
|
children: [
|
|
393
|
+
/* @__PURE__ */ jsx(GlobalFormDirtyRefBridge, {
|
|
394
|
+
control: form.control,
|
|
395
|
+
onDirtyChange: handleFormDirtyChange
|
|
396
|
+
}),
|
|
368
397
|
/* @__PURE__ */ jsxs("form", {
|
|
369
398
|
onSubmit: form.handleSubmit(onSubmit),
|
|
370
399
|
className: "qa-global-form w-full space-y-4",
|
|
@@ -414,7 +443,7 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
414
443
|
}), stage.label || stage.name]
|
|
415
444
|
}, stage.name))
|
|
416
445
|
})] }),
|
|
417
|
-
/* @__PURE__ */ jsxs(Button, {
|
|
446
|
+
globalSchema?.options?.versioning && /* @__PURE__ */ jsxs(Button, {
|
|
418
447
|
type: "button",
|
|
419
448
|
variant: "outline",
|
|
420
449
|
size: "icon-sm",
|
|
@@ -428,33 +457,24 @@ function GlobalFormView({ global: globalName, config, viewConfig, registry, show
|
|
|
428
457
|
children: t("history.title")
|
|
429
458
|
})]
|
|
430
459
|
}),
|
|
431
|
-
/* @__PURE__ */ jsx(
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
className: "gap-2",
|
|
436
|
-
children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Icon, {
|
|
437
|
-
icon: "ph:spinner-gap",
|
|
438
|
-
className: "size-4 animate-spin"
|
|
439
|
-
}), t("common.loading")] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Icon, {
|
|
440
|
-
icon: "ph:check",
|
|
441
|
-
width: 16,
|
|
442
|
-
height: 16
|
|
443
|
-
}), t("common.save")] })
|
|
460
|
+
/* @__PURE__ */ jsx(GlobalSaveButton, {
|
|
461
|
+
control: form.control,
|
|
462
|
+
isMutationPending: updateMutation.isPending,
|
|
463
|
+
t
|
|
444
464
|
})
|
|
445
465
|
] })
|
|
446
466
|
}), /* @__PURE__ */ jsx(AutoFormFields, {
|
|
447
467
|
collection: globalName,
|
|
448
468
|
mode: "global",
|
|
449
469
|
config: resolvedConfig,
|
|
450
|
-
registry
|
|
470
|
+
registry,
|
|
471
|
+
resolvedFields: schemaFields,
|
|
472
|
+
schema: globalSchema
|
|
451
473
|
})]
|
|
452
474
|
}),
|
|
453
|
-
/* @__PURE__ */ jsx(HistorySidebar, {
|
|
475
|
+
isHistoryOpen && /* @__PURE__ */ jsx(HistorySidebar, {
|
|
454
476
|
open: isHistoryOpen,
|
|
455
477
|
onOpenChange: setIsHistoryOpen,
|
|
456
|
-
auditEntries: auditData ?? [],
|
|
457
|
-
isLoadingAudit: auditLoading,
|
|
458
478
|
versions: versionsData ?? [],
|
|
459
479
|
fields: globalSchema?.fields,
|
|
460
480
|
isLoadingVersions: versionsLoading,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ServerAdminShellRailConfig } from "../../../server/augmentation/shell.mjs";
|
|
2
|
+
import "../../../server/augmentation.mjs";
|
|
1
3
|
import { AdminToasterProps } from "../../components/ui/sonner.mjs";
|
|
2
4
|
import { AdminSidebarProps } from "./admin-sidebar.mjs";
|
|
3
5
|
import { AdminTheme } from "./admin-theme.mjs";
|
|
@@ -73,6 +75,18 @@ interface AdminLayoutSharedProps {
|
|
|
73
75
|
*/
|
|
74
76
|
layoutMode?: LayoutMode;
|
|
75
77
|
}
|
|
78
|
+
interface AdminShellRailProps {
|
|
79
|
+
/** Current route path, usually the browser pathname. */
|
|
80
|
+
activeRoute?: string;
|
|
81
|
+
/** Admin base path, usually "/admin". */
|
|
82
|
+
basePath: string;
|
|
83
|
+
/** Resolved rail placement. */
|
|
84
|
+
placement: "left" | "right";
|
|
85
|
+
/** Raw rail config from server admin config. */
|
|
86
|
+
config: ServerAdminShellRailConfig;
|
|
87
|
+
/** Navigate function from the admin runtime. */
|
|
88
|
+
navigate: (path: string) => void;
|
|
89
|
+
}
|
|
76
90
|
interface AdminLayoutProps extends AdminLayoutSharedProps {
|
|
77
91
|
/**
|
|
78
92
|
* Brand name for sidebar.
|
|
@@ -127,4 +141,4 @@ declare function AdminLayout({
|
|
|
127
141
|
layoutMode
|
|
128
142
|
}: AdminLayoutProps): React.ReactElement;
|
|
129
143
|
//#endregion
|
|
130
|
-
export { AdminLayout, AdminLayoutSharedProps };
|
|
144
|
+
export { AdminLayout, AdminLayoutSharedProps, AdminShellRailProps };
|