@nitrogenbuilder/connector-payload 0.1.34 → 0.1.35

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.
Files changed (38) hide show
  1. package/dist/collections/NitrogenComponentCatalog.d.ts +2 -0
  2. package/dist/collections/NitrogenComponentCatalog.js +44 -0
  3. package/dist/collections/NitrogenComponentUsage.d.ts +2 -0
  4. package/dist/collections/NitrogenComponentUsage.js +81 -0
  5. package/dist/components/LockedJsonField.d.ts +2 -9
  6. package/dist/components/LockedJsonField.js +7 -6
  7. package/dist/components/LockedJsonFieldRuntime.d.ts +3 -7
  8. package/dist/components/LockedJsonFieldRuntime.js +21 -22
  9. package/dist/components/NitrogenComponentInventoryClient.d.ts +13 -0
  10. package/dist/components/NitrogenComponentInventoryClient.js +309 -0
  11. package/dist/components/NitrogenComponentInventoryField.d.ts +3 -0
  12. package/dist/components/NitrogenComponentInventoryField.js +26 -0
  13. package/dist/components/NitrogenComponentInventoryNavLink.d.ts +1 -0
  14. package/dist/components/NitrogenComponentInventoryNavLink.js +75 -0
  15. package/dist/components/NitrogenComponentInventoryReindexButton.d.ts +1 -0
  16. package/dist/components/NitrogenComponentInventoryReindexButton.js +91 -0
  17. package/dist/components/NitrogenComponentInventoryView.d.ts +2 -0
  18. package/dist/components/NitrogenComponentInventoryView.js +28 -0
  19. package/dist/components/NitrogenDataViewer.d.ts +2 -2
  20. package/dist/components/NitrogenDataViewer.js +7 -6
  21. package/dist/components/NitrogenDataViewerRuntime.d.ts +6 -2
  22. package/dist/components/NitrogenDataViewerRuntime.js +24 -40
  23. package/dist/components/NitrogenNavGroup.d.ts +1 -0
  24. package/dist/components/NitrogenNavGroup.js +74 -0
  25. package/dist/editor/NitrogenEditorPage.d.ts +1 -2
  26. package/dist/endpoints/component-inventory.d.ts +6 -0
  27. package/dist/endpoints/component-inventory.js +20 -0
  28. package/dist/frontend/NitrogenWrapper.d.ts +1 -2
  29. package/dist/globals/NitrogenComponents.d.ts +2 -0
  30. package/dist/globals/NitrogenComponents.js +22 -0
  31. package/dist/index.d.ts +12 -2
  32. package/dist/index.js +94 -3
  33. package/dist/inventory/indexing.d.ts +31 -0
  34. package/dist/inventory/indexing.js +230 -0
  35. package/dist/inventory/manifest.d.ts +3 -0
  36. package/dist/inventory/manifest.js +22 -0
  37. package/dist/types.d.ts +35 -1
  38. package/package.json +7 -2
