@nitrogenbuilder/connector-payload 0.1.39 → 0.1.41
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/components/LockedJsonField.js +1 -1
- package/dist/components/NitrogenComponentInventoryClient.d.ts +3 -2
- package/dist/components/NitrogenComponentInventoryClient.js +370 -63
- package/dist/components/NitrogenComponentInventoryField.js +15 -5
- package/dist/components/NitrogenComponentInventoryView.js +15 -5
- package/dist/components/NitrogenDataViewer.js +1 -1
- package/dist/components/NitrogenEditButton.js +2 -2
- package/dist/components/NitrogenEditButtonRuntime.js +21 -5
- package/dist/components/NitrogenViewButton.js +2 -2
- package/dist/components/NitrogenViewButtonRuntime.js +7 -3
- package/dist/editor/NitrogenEditorPage.js +12 -29
- package/dist/editor/index.d.ts +3 -3
- package/dist/editor/index.js +3 -3
- package/dist/endpoints/all.js +2 -2
- package/dist/endpoints/batch.js +2 -2
- package/dist/endpoints/collection-endpoints.js +1 -1
- package/dist/endpoints/component-inventory.d.ts +1 -1
- package/dist/endpoints/component-inventory.js +275 -2
- package/dist/endpoints/helpers.d.ts +5 -5
- package/dist/endpoints/helpers.js +1 -1
- package/dist/endpoints/media.js +1 -1
- package/dist/endpoints/nitrogen-settings.js +1 -1
- package/dist/endpoints/templates.js +2 -2
- package/dist/frontend/NitrogenWrapper.js +2 -2
- package/dist/frontend/index.d.ts +5 -5
- package/dist/frontend/index.js +3 -3
- package/dist/globals/NitrogenSettings.js +46 -11
- package/dist/index.d.ts +7 -7
- package/dist/index.js +22 -22
- package/dist/inventory/indexing.d.ts +2 -2
- package/dist/inventory/indexing.js +1 -1
- package/dist/types.d.ts +6 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { LockedJsonFieldRuntime } from './LockedJsonFieldRuntime';
|
|
2
|
+
import { LockedJsonFieldRuntime } from './LockedJsonFieldRuntime.js';
|
|
3
3
|
export const LockedJsonField = ({ field, value }) => {
|
|
4
4
|
const label = typeof field.label === 'string' ? field.label : field.name;
|
|
5
5
|
const description = typeof field.admin?.description === 'string' ? field.admin.description : undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentManifestEntry } from '@nitrogenbuilder/types';
|
|
2
|
-
import type { NitrogenComponentUsageDoc } from '../types';
|
|
2
|
+
import type { NitrogenComponentUsageDoc } from '../types.js';
|
|
3
3
|
type InventoryClientProps = {
|
|
4
4
|
adminRoute: string;
|
|
5
5
|
components: Array<{
|
|
@@ -8,6 +8,7 @@ type InventoryClientProps = {
|
|
|
8
8
|
isUnknown: boolean;
|
|
9
9
|
usages: NitrogenComponentUsageDoc[];
|
|
10
10
|
}>;
|
|
11
|
+
previewUrl: string;
|
|
11
12
|
};
|
|
12
|
-
export default function NitrogenComponentInventoryClient({ adminRoute, components, }: InventoryClientProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export default function NitrogenComponentInventoryClient({ adminRoute, components, previewUrl, }: InventoryClientProps): import("react/jsx-runtime").JSX.Element;
|
|
13
14
|
export {};
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { formatAdminURL } from 'payload/shared';
|
|
5
|
+
const EDITABLE_PROP_TYPES = new Set([
|
|
6
|
+
'boolean',
|
|
7
|
+
'color',
|
|
8
|
+
'color-select',
|
|
9
|
+
'color-swatch',
|
|
10
|
+
'enum',
|
|
11
|
+
'enum-inline',
|
|
12
|
+
'number',
|
|
13
|
+
'string',
|
|
14
|
+
'url',
|
|
15
|
+
'wysiwyg',
|
|
16
|
+
]);
|
|
5
17
|
function formatSourceLabel(slug) {
|
|
6
18
|
if (slug === 'nitrogen-templates') {
|
|
7
19
|
return 'Nitrogen Templates';
|
|
@@ -12,6 +24,86 @@ function formatSourceLabel(slug) {
|
|
|
12
24
|
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
13
25
|
.join(' ');
|
|
14
26
|
}
|
|
27
|
+
function getPreviewTargetOrigin(previewUrl) {
|
|
28
|
+
try {
|
|
29
|
+
return new URL(previewUrl).origin;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return '*';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function normalizeManifestOptions(options) {
|
|
36
|
+
if (!options?.length) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return options.map((option) => {
|
|
40
|
+
if (typeof option === 'string') {
|
|
41
|
+
return {
|
|
42
|
+
label: option,
|
|
43
|
+
value: option,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
label: option.label || option.value,
|
|
48
|
+
value: option.value,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function isEditableProp(prop) {
|
|
53
|
+
return EDITABLE_PROP_TYPES.has(prop.type);
|
|
54
|
+
}
|
|
55
|
+
function collectEditablePropOptions(definition) {
|
|
56
|
+
if (!definition) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const options = [];
|
|
60
|
+
Object.values(definition.categories).forEach((category) => {
|
|
61
|
+
Object.values(category.groups).forEach((group) => {
|
|
62
|
+
Object.entries(group.props).forEach(([propName, prop]) => {
|
|
63
|
+
if (!isEditableProp(prop)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
options.push({
|
|
67
|
+
defaultValue: prop.default,
|
|
68
|
+
label: `${category.label} / ${group.label} / ${prop.label || propName}`,
|
|
69
|
+
options: normalizeManifestOptions(prop.options),
|
|
70
|
+
path: `${group.key}.${propName}`,
|
|
71
|
+
type: prop.type,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
return options;
|
|
77
|
+
}
|
|
78
|
+
function stringifyPropValue(value, propType) {
|
|
79
|
+
if (propType === 'boolean') {
|
|
80
|
+
return Boolean(value);
|
|
81
|
+
}
|
|
82
|
+
if (typeof value === 'string') {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === 'number') {
|
|
86
|
+
return String(value);
|
|
87
|
+
}
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
function coercePropValue(value, propType) {
|
|
91
|
+
if (propType === 'boolean') {
|
|
92
|
+
return Boolean(value);
|
|
93
|
+
}
|
|
94
|
+
if (propType === 'number') {
|
|
95
|
+
const parsed = Number(value);
|
|
96
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
97
|
+
}
|
|
98
|
+
return String(value);
|
|
99
|
+
}
|
|
100
|
+
function getDefaultDraft(option) {
|
|
101
|
+
return {
|
|
102
|
+
propPath: option?.path || '',
|
|
103
|
+
status: 'idle',
|
|
104
|
+
value: option ? stringifyPropValue(option.defaultValue, option.type) : '',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
15
107
|
function renderProp(name, prop, depth = 0) {
|
|
16
108
|
return (_jsxs("div", { style: { paddingLeft: depth * 16 }, children: [_jsxs("div", { style: { fontFamily: 'monospace', fontSize: 12, lineHeight: 1.5 }, children: [_jsx("strong", { children: name }), " ", _jsxs("span", { style: { color: '#6b7280' }, children: ["(", prop.type, ")"] }), prop.label ? _jsxs("span", { style: { color: '#6b7280' }, children: [" - ", prop.label] }) : null] }), prop.props ? (_jsx("div", { style: { marginTop: 4 }, children: Object.entries(prop.props).map(([childName, childProp]) => renderProp(childName, childProp, depth + 1)) })) : null] }, `${name}-${depth}`));
|
|
17
109
|
}
|
|
@@ -34,51 +126,22 @@ function buttonStyle(kind) {
|
|
|
34
126
|
padding: '8px 12px',
|
|
35
127
|
};
|
|
36
128
|
}
|
|
37
|
-
function
|
|
38
|
-
const relationshipMap = new Map();
|
|
39
|
-
usages.forEach((usage) => {
|
|
40
|
-
const relationshipName = getRelationshipName(usage);
|
|
41
|
-
if (!relationshipName) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const entry = relationshipMap.get(relationshipName) ?? {
|
|
45
|
-
documentKeys: new Set(),
|
|
46
|
-
occurrenceCount: 0,
|
|
47
|
-
};
|
|
48
|
-
entry.occurrenceCount += 1;
|
|
49
|
-
entry.documentKeys.add(`${usage.sourceCollection}:${usage.sourceDocumentId}`);
|
|
50
|
-
relationshipMap.set(relationshipName, entry);
|
|
51
|
-
});
|
|
52
|
-
return Array.from(relationshipMap.entries())
|
|
53
|
-
.map(([componentName, entry]) => ({
|
|
54
|
-
componentName,
|
|
55
|
-
documentCount: entry.documentKeys.size,
|
|
56
|
-
occurrenceCount: entry.occurrenceCount,
|
|
57
|
-
}))
|
|
58
|
-
.sort((a, b) => {
|
|
59
|
-
if (b.documentCount !== a.documentCount) {
|
|
60
|
-
return b.documentCount - a.documentCount;
|
|
61
|
-
}
|
|
62
|
-
if (b.occurrenceCount !== a.occurrenceCount) {
|
|
63
|
-
return b.occurrenceCount - a.occurrenceCount;
|
|
64
|
-
}
|
|
65
|
-
return a.componentName.localeCompare(b.componentName);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
function renderRelationshipList(title, summaries, emptyMessage) {
|
|
69
|
-
return (_jsxs("div", { style: { marginTop: 16 }, children: [_jsx("h3", { style: { margin: '0 0 10px', fontSize: 15 }, children: title }), summaries.length ? (_jsx("div", { style: { display: 'grid', gap: 8 }, children: summaries.map((summary) => (_jsxs("div", { style: {
|
|
70
|
-
border: '1px solid #e5e7eb',
|
|
71
|
-
borderRadius: 6,
|
|
72
|
-
padding: 12,
|
|
73
|
-
}, children: [_jsx("div", { style: { fontWeight: 600 }, children: summary.componentName }), _jsxs("div", { style: { color: '#6b7280', fontSize: 12, marginTop: 2 }, children: [summary.documentCount, " doc", summary.documentCount === 1 ? '' : 's', " \u00B7", ' ', summary.occurrenceCount, " occurrence", summary.occurrenceCount === 1 ? '' : 's'] })] }, `${title}-${summary.componentName}`))) })) : (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: emptyMessage }))] }));
|
|
74
|
-
}
|
|
75
|
-
export default function NitrogenComponentInventoryClient({ adminRoute, components, }) {
|
|
129
|
+
export default function NitrogenComponentInventoryClient({ adminRoute, components, previewUrl, }) {
|
|
76
130
|
const sourceFilterRef = useRef(null);
|
|
131
|
+
const previewFrameRefs = useRef({});
|
|
77
132
|
const allComponentNames = useMemo(() => components.map(({ componentName }) => componentName), [components]);
|
|
78
133
|
const [searchValue, setSearchValue] = useState('');
|
|
79
134
|
const [sourceSearchValue, setSourceSearchValue] = useState('');
|
|
80
135
|
const [selectedSources, setSelectedSources] = useState([]);
|
|
81
136
|
const [openNames, setOpenNames] = useState({});
|
|
137
|
+
const [previewStates, setPreviewStates] = useState({});
|
|
138
|
+
const [propUpdateDrafts, setPropUpdateDrafts] = useState({});
|
|
139
|
+
const editablePropOptionsByComponent = useMemo(() => {
|
|
140
|
+
return new Map(components.map((component) => [
|
|
141
|
+
component.componentName,
|
|
142
|
+
collectEditablePropOptions(component.definition),
|
|
143
|
+
]));
|
|
144
|
+
}, [components]);
|
|
82
145
|
const sourceOptions = useMemo(() => {
|
|
83
146
|
const collectionOptions = new Map();
|
|
84
147
|
const documentOptions = new Map();
|
|
@@ -140,15 +203,6 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
140
203
|
return !hasSourceFilter || component.usages.length > 0;
|
|
141
204
|
});
|
|
142
205
|
}, [components, selectedSources]);
|
|
143
|
-
const allSourceFilteredUsages = useMemo(() => sourceFilteredComponents.flatMap((component) => component.usages), [sourceFilteredComponents]);
|
|
144
|
-
const containsByComponent = useMemo(() => {
|
|
145
|
-
const componentMap = new Map();
|
|
146
|
-
sourceFilteredComponents.forEach(({ componentName }) => {
|
|
147
|
-
const summaries = buildRelationshipSummaries(allSourceFilteredUsages.filter((usage) => usage.parentComponentName === componentName), (usage) => usage.componentName);
|
|
148
|
-
componentMap.set(componentName, summaries);
|
|
149
|
-
});
|
|
150
|
-
return componentMap;
|
|
151
|
-
}, [allSourceFilteredUsages, sourceFilteredComponents]);
|
|
152
206
|
const filteredComponents = useMemo(() => {
|
|
153
207
|
const normalizedQuery = searchValue.trim().toLowerCase();
|
|
154
208
|
return sourceFilteredComponents.filter((component) => {
|
|
@@ -170,6 +224,22 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
170
224
|
document.removeEventListener('mousedown', handlePointerDown);
|
|
171
225
|
};
|
|
172
226
|
}, []);
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
setPropUpdateDrafts((current) => {
|
|
229
|
+
const next = { ...current };
|
|
230
|
+
Object.entries(next).forEach(([componentName, draft]) => {
|
|
231
|
+
if (!draft.result) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
next[componentName] = {
|
|
235
|
+
...draft,
|
|
236
|
+
result: undefined,
|
|
237
|
+
status: 'idle',
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
return next;
|
|
241
|
+
});
|
|
242
|
+
}, [selectedSources]);
|
|
173
243
|
const expandAll = () => {
|
|
174
244
|
setOpenNames((current) => ({
|
|
175
245
|
...current,
|
|
@@ -196,6 +266,154 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
196
266
|
: selectedSourceLabels.length === 1
|
|
197
267
|
? selectedSourceLabels[0]
|
|
198
268
|
: `${selectedSourceLabels.length} sources`;
|
|
269
|
+
const postPreviewDataToFrame = useCallback((componentName, data) => {
|
|
270
|
+
if (!previewUrl || !data) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const frameWindow = previewFrameRefs.current[componentName]?.contentWindow;
|
|
274
|
+
if (!frameWindow) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
frameWindow.postMessage({
|
|
278
|
+
componentName,
|
|
279
|
+
payload: data,
|
|
280
|
+
type: 'nitrogen-component-preview',
|
|
281
|
+
}, getPreviewTargetOrigin(previewUrl));
|
|
282
|
+
}, [previewUrl]);
|
|
283
|
+
const loadPreviewData = useCallback(async (componentName, usages) => {
|
|
284
|
+
if (!previewUrl) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
setPreviewStates((current) => {
|
|
288
|
+
if (current[componentName]?.status === 'loading' || current[componentName]?.data) {
|
|
289
|
+
return current;
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
...current,
|
|
293
|
+
[componentName]: {
|
|
294
|
+
status: 'loading',
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
const previewUsage = usages[0];
|
|
299
|
+
const params = new URLSearchParams({
|
|
300
|
+
componentName,
|
|
301
|
+
});
|
|
302
|
+
if (previewUsage) {
|
|
303
|
+
params.set('sourceCollection', previewUsage.sourceCollection);
|
|
304
|
+
params.set('sourceDocumentId', previewUsage.sourceDocumentId);
|
|
305
|
+
if (previewUsage.moduleId) {
|
|
306
|
+
params.set('moduleId', previewUsage.moduleId);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
const response = await fetch(`/api/nitrogen/v1/component-inventory/preview-data?${params}`);
|
|
311
|
+
const data = (await response.json());
|
|
312
|
+
if (!response.ok || !('success' in data)) {
|
|
313
|
+
throw new Error('error' in data && data.error ? data.error : 'Failed to load preview data.');
|
|
314
|
+
}
|
|
315
|
+
setPreviewStates((current) => ({
|
|
316
|
+
...current,
|
|
317
|
+
[componentName]: {
|
|
318
|
+
data,
|
|
319
|
+
status: 'loaded',
|
|
320
|
+
},
|
|
321
|
+
}));
|
|
322
|
+
window.setTimeout(() => postPreviewDataToFrame(componentName, data), 0);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
setPreviewStates((current) => ({
|
|
326
|
+
...current,
|
|
327
|
+
[componentName]: {
|
|
328
|
+
error: error instanceof Error ? error.message : 'Failed to load preview data.',
|
|
329
|
+
status: 'error',
|
|
330
|
+
},
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
}, [postPreviewDataToFrame, previewUrl]);
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
filteredComponents.forEach((component) => {
|
|
336
|
+
if (openNames[component.componentName]) {
|
|
337
|
+
void loadPreviewData(component.componentName, component.usages);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}, [filteredComponents, loadPreviewData, openNames]);
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
function handlePreviewReady(event) {
|
|
343
|
+
if (!previewUrl || event.origin !== getPreviewTargetOrigin(previewUrl)) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const data = event.data;
|
|
347
|
+
if (data.type !== 'nitrogen-component-preview-ready' || typeof data.componentName !== 'string') {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
postPreviewDataToFrame(data.componentName, previewStates[data.componentName]?.data);
|
|
351
|
+
}
|
|
352
|
+
window.addEventListener('message', handlePreviewReady);
|
|
353
|
+
return () => {
|
|
354
|
+
window.removeEventListener('message', handlePreviewReady);
|
|
355
|
+
};
|
|
356
|
+
}, [postPreviewDataToFrame, previewStates, previewUrl]);
|
|
357
|
+
function updatePropDraft(componentName, nextDraft) {
|
|
358
|
+
setPropUpdateDrafts((current) => ({
|
|
359
|
+
...current,
|
|
360
|
+
[componentName]: {
|
|
361
|
+
...(current[componentName] || getDefaultDraft()),
|
|
362
|
+
...nextDraft,
|
|
363
|
+
},
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
366
|
+
async function runPropUpdate(componentName, editableProps, mode, usages) {
|
|
367
|
+
const draft = propUpdateDrafts[componentName] || getDefaultDraft(editableProps[0]);
|
|
368
|
+
const selectedProp = editableProps.find((option) => option.path === draft.propPath);
|
|
369
|
+
if (!selectedProp) {
|
|
370
|
+
updatePropDraft(componentName, {
|
|
371
|
+
error: 'Select a supported prop first.',
|
|
372
|
+
status: 'error',
|
|
373
|
+
});
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
updatePropDraft(componentName, {
|
|
377
|
+
error: undefined,
|
|
378
|
+
status: mode === 'apply' ? 'applying' : 'previewing',
|
|
379
|
+
});
|
|
380
|
+
try {
|
|
381
|
+
const response = await fetch(`/api/nitrogen/v1/component-inventory/prop-update/${mode}`, {
|
|
382
|
+
body: JSON.stringify({
|
|
383
|
+
componentName,
|
|
384
|
+
propPath: selectedProp.path,
|
|
385
|
+
sourceFilters: selectedSources,
|
|
386
|
+
value: coercePropValue(draft.value, selectedProp.type),
|
|
387
|
+
}),
|
|
388
|
+
headers: {
|
|
389
|
+
'Content-Type': 'application/json',
|
|
390
|
+
},
|
|
391
|
+
method: 'POST',
|
|
392
|
+
});
|
|
393
|
+
const result = (await response.json());
|
|
394
|
+
if (!response.ok || !('success' in result)) {
|
|
395
|
+
throw new Error('error' in result && result.error ? result.error : 'Prop update failed.');
|
|
396
|
+
}
|
|
397
|
+
updatePropDraft(componentName, {
|
|
398
|
+
result,
|
|
399
|
+
status: mode === 'apply' ? 'applied' : 'ready',
|
|
400
|
+
});
|
|
401
|
+
if (mode === 'apply') {
|
|
402
|
+
setPreviewStates((current) => {
|
|
403
|
+
const next = { ...current };
|
|
404
|
+
delete next[componentName];
|
|
405
|
+
return next;
|
|
406
|
+
});
|
|
407
|
+
void loadPreviewData(componentName, usages);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
updatePropDraft(componentName, {
|
|
412
|
+
error: error instanceof Error ? error.message : 'Prop update failed.',
|
|
413
|
+
status: 'error',
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
199
417
|
return (_jsxs("div", { style: { display: 'grid', gap: 16 }, children: [_jsxs("div", { style: {
|
|
200
418
|
alignItems: 'center',
|
|
201
419
|
display: 'flex',
|
|
@@ -267,8 +485,13 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
267
485
|
}, children: option.kind === 'collection' ? 'Collection' : 'Item' })] }), _jsx("span", { style: { color: '#6b7280', fontSize: 12 }, children: option.description })] })] }, option.key));
|
|
268
486
|
})) : (_jsx("p", { style: { color: '#6b7280', fontSize: 13, margin: 0 }, children: "No sources match." })) })] }) })] }), _jsx("button", { onClick: expandAll, style: buttonStyle('secondary'), type: "button", children: "Expand All" }), _jsx("button", { onClick: collapseAll, style: buttonStyle('secondary'), type: "button", children: "Collapse All" })] })] }), filteredComponents.map(({ componentName, definition, isUnknown, usages }) => {
|
|
269
487
|
const isOpen = Boolean(openNames[componentName]);
|
|
270
|
-
const
|
|
271
|
-
const
|
|
488
|
+
const editableProps = editablePropOptionsByComponent.get(componentName) || [];
|
|
489
|
+
const propDraft = propUpdateDrafts[componentName] || getDefaultDraft(editableProps[0]);
|
|
490
|
+
const selectedEditableProp = editableProps.find((option) => option.path === propDraft.propPath);
|
|
491
|
+
const previewState = previewStates[componentName] || { status: 'idle' };
|
|
492
|
+
const componentPreviewUrl = previewUrl
|
|
493
|
+
? `${previewUrl}${previewUrl.includes('?') ? '&' : '?'}componentName=${encodeURIComponent(componentName)}`
|
|
494
|
+
: '';
|
|
272
495
|
return (_jsxs("section", { style: {
|
|
273
496
|
background: '#fff',
|
|
274
497
|
border: '1px solid #e5e7eb',
|
|
@@ -289,21 +512,105 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
289
512
|
padding: 16,
|
|
290
513
|
textAlign: 'left',
|
|
291
514
|
width: '100%',
|
|
292
|
-
}, type: "button", children: [_jsxs("div", { style: { display: 'grid', gap: 4 }, children: [_jsxs("div", { style: { alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: 8 }, children: [_jsx("span", { style: { fontSize: 16, fontWeight: 700 }, children: componentName }), _jsxs("span", { style: { color: '#6b7280', fontSize: 13, fontWeight: 500 }, children: [usages.length, " usage", usages.length === 1 ? '' : 's'] }),
|
|
515
|
+
}, type: "button", children: [_jsxs("div", { style: { display: 'grid', gap: 4 }, children: [_jsxs("div", { style: { alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: 8 }, children: [_jsx("span", { style: { fontSize: 16, fontWeight: 700 }, children: componentName }), _jsxs("span", { style: { color: '#6b7280', fontSize: 13, fontWeight: 500 }, children: [usages.length, " usage", usages.length === 1 ? '' : 's'] }), isUnknown ? (_jsx("span", { style: { color: '#b45309', fontSize: 13, fontWeight: 600 }, children: "Missing from manifest" })) : null] }), definition ? (_jsxs("div", { style: { color: '#6b7280', fontSize: 13 }, children: [definition.sidebarCategory || 'Uncategorized', definition.scope ? ` - ${definition.scope}` : ''] })) : null] }), _jsx("span", { style: { color: '#6b7280', fontSize: 18, lineHeight: 1 }, children: isOpen ? '−' : '+' })] }), isOpen ? (_jsxs("div", { style: {
|
|
293
516
|
borderTop: '1px solid #e5e7eb',
|
|
294
517
|
display: 'grid',
|
|
295
518
|
gap: 0,
|
|
296
|
-
gridTemplateColumns: '
|
|
297
|
-
}, children: [_jsxs("div", { style: {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
519
|
+
gridTemplateColumns: 'minmax(0, 1.45fr) minmax(300px, 0.9fr)',
|
|
520
|
+
}, children: [_jsxs("div", { style: {
|
|
521
|
+
borderRight: '1px solid #e5e7eb',
|
|
522
|
+
display: 'grid',
|
|
523
|
+
gap: 18,
|
|
524
|
+
padding: 16,
|
|
525
|
+
}, children: [_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 10px', fontSize: 15 }, children: "Used On" }), usages.length ? (_jsx("div", { style: { display: 'grid', gap: 8 }, children: usages.map((usage) => (_jsxs("a", { href: formatAdminURL({
|
|
526
|
+
adminRoute,
|
|
527
|
+
path: `/collections/${usage.sourceCollection}/${usage.sourceDocumentId}`,
|
|
528
|
+
}), style: {
|
|
529
|
+
border: '1px solid #e5e7eb',
|
|
530
|
+
borderRadius: 6,
|
|
531
|
+
color: 'inherit',
|
|
532
|
+
display: 'block',
|
|
533
|
+
padding: 12,
|
|
534
|
+
textDecoration: 'none',
|
|
535
|
+
}, children: [_jsx("div", { style: { fontWeight: 600 }, children: usage.sourceTitle || usage.sourceSlug || usage.sourceDocumentId }), _jsxs("div", { style: { color: '#6b7280', fontSize: 12, marginTop: 2 }, children: [usage.sourceCollection, " - ", usage.modulePath] })] }, usage.usageKey))) })) : (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: "No indexed usages yet." }))] }), _jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 10px', fontSize: 15 }, children: "Props" }), definition ? (Object.values(definition.categories).map((category) => renderCategory(category))) : (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: "No manifest definition available." }))] }), _jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 10px', fontSize: 15 }, children: "Update Prop Across Site" }), editableProps.length ? (_jsxs("div", { style: {
|
|
536
|
+
border: '1px solid #e5e7eb',
|
|
537
|
+
borderRadius: 8,
|
|
538
|
+
display: 'grid',
|
|
539
|
+
gap: 10,
|
|
540
|
+
padding: 12,
|
|
541
|
+
}, children: [_jsxs("label", { style: { display: 'grid', gap: 6, fontSize: 13, fontWeight: 600 }, children: ["Prop", _jsx("select", { onChange: (event) => {
|
|
542
|
+
const nextProp = editableProps.find((option) => option.path === event.target.value);
|
|
543
|
+
updatePropDraft(componentName, {
|
|
544
|
+
error: undefined,
|
|
545
|
+
propPath: event.target.value,
|
|
546
|
+
result: undefined,
|
|
547
|
+
status: 'idle',
|
|
548
|
+
value: nextProp
|
|
549
|
+
? stringifyPropValue(nextProp.defaultValue, nextProp.type)
|
|
550
|
+
: '',
|
|
551
|
+
});
|
|
552
|
+
}, style: {
|
|
553
|
+
border: '1px solid #d1d5db',
|
|
554
|
+
borderRadius: 6,
|
|
555
|
+
color: '#111827',
|
|
556
|
+
maxWidth: 520,
|
|
557
|
+
padding: '8px 10px',
|
|
558
|
+
width: '100%',
|
|
559
|
+
}, value: propDraft.propPath, children: editableProps.map((option) => (_jsx("option", { value: option.path, children: option.label }, option.path))) })] }), selectedEditableProp?.type === 'boolean' ? (_jsxs("label", { style: { alignItems: 'center', display: 'flex', gap: 8, fontSize: 13 }, children: [_jsx("input", { checked: Boolean(propDraft.value), onChange: (event) => updatePropDraft(componentName, {
|
|
560
|
+
result: undefined,
|
|
561
|
+
status: 'idle',
|
|
562
|
+
value: event.target.checked,
|
|
563
|
+
}), type: "checkbox" }), "Value"] })) : selectedEditableProp?.options?.length ? (_jsxs("label", { style: { display: 'grid', gap: 6, fontSize: 13, fontWeight: 600 }, children: ["Value", _jsx("select", { onChange: (event) => updatePropDraft(componentName, {
|
|
564
|
+
result: undefined,
|
|
565
|
+
status: 'idle',
|
|
566
|
+
value: event.target.value,
|
|
567
|
+
}), style: {
|
|
568
|
+
border: '1px solid #d1d5db',
|
|
569
|
+
borderRadius: 6,
|
|
570
|
+
color: '#111827',
|
|
571
|
+
maxWidth: 520,
|
|
572
|
+
padding: '8px 10px',
|
|
573
|
+
width: '100%',
|
|
574
|
+
}, value: String(propDraft.value), children: selectedEditableProp.options.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value))) })] })) : (_jsxs("label", { style: { display: 'grid', gap: 6, fontSize: 13, fontWeight: 600 }, children: ["Value", _jsx("input", { onChange: (event) => updatePropDraft(componentName, {
|
|
575
|
+
result: undefined,
|
|
576
|
+
status: 'idle',
|
|
577
|
+
value: event.target.value,
|
|
578
|
+
}), style: {
|
|
579
|
+
border: '1px solid #d1d5db',
|
|
580
|
+
borderRadius: 6,
|
|
581
|
+
color: '#111827',
|
|
582
|
+
maxWidth: 520,
|
|
583
|
+
padding: '8px 10px',
|
|
584
|
+
width: '100%',
|
|
585
|
+
}, type: selectedEditableProp?.type === 'number' ? 'number' : 'text', value: String(propDraft.value) })] })), _jsxs("div", { style: { color: '#6b7280', fontSize: 12 }, children: ["Scope: ", selectedSources.length ? sourceFilterLabel : 'all indexed usages'] }), _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8 }, children: [_jsx("button", { disabled: propDraft.status === 'previewing' || propDraft.status === 'applying', onClick: () => void runPropUpdate(componentName, editableProps, 'preview', usages), style: buttonStyle('secondary'), type: "button", children: propDraft.status === 'previewing' ? 'Checking...' : 'Dry Run' }), _jsx("button", { disabled: propDraft.status === 'previewing' ||
|
|
586
|
+
propDraft.status === 'applying' ||
|
|
587
|
+
!propDraft.result, onClick: () => void runPropUpdate(componentName, editableProps, 'apply', usages), style: {
|
|
588
|
+
...buttonStyle('primary'),
|
|
589
|
+
opacity: propDraft.result ? 1 : 0.5,
|
|
590
|
+
}, type: "button", children: propDraft.status === 'applying' ? 'Applying...' : 'Apply Update' })] }), propDraft.error ? (_jsx("p", { style: { color: '#b91c1c', fontSize: 13, margin: 0 }, children: propDraft.error })) : null, propDraft.result ? (_jsxs("div", { style: { color: '#374151', fontSize: 13 }, children: [propDraft.result.mode === 'apply' ? 'Updated' : 'Would update', ' ', propDraft.result.occurrenceCount, " occurrence", propDraft.result.occurrenceCount === 1 ? '' : 's', " in", ' ', propDraft.result.documentCount, " document", propDraft.result.documentCount === 1 ? '' : 's', "."] })) : null] })) : (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: "No scalar props are available for bulk updates." }))] })] }), _jsxs("div", { style: { padding: 16 }, children: [_jsxs("div", { style: {
|
|
591
|
+
alignItems: 'center',
|
|
592
|
+
display: 'flex',
|
|
593
|
+
gap: 12,
|
|
594
|
+
justifyContent: 'space-between',
|
|
595
|
+
marginBottom: 10,
|
|
596
|
+
}, children: [_jsxs("div", { children: [_jsx("h3", { style: { margin: 0, fontSize: 15 }, children: "Preview" }), previewState.data?.source ? (_jsxs("div", { style: { color: '#6b7280', fontSize: 12, marginTop: 2 }, children: ["Rendering saved usage from ", previewState.data.source.title || previewState.data.source.documentId] })) : null] }), previewState.status === 'loading' ? (_jsx("span", { style: { color: '#6b7280', fontSize: 12 }, children: "Loading preview..." })) : null] }), !previewUrl ? (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: "Configure a Nitrogen development or frontend URL to enable component previews." })) : previewState.status === 'error' ? (_jsx("p", { style: { color: '#b91c1c', margin: 0 }, children: previewState.error })) : (_jsx("div", { style: {
|
|
597
|
+
background: '#f9fafb',
|
|
598
|
+
border: '1px solid #e5e7eb',
|
|
599
|
+
borderRadius: 8,
|
|
600
|
+
overflow: 'hidden',
|
|
601
|
+
position: 'relative',
|
|
602
|
+
width: '100%',
|
|
603
|
+
aspectRatio: '4 / 3',
|
|
604
|
+
}, children: _jsx("iframe", { ref: (element) => {
|
|
605
|
+
previewFrameRefs.current[componentName] = element;
|
|
606
|
+
}, onLoad: () => postPreviewDataToFrame(componentName, previewState.data), src: componentPreviewUrl, style: {
|
|
607
|
+
border: 0,
|
|
304
608
|
display: 'block',
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
609
|
+
height: '100%',
|
|
610
|
+
left: 0,
|
|
611
|
+
position: 'absolute',
|
|
612
|
+
top: 0,
|
|
613
|
+
width: '100%',
|
|
614
|
+
}, title: `${componentName} preview` }) }))] })] })) : null] }, componentName));
|
|
308
615
|
})] }));
|
|
309
616
|
}
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { findAllDocs, NITROGEN_COMPONENT_CATALOG_COLLECTION, NITROGEN_COMPONENT_USAGE_COLLECTION, } from '../inventory/indexing';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { findAllDocs, NITROGEN_COMPONENT_CATALOG_COLLECTION, NITROGEN_COMPONENT_USAGE_COLLECTION, } from '../inventory/indexing.js';
|
|
3
|
+
import { getNitrogenSettings } from '../endpoints/helpers.js';
|
|
4
|
+
import NitrogenComponentInventoryClient from './NitrogenComponentInventoryClient.js';
|
|
5
|
+
import NitrogenComponentInventoryReindexButton from './NitrogenComponentInventoryReindexButton.js';
|
|
6
|
+
function buildComponentPreviewUrl(settings) {
|
|
7
|
+
const frontendBaseUrl = String(settings.frontendDevUrl || settings.developmentUrl || settings.frontendUrl || '').replace(/\/$/, '');
|
|
8
|
+
if (!frontendBaseUrl) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
const url = new URL('/nitrogen-component-preview', frontendBaseUrl);
|
|
12
|
+
return url.toString();
|
|
13
|
+
}
|
|
5
14
|
const NitrogenComponentInventoryField = async ({ payload, req, }) => {
|
|
6
|
-
const [catalogDocs, usageDocs] = await Promise.all([
|
|
15
|
+
const [catalogDocs, usageDocs, nitrogenSettings] = await Promise.all([
|
|
7
16
|
findAllDocs(payload, NITROGEN_COMPONENT_CATALOG_COLLECTION),
|
|
8
17
|
findAllDocs(payload, NITROGEN_COMPONENT_USAGE_COLLECTION),
|
|
18
|
+
getNitrogenSettings(payload).catch(() => null),
|
|
9
19
|
]);
|
|
10
20
|
const catalogByName = new Map(catalogDocs.map((doc) => [doc.componentName, doc.definition]));
|
|
11
21
|
const usageByName = new Map();
|
|
@@ -21,6 +31,6 @@ const NitrogenComponentInventoryField = async ({ payload, req, }) => {
|
|
|
21
31
|
isUnknown: !catalogByName.get(componentName),
|
|
22
32
|
usages: usageByName.get(componentName) || [],
|
|
23
33
|
}));
|
|
24
|
-
return (_jsxs("div", { style: { display: 'grid', gap: 24, paddingBottom: 24 }, children: [_jsxs("div", { style: { display: 'grid', gap: 12 }, children: [_jsx("p", { style: { margin: 0, color: '#4b5563', maxWidth: 920 }, children: "Inventory view for the registered Nitrogen component manifest and all indexed usages across Nitrogen-enabled documents and templates." }), _jsx(NitrogenComponentInventoryReindexButton, {})] }), _jsx(NitrogenComponentInventoryClient, { adminRoute: req.payload.config.routes.admin, components: components })] }));
|
|
34
|
+
return (_jsxs("div", { style: { display: 'grid', gap: 24, paddingBottom: 24 }, children: [_jsxs("div", { style: { display: 'grid', gap: 12 }, children: [_jsx("p", { style: { margin: 0, color: '#4b5563', maxWidth: 920 }, children: "Inventory view for the registered Nitrogen component manifest and all indexed usages across Nitrogen-enabled documents and templates." }), _jsx(NitrogenComponentInventoryReindexButton, {})] }), _jsx(NitrogenComponentInventoryClient, { adminRoute: req.payload.config.routes.admin, components: components, previewUrl: nitrogenSettings ? buildComponentPreviewUrl(nitrogenSettings) : '' })] }));
|
|
25
35
|
};
|
|
26
36
|
export default NitrogenComponentInventoryField;
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @ts-expect-error The consuming Payload app provides @payloadcms/next at runtime.
|
|
3
3
|
import { DefaultTemplate } from '@payloadcms/next/templates';
|
|
4
|
-
import { findAllDocs, NITROGEN_COMPONENT_CATALOG_COLLECTION, NITROGEN_COMPONENT_USAGE_COLLECTION, } from '../inventory/indexing';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
4
|
+
import { findAllDocs, NITROGEN_COMPONENT_CATALOG_COLLECTION, NITROGEN_COMPONENT_USAGE_COLLECTION, } from '../inventory/indexing.js';
|
|
5
|
+
import { getNitrogenSettings } from '../endpoints/helpers.js';
|
|
6
|
+
import NitrogenComponentInventoryClient from './NitrogenComponentInventoryClient.js';
|
|
7
|
+
import NitrogenComponentInventoryReindexButton from './NitrogenComponentInventoryReindexButton.js';
|
|
8
|
+
function buildComponentPreviewUrl(settings) {
|
|
9
|
+
const frontendBaseUrl = String(settings.frontendDevUrl || settings.developmentUrl || settings.frontendUrl || '').replace(/\/$/, '');
|
|
10
|
+
if (!frontendBaseUrl) {
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
const url = new URL('/nitrogen-component-preview', frontendBaseUrl);
|
|
14
|
+
return url.toString();
|
|
15
|
+
}
|
|
7
16
|
export default async function NitrogenComponentInventoryView({ clientConfig, initPageResult, i18n, locale, params, searchParams, viewActions, viewType, }) {
|
|
8
17
|
const payload = initPageResult.req.payload;
|
|
9
|
-
const [catalogDocs, usageDocs] = await Promise.all([
|
|
18
|
+
const [catalogDocs, usageDocs, nitrogenSettings] = await Promise.all([
|
|
10
19
|
findAllDocs(payload, NITROGEN_COMPONENT_CATALOG_COLLECTION),
|
|
11
20
|
findAllDocs(payload, NITROGEN_COMPONENT_USAGE_COLLECTION),
|
|
21
|
+
getNitrogenSettings(payload).catch(() => null),
|
|
12
22
|
]);
|
|
13
23
|
const catalogByName = new Map(catalogDocs.map((doc) => [doc.componentName, doc.definition]));
|
|
14
24
|
const usageByName = new Map();
|
|
@@ -24,5 +34,5 @@ export default async function NitrogenComponentInventoryView({ clientConfig, ini
|
|
|
24
34
|
isUnknown: !catalogByName.get(componentName),
|
|
25
35
|
usages: usageByName.get(componentName) || [],
|
|
26
36
|
}));
|
|
27
|
-
return (_jsx(DefaultTemplate, { i18n: i18n, locale: locale, params: params, payload: payload, req: initPageResult.req, searchParams: searchParams, user: initPageResult.req.user, viewActions: viewActions, viewType: viewType, visibleEntities: initPageResult.visibleEntities, children: _jsxs("div", { style: { padding: 24 }, children: [_jsxs("div", { style: { display: 'grid', gap: 12, marginBottom: 24 }, children: [_jsx("h1", { style: { margin: 0, fontSize: 28 }, children: "Nitrogen Components" }), _jsx("p", { style: { margin: 0, color: '#4b5563', maxWidth: 920 }, children: "Inventory view for the registered Nitrogen component manifest and all indexed usages across Nitrogen-enabled documents and templates." }), _jsx(NitrogenComponentInventoryReindexButton, {})] }), _jsx(NitrogenComponentInventoryClient, { adminRoute: clientConfig.routes.admin, components: components })] }) }));
|
|
37
|
+
return (_jsx(DefaultTemplate, { i18n: i18n, locale: locale, params: params, payload: payload, req: initPageResult.req, searchParams: searchParams, user: initPageResult.req.user, viewActions: viewActions, viewType: viewType, visibleEntities: initPageResult.visibleEntities, children: _jsxs("div", { style: { padding: 24 }, children: [_jsxs("div", { style: { display: 'grid', gap: 12, marginBottom: 24 }, children: [_jsx("h1", { style: { margin: 0, fontSize: 28 }, children: "Nitrogen Components" }), _jsx("p", { style: { margin: 0, color: '#4b5563', maxWidth: 920 }, children: "Inventory view for the registered Nitrogen component manifest and all indexed usages across Nitrogen-enabled documents and templates." }), _jsx(NitrogenComponentInventoryReindexButton, {})] }), _jsx(NitrogenComponentInventoryClient, { adminRoute: clientConfig.routes.admin, components: components, previewUrl: nitrogenSettings ? buildComponentPreviewUrl(nitrogenSettings) : '' })] }) }));
|
|
28
38
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import NitrogenDataViewerRuntime from './NitrogenDataViewerRuntime';
|
|
2
|
+
import NitrogenDataViewerRuntime from './NitrogenDataViewerRuntime.js';
|
|
3
3
|
const NitrogenDataViewer = ({ field, path, value }) => {
|
|
4
4
|
const label = typeof field.label === 'string' ? field.label : field.name;
|
|
5
5
|
const description = typeof field.admin?.description === 'string' ? field.admin.description : undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import dynamic from 'next/dynamic';
|
|
3
|
-
export const NitrogenEditButton = dynamic(() => import('./NitrogenEditButtonRuntime').then((module) => module.NitrogenEditButtonRuntime), {
|
|
2
|
+
import dynamic from 'next/dynamic.js';
|
|
3
|
+
export const NitrogenEditButton = dynamic(() => import('./NitrogenEditButtonRuntime.js').then((module) => module.NitrogenEditButtonRuntime), {
|
|
4
4
|
loading: () => null,
|
|
5
5
|
ssr: false,
|
|
6
6
|
});
|