@@ -0,0 +1,2 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare const NitrogenComponentCatalog: CollectionConfig;
@@ -0,0 +1,44 @@
1
+ const isAuthenticated = ({ req }) => !!req.user;
2
+ export const NitrogenComponentCatalog = {
3
+ slug: 'nitrogen-component-catalog',
4
+ admin: {
5
+ hidden: true,
6
+ useAsTitle: 'componentName',
7
+ defaultColumns: ['componentName', 'sidebarCategory', 'scope', 'updatedAt'],
8
+ group: 'Nitrogen',
9
+ },
10
+ access: {
11
+ read: isAuthenticated,
12
+ create: isAuthenticated,
13
+ update: isAuthenticated,
14
+ delete: isAuthenticated,
15
+ },
16
+ fields: [
17
+ {
18
+ name: 'componentName',
19
+ type: 'text',
20
+ required: true,
21
+ unique: true,
22
+ },
23
+ {
24
+ name: 'scope',
25
+ type: 'text',
26
+ },
27
+ {
28
+ name: 'sidebarCategory',
29
+ type: 'text',
30
+ },
31
+ {
32
+ name: 'icon',
33
+ type: 'text',
34
+ },
35
+ {
36
+ name: 'definition',
37
+ type: 'json',
38
+ },
39
+ {
40
+ name: 'source',
41
+ type: 'text',
42
+ },
43
+ ],
44
+ };
@@ -0,0 +1,2 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare const NitrogenComponentUsage: CollectionConfig;
@@ -0,0 +1,81 @@
1
+ const isAuthenticated = ({ req }) => !!req.user;
2
+ export const NitrogenComponentUsage = {
3
+ slug: 'nitrogen-component-usage',
4
+ admin: {
5
+ hidden: true,
6
+ useAsTitle: 'usageKey',
7
+ defaultColumns: ['componentName', 'sourceCollection', 'sourceTitle', 'updatedAt'],
8
+ group: 'Nitrogen',
9
+ },
10
+ access: {
11
+ read: isAuthenticated,
12
+ create: isAuthenticated,
13
+ update: isAuthenticated,
14
+ delete: isAuthenticated,
15
+ },
16
+ fields: [
17
+ {
18
+ name: 'usageKey',
19
+ type: 'text',
20
+ required: true,
21
+ unique: true,
22
+ },
23
+ {
24
+ name: 'componentName',
25
+ type: 'text',
26
+ required: true,
27
+ },
28
+ {
29
+ name: 'depth',
30
+ type: 'number',
31
+ },
32
+ {
33
+ name: 'sourceCollection',
34
+ type: 'text',
35
+ required: true,
36
+ },
37
+ {
38
+ name: 'sourceType',
39
+ type: 'text',
40
+ required: true,
41
+ },
42
+ {
43
+ name: 'sourceDocumentId',
44
+ type: 'text',
45
+ required: true,
46
+ },
47
+ {
48
+ name: 'sourceTitle',
49
+ type: 'text',
50
+ },
51
+ {
52
+ name: 'sourceSlug',
53
+ type: 'text',
54
+ },
55
+ {
56
+ name: 'sourceStatus',
57
+ type: 'text',
58
+ },
59
+ {
60
+ name: 'moduleId',
61
+ type: 'text',
62
+ },
63
+ {
64
+ name: 'modulePath',
65
+ type: 'text',
66
+ required: true,
67
+ },
68
+ {
69
+ name: 'parentComponentName',
70
+ type: 'text',
71
+ },
72
+ {
73
+ name: 'parentModuleId',
74
+ type: 'text',
75
+ },
76
+ {
77
+ name: 'parentModulePath',
78
+ type: 'text',
79
+ },
80
+ ],
81
+ };
@@ -1,9 +1,2 @@
1
- export declare const LockedJsonField: import("react").ComponentType<{
2
- path: string;
3
- field: {
4
- name: string;
5
- admin?: {
6
- description?: string;
7
- };
8
- };
9
- }>;
1
+ import type { JSONFieldServerComponent } from 'payload';
2
+ export declare const LockedJsonField: JSONFieldServerComponent;
@@ -1,6 +1,7 @@
1
- 'use client';
2
- import dynamic from 'next/dynamic';
3
- export const LockedJsonField = dynamic(() => import('./LockedJsonFieldRuntime').then((module) => module.LockedJsonFieldRuntime), {
4
- loading: () => null,
5
- ssr: false,
6
- });
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { LockedJsonFieldRuntime } from './LockedJsonFieldRuntime';
3
+ export const LockedJsonField = ({ field, value }) => {
4
+ const label = typeof field.label === 'string' ? field.label : field.name;
5
+ const description = typeof field.admin?.description === 'string' ? field.admin.description : undefined;
6
+ return (_jsx(LockedJsonFieldRuntime, { description: description, label: label, value: value }));
7
+ };
@@ -1,10 +1,6 @@
1
1
  import React from 'react';
2
2
  export declare const LockedJsonFieldRuntime: React.FC<{
3
- path: string;
4
- field: {
5
- name: string;
6
- admin?: {
7
- description?: string;
8
- };
9
- };
3
+ label: string;
4
+ description?: string;
5
+ value?: unknown;
10
6
  }>;
@@ -1,38 +1,37 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState } from 'react';
4
- import { useField } from '@payloadcms/ui';
5
- export const LockedJsonFieldRuntime = ({ path, field }) => {
6
- const [unlocked, setUnlocked] = useState(false);
7
- const { value, setValue } = useField({ path });
8
- const displayValue = value ? JSON.stringify(value, null, 2) : '';
9
- return (_jsxs("div", { style: { marginBottom: '1.5rem' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.5rem' }, children: [_jsx("label", { style: { fontWeight: 500 }, children: field.name }), _jsx("button", { type: "button", onClick: () => setUnlocked(!unlocked), style: {
3
+ import { useMemo, useState } from 'react';
4
+ export const LockedJsonFieldRuntime = ({ description, label, value }) => {
5
+ const [copied, setCopied] = useState(false);
6
+ const displayValue = useMemo(() => {
7
+ if (typeof value === 'string') {
8
+ return value;
9
+ }
10
+ return value != null ? JSON.stringify(value, null, 2) : '';
11
+ }, [value]);
12
+ return (_jsxs("div", { style: { marginBottom: '1.5rem' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.5rem' }, children: [_jsx("label", { style: { fontWeight: 500 }, children: label }), _jsx("button", { type: "button", onClick: () => {
13
+ navigator.clipboard.writeText(displayValue).then(() => {
14
+ setCopied(true);
15
+ setTimeout(() => setCopied(false), 2000);
16
+ });
17
+ }, style: {
10
18
  padding: '4px 12px',
11
19
  fontSize: '12px',
12
- background: unlocked ? '#ef4444' : '#6b7280',
20
+ background: '#6b7280',
13
21
  color: '#fff',
14
22
  border: 'none',
15
23
  borderRadius: '4px',
16
24
  cursor: 'pointer',
17
- }, children: unlocked ? 'Lock' : 'Unlock Edit' })] }), _jsx("textarea", { value: displayValue, readOnly: !unlocked, onChange: (e) => {
18
- if (!unlocked)
19
- return;
20
- try {
21
- setValue(JSON.parse(e.target.value));
22
- }
23
- catch {
24
- // Allow typing invalid JSON while editing
25
- }
26
- }, style: {
25
+ }, children: copied ? 'Copied' : 'Copy' })] }), _jsx("textarea", { value: displayValue, readOnly: true, style: {
27
26
  width: '100%',
28
27
  minHeight: '120px',
29
28
  fontFamily: 'monospace',
30
29
  fontSize: '12px',
31
30
  padding: '8px',
32
- background: unlocked ? '#1a1a2e' : '#111',
33
- color: unlocked ? '#e2e8f0' : '#888',
34
- border: `1px solid ${unlocked ? '#ef4444' : '#333'}`,
31
+ background: '#111',
32
+ color: '#888',
33
+ border: '1px solid #333',
35
34
  borderRadius: '4px',
36
35
  resize: 'vertical',
37
- } }), field.admin?.description && (_jsx("p", { style: { fontSize: '12px', color: '#888', marginTop: '4px' }, children: field.admin.description }))] }));
36
+ } }), description && (_jsx("p", { style: { fontSize: '12px', color: '#888', marginTop: '4px' }, children: description }))] }));
38
37
  };
@@ -0,0 +1,13 @@
1
+ import type { ComponentManifestEntry } from '@nitrogenbuilder/types';
2
+ import type { NitrogenComponentUsageDoc } from '../types';
3
+ type InventoryClientProps = {
4
+ adminRoute: string;
5
+ components: Array<{
6
+ componentName: string;
7
+ definition?: ComponentManifestEntry;
8
+ isUnknown: boolean;
9
+ usages: NitrogenComponentUsageDoc[];
10
+ }>;
11
+ };
12
+ export default function NitrogenComponentInventoryClient({ adminRoute, components, }: InventoryClientProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,309 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useMemo, useRef, useState } from 'react';
4
+ import { formatAdminURL } from 'payload/shared';
5
+ function formatSourceLabel(slug) {
6
+ if (slug === 'nitrogen-templates') {
7
+ return 'Nitrogen Templates';
8
+ }
9
+ return slug
10
+ .split(/[-_]/g)
11
+ .filter(Boolean)
12
+ .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
13
+ .join(' ');
14
+ }
15
+ function renderProp(name, prop, depth = 0) {
16
+ 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
+ }
18
+ function renderGroup(group) {
19
+ return (_jsxs("div", { style: { marginTop: 12 }, children: [_jsx("h4", { style: { margin: '0 0 6px', fontSize: 14 }, children: group.label }), _jsx("div", { style: { display: 'grid', gap: 6 }, children: Object.entries(group.props).map(([propName, prop]) => renderProp(propName, prop)) })] }, group.key));
20
+ }
21
+ function renderCategory(category) {
22
+ return (_jsxs("div", { style: { marginTop: 16 }, children: [_jsx("h3", { style: { margin: '0 0 8px', fontSize: 16 }, children: category.label }), Object.values(category.groups).map((group) => renderGroup(group))] }, category.key));
23
+ }
24
+ function buttonStyle(kind) {
25
+ return {
26
+ appearance: 'none',
27
+ background: kind === 'primary' ? '#111827' : '#ffffff',
28
+ border: `1px solid ${kind === 'primary' ? '#111827' : '#d1d5db'}`,
29
+ borderRadius: 8,
30
+ color: kind === 'primary' ? '#ffffff' : '#111827',
31
+ cursor: 'pointer',
32
+ fontSize: 13,
33
+ fontWeight: 600,
34
+ padding: '8px 12px',
35
+ };
36
+ }
37
+ function buildRelationshipSummaries(usages, getRelationshipName) {
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, }) {
76
+ const sourceFilterRef = useRef(null);
77
+ const allComponentNames = useMemo(() => components.map(({ componentName }) => componentName), [components]);
78
+ const [searchValue, setSearchValue] = useState('');
79
+ const [sourceSearchValue, setSourceSearchValue] = useState('');
80
+ const [selectedSources, setSelectedSources] = useState([]);
81
+ const [openNames, setOpenNames] = useState({});
82
+ const sourceOptions = useMemo(() => {
83
+ const collectionOptions = new Map();
84
+ const documentOptions = new Map();
85
+ components.forEach(({ usages }) => {
86
+ usages.forEach((usage) => {
87
+ const collectionLabel = formatSourceLabel(usage.sourceCollection);
88
+ const collectionKey = `collection:${usage.sourceCollection}`;
89
+ const documentLabel = usage.sourceTitle || usage.sourceSlug || usage.sourceDocumentId;
90
+ const documentKey = `document:${usage.sourceCollection}:${usage.sourceDocumentId}`;
91
+ if (!collectionOptions.has(collectionKey)) {
92
+ collectionOptions.set(collectionKey, {
93
+ description: usage.sourceCollection,
94
+ key: collectionKey,
95
+ kind: 'collection',
96
+ label: collectionLabel,
97
+ searchValue: `${collectionLabel} ${usage.sourceCollection}`.toLowerCase(),
98
+ });
99
+ }
100
+ if (!documentOptions.has(documentKey)) {
101
+ documentOptions.set(documentKey, {
102
+ description: `${collectionLabel} (${usage.sourceCollection})`,
103
+ key: documentKey,
104
+ kind: 'document',
105
+ label: documentLabel,
106
+ searchValue: `${documentLabel} ${collectionLabel} ${usage.sourceCollection} ${usage.sourceSlug || ''}`.toLowerCase(),
107
+ });
108
+ }
109
+ });
110
+ });
111
+ return [
112
+ ...Array.from(collectionOptions.values()).sort((a, b) => a.label.localeCompare(b.label)),
113
+ ...Array.from(documentOptions.values()).sort((a, b) => a.label.localeCompare(b.label)),
114
+ ];
115
+ }, [components]);
116
+ const visibleSourceOptions = useMemo(() => {
117
+ const normalizedQuery = sourceSearchValue.trim().toLowerCase();
118
+ if (!normalizedQuery) {
119
+ return sourceOptions;
120
+ }
121
+ return sourceOptions.filter((option) => option.searchValue.includes(normalizedQuery));
122
+ }, [sourceOptions, sourceSearchValue]);
123
+ const sourceFilteredComponents = useMemo(() => {
124
+ const hasSourceFilter = selectedSources.length > 0;
125
+ return components
126
+ .map((component) => {
127
+ const filteredUsages = hasSourceFilter
128
+ ? component.usages.filter((usage) => {
129
+ const collectionKey = `collection:${usage.sourceCollection}`;
130
+ const documentKey = `document:${usage.sourceCollection}:${usage.sourceDocumentId}`;
131
+ return selectedSources.includes(collectionKey) || selectedSources.includes(documentKey);
132
+ })
133
+ : component.usages;
134
+ return {
135
+ ...component,
136
+ usages: filteredUsages,
137
+ };
138
+ })
139
+ .filter((component) => {
140
+ return !hasSourceFilter || component.usages.length > 0;
141
+ });
142
+ }, [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
+ const filteredComponents = useMemo(() => {
153
+ const normalizedQuery = searchValue.trim().toLowerCase();
154
+ return sourceFilteredComponents.filter((component) => {
155
+ return !normalizedQuery || component.componentName.toLowerCase().includes(normalizedQuery);
156
+ });
157
+ }, [searchValue, sourceFilteredComponents]);
158
+ const filteredComponentNames = useMemo(() => filteredComponents.map(({ componentName }) => componentName), [filteredComponents]);
159
+ useEffect(() => {
160
+ function handlePointerDown(event) {
161
+ const details = sourceFilterRef.current;
162
+ const target = event.target;
163
+ if (!(target instanceof Node) || !details?.open || details.contains(target)) {
164
+ return;
165
+ }
166
+ details.open = false;
167
+ }
168
+ document.addEventListener('mousedown', handlePointerDown);
169
+ return () => {
170
+ document.removeEventListener('mousedown', handlePointerDown);
171
+ };
172
+ }, []);
173
+ const expandAll = () => {
174
+ setOpenNames((current) => ({
175
+ ...current,
176
+ ...Object.fromEntries(filteredComponentNames.map((name) => [name, true])),
177
+ }));
178
+ };
179
+ const collapseAll = () => {
180
+ if (!filteredComponentNames.length) {
181
+ return;
182
+ }
183
+ setOpenNames((current) => {
184
+ const next = { ...current };
185
+ filteredComponentNames.forEach((name) => {
186
+ delete next[name];
187
+ });
188
+ return next;
189
+ });
190
+ };
191
+ const selectedSourceLabels = useMemo(() => sourceOptions
192
+ .filter((option) => selectedSources.includes(option.key))
193
+ .map((option) => option.label), [selectedSources, sourceOptions]);
194
+ const sourceFilterLabel = selectedSourceLabels.length === 0
195
+ ? 'All Sources'
196
+ : selectedSourceLabels.length === 1
197
+ ? selectedSourceLabels[0]
198
+ : `${selectedSourceLabels.length} sources`;
199
+ return (_jsxs("div", { style: { display: 'grid', gap: 16 }, children: [_jsxs("div", { style: {
200
+ alignItems: 'center',
201
+ display: 'flex',
202
+ flexWrap: 'wrap',
203
+ gap: 8,
204
+ justifyContent: 'space-between',
205
+ }, children: [_jsxs("div", { style: { color: '#4b5563', fontSize: 13 }, children: [filteredComponents.length, " component", filteredComponents.length === 1 ? '' : 's', filteredComponents.length !== allComponentNames.length
206
+ ? ` of ${allComponentNames.length}`
207
+ : ''] }), _jsxs("div", { style: { alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: 8 }, children: [_jsx("input", { onChange: (event) => setSearchValue(event.target.value), placeholder: "Search components...", style: {
208
+ background: '#ffffff',
209
+ border: '1px solid #d1d5db',
210
+ borderRadius: 8,
211
+ color: '#111827',
212
+ fontSize: 13,
213
+ minWidth: 240,
214
+ padding: '8px 12px',
215
+ }, type: "search", value: searchValue }), _jsxs("details", { ref: sourceFilterRef, style: { position: 'relative' }, children: [_jsxs("summary", { style: {
216
+ alignItems: 'center',
217
+ appearance: 'none',
218
+ background: '#ffffff',
219
+ border: '1px solid #d1d5db',
220
+ borderRadius: 8,
221
+ color: '#111827',
222
+ cursor: 'pointer',
223
+ display: 'flex',
224
+ fontSize: 13,
225
+ gap: 8,
226
+ listStyle: 'none',
227
+ minWidth: 220,
228
+ padding: '8px 12px',
229
+ userSelect: 'none',
230
+ }, children: [_jsx("span", { children: "Sources:" }), _jsx("span", { style: { color: '#4b5563', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: sourceFilterLabel }), _jsx("span", { style: { color: '#6b7280', marginLeft: 'auto' }, children: "\u25BE" })] }), _jsx("div", { style: {
231
+ background: '#ffffff',
232
+ border: '1px solid #d1d5db',
233
+ borderRadius: 10,
234
+ boxShadow: '0 12px 32px rgba(17, 24, 39, 0.12)',
235
+ marginTop: 8,
236
+ padding: 12,
237
+ position: 'absolute',
238
+ right: 0,
239
+ width: 320,
240
+ zIndex: 20,
241
+ }, children: _jsxs("div", { style: { display: 'grid', gap: 10 }, children: [_jsx("input", { onChange: (event) => setSourceSearchValue(event.target.value), placeholder: "Search collections or items...", style: {
242
+ background: '#ffffff',
243
+ border: '1px solid #d1d5db',
244
+ borderRadius: 8,
245
+ color: '#111827',
246
+ fontSize: 13,
247
+ padding: '8px 12px',
248
+ width: '100%',
249
+ }, type: "search", value: sourceSearchValue }), _jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx("button", { onClick: () => setSelectedSources(sourceOptions.map((option) => option.key)), style: buttonStyle('secondary'), type: "button", children: "Select All" }), _jsx("button", { onClick: () => setSelectedSources([]), style: buttonStyle('secondary'), type: "button", children: "Clear" })] }), _jsx("div", { style: { display: 'grid', gap: 8, maxHeight: 220, overflow: 'auto' }, children: visibleSourceOptions.length ? (visibleSourceOptions.map((option) => {
250
+ const checked = selectedSources.includes(option.key);
251
+ return (_jsxs("label", { style: {
252
+ cursor: 'pointer',
253
+ display: 'grid',
254
+ gap: 10,
255
+ fontSize: 13,
256
+ gridTemplateColumns: 'auto 1fr',
257
+ }, children: [_jsx("input", { checked: checked, onChange: () => setSelectedSources((current) => checked
258
+ ? current.filter((value) => value !== option.key)
259
+ : [...current, option.key]), style: { marginTop: 2 }, type: "checkbox" }), _jsxs("div", { style: { display: 'grid', gap: 2 }, children: [_jsxs("div", { style: { alignItems: 'center', display: 'flex', gap: 8 }, children: [_jsx("span", { children: option.label }), _jsx("span", { style: {
260
+ background: option.kind === 'collection' ? '#eef2ff' : '#f3f4f6',
261
+ borderRadius: 999,
262
+ color: option.kind === 'collection' ? '#3730a3' : '#4b5563',
263
+ fontSize: 11,
264
+ fontWeight: 700,
265
+ padding: '2px 8px',
266
+ textTransform: 'uppercase',
267
+ }, children: option.kind === 'collection' ? 'Collection' : 'Item' })] }), _jsx("span", { style: { color: '#6b7280', fontSize: 12 }, children: option.description })] })] }, option.key));
268
+ })) : (_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
+ const isOpen = Boolean(openNames[componentName]);
270
+ const usedInside = buildRelationshipSummaries(usages, (usage) => usage.parentComponentName);
271
+ const contains = containsByComponent.get(componentName) ?? [];
272
+ return (_jsxs("section", { style: {
273
+ background: '#fff',
274
+ border: '1px solid #e5e7eb',
275
+ borderRadius: 10,
276
+ overflow: 'hidden',
277
+ }, children: [_jsxs("button", { onClick: () => setOpenNames((current) => ({
278
+ ...current,
279
+ [componentName]: !current[componentName],
280
+ })), style: {
281
+ alignItems: 'center',
282
+ appearance: 'none',
283
+ background: 'transparent',
284
+ border: 0,
285
+ cursor: 'pointer',
286
+ display: 'flex',
287
+ gap: 12,
288
+ justifyContent: 'space-between',
289
+ padding: 16,
290
+ textAlign: 'left',
291
+ 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'] }), contains.length ? (_jsxs("span", { style: { color: '#6b7280', fontSize: 13, fontWeight: 500 }, children: ["Contains ", contains.length, " component", contains.length === 1 ? '' : 's'] })) : null, 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
+ borderTop: '1px solid #e5e7eb',
294
+ display: 'grid',
295
+ gap: 0,
296
+ gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))',
297
+ }, children: [_jsxs("div", { style: { borderRight: '1px solid #e5e7eb', padding: 16 }, 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({
298
+ adminRoute,
299
+ path: `/collections/${usage.sourceCollection}/${usage.sourceDocumentId}`,
300
+ }), style: {
301
+ border: '1px solid #e5e7eb',
302
+ borderRadius: 6,
303
+ color: 'inherit',
304
+ display: 'block',
305
+ padding: 12,
306
+ textDecoration: 'none',
307
+ }, 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." })), renderRelationshipList('Used Inside', usedInside, 'This component is not indexed inside another component.')] }), _jsxs("div", { style: { padding: 16 }, children: [renderRelationshipList('Contains', contains, 'No nested child components were indexed for this component.'), _jsx("h3", { style: { margin: '16px 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." }))] })] })) : null] }, componentName));
308
+ })] }));
309
+ }
@@ -0,0 +1,3 @@
1
+ import type { UIFieldServerComponent } from 'payload';
2
+ declare const NitrogenComponentInventoryField: UIFieldServerComponent;
3
+ export default NitrogenComponentInventoryField;
@@ -0,0 +1,26 @@
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 NitrogenComponentInventoryClient from './NitrogenComponentInventoryClient';
4
+ import NitrogenComponentInventoryReindexButton from './NitrogenComponentInventoryReindexButton';
5
+ const NitrogenComponentInventoryField = async ({ payload, req, }) => {
6
+ const [catalogDocs, usageDocs] = await Promise.all([
7
+ findAllDocs(payload, NITROGEN_COMPONENT_CATALOG_COLLECTION),
8
+ findAllDocs(payload, NITROGEN_COMPONENT_USAGE_COLLECTION),
9
+ ]);
10
+ const catalogByName = new Map(catalogDocs.map((doc) => [doc.componentName, doc.definition]));
11
+ const usageByName = new Map();
12
+ for (const usageDoc of usageDocs) {
13
+ const docs = usageByName.get(usageDoc.componentName) || [];
14
+ docs.push(usageDoc);
15
+ usageByName.set(usageDoc.componentName, docs);
16
+ }
17
+ const componentNames = Array.from(new Set([...catalogByName.keys(), ...usageByName.keys()])).sort((a, b) => a.localeCompare(b));
18
+ const components = componentNames.map((componentName) => ({
19
+ componentName,
20
+ definition: catalogByName.get(componentName),
21
+ isUnknown: !catalogByName.get(componentName),
22
+ usages: usageByName.get(componentName) || [],
23
+ }));
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 })] }));
25
+ };
26
+ export default NitrogenComponentInventoryField;
@@ -0,0 +1 @@
1
+ export default function NitrogenComponentInventoryNavLink(): null;