@rakun-kit/manager-react 1.4.2 → 1.4.4
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/cjs/layouts.js +5 -2
- package/dist/cjs/layouts.js.map +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.js +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.js.map +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.js +217 -24
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.js.map +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/edit.js +1 -1
- package/dist/cjs/router/dashboard/[contentType]/[edit]/edit.js.map +1 -1
- package/dist/esm/layouts.d.ts.map +1 -1
- package/dist/esm/layouts.js +5 -2
- package/dist/esm/layouts.js.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.js +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.js.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.d.ts.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.js +219 -29
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.js.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/edit.js +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/edit.js.map +1 -1
- package/dist/styles.css +46 -13
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
package/dist/cjs/layouts.js
CHANGED
|
@@ -8,12 +8,15 @@ exports.ManagerDashboardLayout = exports.ManagerAuthLayout = void 0;
|
|
|
8
8
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
9
|
const app_sidebar_1 = require("./components/app-sidebar.js");
|
|
10
10
|
const bread_crumb_1 = __importDefault(require("./components/bread-crumb.js"));
|
|
11
|
+
const scroll_area_1 = require("./components/ui/scroll-area.js");
|
|
11
12
|
const separator_1 = require("./components/ui/separator.js");
|
|
12
13
|
const manager_help_1 = require("./help/manager-help.js");
|
|
13
14
|
const sidebar_1 = require("./components/ui/sidebar.js");
|
|
14
|
-
const scroll_area_1 = require("./components/ui/scroll-area.js");
|
|
15
15
|
const ManagerAuthLayout = ({ children }) => ((0, jsx_runtime_1.jsx)("div", { className: "bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10", children: (0, jsx_runtime_1.jsx)("div", { className: "w-full max-w-sm", children: children }) }));
|
|
16
16
|
exports.ManagerAuthLayout = ManagerAuthLayout;
|
|
17
|
-
const ManagerDashboardLayout = ({ children, route, contentTypes, pathname, basePath = '', secondaryItems, sidebar, headerStart, headerEnd, }) =>
|
|
17
|
+
const ManagerDashboardLayout = ({ children, route, contentTypes, pathname, basePath = '', secondaryItems, sidebar, headerStart, headerEnd, }) => {
|
|
18
|
+
const hasInternalPageScroll = route?.kind === 'content-create' || route?.kind === 'content-edit';
|
|
19
|
+
return ((0, jsx_runtime_1.jsx)(sidebar_1.SidebarProvider, { className: "h-svh min-h-0 overflow-hidden", children: (0, jsx_runtime_1.jsxs)(manager_help_1.ManagerHelpProvider, { route: route ?? { kind: 'unknown', pathname: pathname ?? '/' }, children: [sidebar ?? ((0, jsx_runtime_1.jsx)(app_sidebar_1.AppSidebar, { contentTypes: contentTypes, pathname: pathname, basePath: basePath, secondaryItems: secondaryItems })), (0, jsx_runtime_1.jsxs)(sidebar_1.SidebarInset, { className: "min-h-0 overflow-hidden p-4 pt-0", children: [(0, jsx_runtime_1.jsxs)(sidebar_1.SidebarHeader, { className: "flex h-16 shrink-0 flex-row items-center justify-between gap-2", "data-tour": "manager-header", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(sidebar_1.SidebarTrigger, { className: "-ml-1" }), (0, jsx_runtime_1.jsx)(separator_1.Separator, { orientation: "vertical", className: "mr-2 data-[orientation=vertical]:h-4" }), headerStart ?? (0, jsx_runtime_1.jsx)(bread_crumb_1.default, { basePath: basePath, pathname: pathname })] }), headerEnd] }), hasInternalPageScroll ? ((0, jsx_runtime_1.jsx)("div", { className: "min-h-0 flex-1 overflow-hidden", "data-tour": "manager-page", children: children })) : ((0, jsx_runtime_1.jsx)(scroll_area_1.ScrollArea, { className: "min-h-0 flex-1", "data-tour": "manager-page", children: children }))] })] }) }));
|
|
20
|
+
};
|
|
18
21
|
exports.ManagerDashboardLayout = ManagerDashboardLayout;
|
|
19
22
|
//# sourceMappingURL=layouts.js.map
|
package/dist/cjs/layouts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layouts.js","sourceRoot":"","sources":["../../src/layouts.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAA;;;;;;;AAMZ,0DAA8E;AAC9E,2EAA0D;AAC1D,yDAAqD;AACrD,sDAAyD;AACzD,qDAKgC;
|
|
1
|
+
{"version":3,"file":"layouts.js","sourceRoot":"","sources":["../../src/layouts.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAA;;;;;;;AAMZ,0DAA8E;AAC9E,2EAA0D;AAC1D,6DAAwD;AACxD,yDAAqD;AACrD,sDAAyD;AACzD,qDAKgC;AAoBzB,MAAM,iBAAiB,GAAG,CAAC,EAAE,QAAQ,EAA0B,EAAE,EAAE,CAAC,CACzE,gCAAK,SAAS,EAAC,qFAAqF,YAClG,gCAAK,SAAS,EAAC,iBAAiB,YAAE,QAAQ,GAAO,GAC7C,CACP,CAAA;AAJY,QAAA,iBAAiB,qBAI7B;AAcM,MAAM,sBAAsB,GAAG,CAAC,EACrC,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,GAAG,EAAE,EACb,cAAc,EACd,OAAO,EACP,WAAW,EACX,SAAS,GACmB,EAAE,EAAE;IAChC,MAAM,qBAAqB,GAAG,KAAK,EAAE,IAAI,KAAK,gBAAgB,IAAI,KAAK,EAAE,IAAI,KAAK,cAAc,CAAA;IAEhG,OAAO,CACL,uBAAC,yBAAe,IAAC,SAAS,EAAC,+BAA+B,YACxD,wBAAC,kCAAmB,IAAC,KAAK,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI,GAAG,EAAE,aAChF,OAAO,IAAI,CACV,uBAAC,wBAAU,IACT,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,cAAc,GAC9B,CACH,EACD,wBAAC,sBAAY,IAAC,SAAS,EAAC,kCAAkC,aACxD,wBAAC,uBAAa,IACZ,SAAS,EAAC,gEAAgE,eAChE,gBAAgB,aAE1B,iCAAK,SAAS,EAAC,yBAAyB,aACtC,uBAAC,wBAAc,IAAC,SAAS,EAAC,OAAO,GAAG,EACpC,uBAAC,qBAAS,IACR,WAAW,EAAC,UAAU,EACtB,SAAS,EAAC,sCAAsC,GAChD,EACD,WAAW,IAAI,uBAAC,qBAAmB,IAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAI,IAC3E,EACL,SAAS,IACI,EAEf,qBAAqB,CAAC,CAAC,CAAC,CACvB,gCAAK,SAAS,EAAC,gCAAgC,eAAW,cAAc,YACrE,QAAQ,GACL,CACP,CAAC,CAAC,CAAC,CACF,uBAAC,wBAAU,IAAC,SAAS,EAAC,gBAAgB,eAAW,cAAc,YAC5D,QAAQ,GACE,CACd,IACY,IACK,GACN,CACnB,CAAA;AACH,CAAC,CAAA;AArDY,QAAA,sBAAsB,0BAqDlC"}
|
|
@@ -15,7 +15,7 @@ const tabs_1 = require("../../../../../components/ui/tabs.js");
|
|
|
15
15
|
const scroll_area_1 = require("../../../../../components/ui/scroll-area.js");
|
|
16
16
|
const EditTabPanels = () => {
|
|
17
17
|
const { activeTab, canRestoreVersions, contentType, contentTypeId, contentTypeName, form, hasVersioning, hasLocaleVariants, onAfterRestore, routeLayout, sections, } = (0, EditPageContext_1.useEditPageContext)();
|
|
18
|
-
return ((0, jsx_runtime_1.jsx)(scroll_area_1.ScrollArea, { className: "h-full pr-4", "data-rakun-manager-edit-scroll-area": true, children: (0, jsx_runtime_1.jsxs)("div", { className: "min-
|
|
18
|
+
return ((0, jsx_runtime_1.jsx)(scroll_area_1.ScrollArea, { className: "h-full min-h-0 pr-4", "data-rakun-manager-edit-scroll-area": true, children: (0, jsx_runtime_1.jsxs)("div", { className: "min-h-full min-w-0", children: [sections.hasIterables ? ((0, jsx_runtime_1.jsx)(tabs_1.TabsContent, { value: "content", forceMount: true, hidden: activeTab !== 'content', className: "w-full h-full", "data-rakun-manager-tab-panel": "content", "data-tour": "content-edit-fields", children: (0, jsx_runtime_1.jsx)(ContentTypeEdit_1.default, { defaultData: form.draft.current, ref: form.iterablesRef, contentType: sections.iterables, parentContentType: contentType, id: contentTypeName, collapsible: true, hideTitle: true }, `iterables:${form.formRevision}`) })) : null, sections.hasNonIterables ? ((0, jsx_runtime_1.jsx)(tabs_1.TabsContent, { value: "info", forceMount: true, hidden: activeTab !== 'info', className: "w-full h-full", "data-tour": "content-edit-fields", children: (0, jsx_runtime_1.jsx)(ContentTypeEdit_1.default, { defaultData: form.draft.current, ref: form.nonIterablesRef, contentType: sections.nonIterables, id: contentTypeName }, `info:${form.formRevision}`) })) : null, sections.hasSeo ? ((0, jsx_runtime_1.jsx)(tabs_1.TabsContent, { value: "seo", forceMount: true, hidden: activeTab !== 'seo', className: "w-full h-full", "data-tour": "content-edit-fields", children: (0, jsx_runtime_1.jsx)(ContentTypeEdit_1.default, { defaultData: form.draft.current, ref: form.seoRef, contentType: sections.seo, id: contentTypeName, hideTitle: true }, `seo:${form.formRevision}`) })) : null, routeLayout.routeLayoutModules.map((layoutModule) => ((0, jsx_runtime_1.jsx)(RouteLayoutModuleTabContent_1.RouteLayoutModuleTabContent, { layoutModule: layoutModule }, layoutModule._id))), hasLocaleVariants ? ((0, jsx_runtime_1.jsx)(tabs_1.TabsContent, { value: "locale-variants", children: (0, jsx_runtime_1.jsx)(LocaleVariants_1.LocaleVariants, {}) })) : null, hasVersioning && contentTypeId ? ((0, jsx_runtime_1.jsx)(tabs_1.TabsContent, { value: "versions", children: (0, jsx_runtime_1.jsx)(Versions_1.default, { contentType: contentTypeName, documentId: contentTypeId, canRestore: canRestoreVersions, onRestored: onAfterRestore }) })) : null] }) }));
|
|
19
19
|
};
|
|
20
20
|
exports.EditTabPanels = EditTabPanels;
|
|
21
21
|
//# sourceMappingURL=EditTabPanels.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditTabPanels.js","sourceRoot":"","sources":["../../../../../../../src/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAA;;;;;;;AAEZ,yEAAgD;AAChD,iEAAgE;AAChE,0DAAuC;AACvC,+EAA2E;AAC3E,qDAAiD;AAEjD,+CAAkD;AAClD,6DAAwD;AAEjD,MAAM,aAAa,GAAG,GAAG,EAAE;IAChC,MAAM,EACJ,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,eAAe,EACf,IAAI,EACJ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,QAAQ,GACT,GAAG,IAAA,oCAAkB,GAAE,CAAA;IAExB,OAAO,CACL,uBAAC,wBAAU,IAAC,SAAS,EAAC,
|
|
1
|
+
{"version":3,"file":"EditTabPanels.js","sourceRoot":"","sources":["../../../../../../../src/router/dashboard/[contentType]/[edit]/_components/EditTabPanels.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAA;;;;;;;AAEZ,yEAAgD;AAChD,iEAAgE;AAChE,0DAAuC;AACvC,+EAA2E;AAC3E,qDAAiD;AAEjD,+CAAkD;AAClD,6DAAwD;AAEjD,MAAM,aAAa,GAAG,GAAG,EAAE;IAChC,MAAM,EACJ,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,eAAe,EACf,IAAI,EACJ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,QAAQ,GACT,GAAG,IAAA,oCAAkB,GAAE,CAAA;IAExB,OAAO,CACL,uBAAC,wBAAU,IAAC,SAAS,EAAC,qBAAqB,yDACzC,iCAAK,SAAS,EAAC,oBAAoB,aAChC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CACvB,uBAAC,kBAAW,IACV,KAAK,EAAC,SAAS,EACf,UAAU,QACV,MAAM,EAAE,SAAS,KAAK,SAAS,EAC/B,SAAS,EAAC,eAAe,kCACI,SAAS,eAC5B,qBAAqB,YAE/B,uBAAC,yBAAe,IAEd,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAC/B,GAAG,EAAE,IAAI,CAAC,YAAY,EACtB,WAAW,EAAE,QAAQ,CAAC,SAAS,EAC/B,iBAAiB,EAAE,WAAW,EAC9B,EAAE,EAAE,eAAe,EACnB,WAAW,QACX,SAAS,UAPJ,aAAa,IAAI,CAAC,YAAY,EAAE,CAQrC,GACU,CACf,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAC1B,uBAAC,kBAAW,IACV,KAAK,EAAC,MAAM,EACZ,UAAU,QACV,MAAM,EAAE,SAAS,KAAK,MAAM,EAC5B,SAAS,EAAC,eAAe,eACf,qBAAqB,YAE/B,uBAAC,yBAAe,IAEd,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAC/B,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,WAAW,EAAE,QAAQ,CAAC,YAAY,EAClC,EAAE,EAAE,eAAe,IAJd,QAAQ,IAAI,CAAC,YAAY,EAAE,CAKhC,GACU,CACf,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CACjB,uBAAC,kBAAW,IACV,KAAK,EAAC,KAAK,EACX,UAAU,QACV,MAAM,EAAE,SAAS,KAAK,KAAK,EAC3B,SAAS,EAAC,eAAe,eACf,qBAAqB,YAE/B,uBAAC,yBAAe,IAEd,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAC/B,GAAG,EAAE,IAAI,CAAC,MAAM,EAChB,WAAW,EAAE,QAAQ,CAAC,GAAG,EACzB,EAAE,EAAE,eAAe,EACnB,SAAS,UALJ,OAAO,IAAI,CAAC,YAAY,EAAE,CAM/B,GACU,CACf,CAAC,CAAC,CAAC,IAAI,EACP,WAAW,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CACpD,uBAAC,yDAA2B,IAAwB,YAAY,EAAE,YAAY,IAA5C,YAAY,CAAC,GAAG,CAAgC,CACnF,CAAC,EACD,iBAAiB,CAAC,CAAC,CAAC,CACnB,uBAAC,kBAAW,IAAC,KAAK,EAAC,iBAAiB,YAClC,uBAAC,+BAAc,KAAG,GACN,CACf,CAAC,CAAC,CAAC,IAAI,EACP,aAAa,IAAI,aAAa,CAAC,CAAC,CAAC,CAChC,uBAAC,kBAAW,IAAC,KAAK,EAAC,UAAU,YAC3B,uBAAC,kBAAc,IACb,WAAW,EAAE,eAAe,EAC5B,UAAU,EAAE,aAAa,EACzB,UAAU,EAAE,kBAAkB,EAC9B,UAAU,EAAE,cAAc,GAC1B,GACU,CACf,CAAC,CAAC,CAAC,IAAI,IACJ,GACK,CACd,CAAA;AACH,CAAC,CAAA;AA/FY,QAAA,aAAa,iBA+FzB"}
|
|
@@ -8,6 +8,7 @@ const lucide_react_1 = require("lucide-react");
|
|
|
8
8
|
const react_1 = require("react");
|
|
9
9
|
const badge_1 = require("../../../../../components/ui/badge.js");
|
|
10
10
|
const button_1 = require("../../../../../components/ui/button.js");
|
|
11
|
+
const collapsible_1 = require("../../../../../components/ui/collapsible.js");
|
|
11
12
|
const dialog_1 = require("../../../../../components/ui/dialog.js");
|
|
12
13
|
const select_1 = require("../../../../../components/ui/select.js");
|
|
13
14
|
const tooltip_1 = require("../../../../../components/ui/tooltip.js");
|
|
@@ -30,6 +31,7 @@ const cleanDynamicBindings = (bindings) => {
|
|
|
30
31
|
};
|
|
31
32
|
const isRecord = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
32
33
|
const isListField = (field) => field.config.ui === 'List' || field.config.ui === 'Iterator';
|
|
34
|
+
const isRelatedCollectionSource = (source) => !!source && 'kind' in source && source.kind === 'relatedCollection';
|
|
33
35
|
const isDynamicVisibleField = (field) => (field.visibility ?? 'all') === 'all' && field.isDynamic !== false;
|
|
34
36
|
const isDynamicFieldEnabled = (contentType, field) => {
|
|
35
37
|
return (contentType.dynamicData !== false &&
|
|
@@ -60,6 +62,8 @@ const getFieldKind = (field) => {
|
|
|
60
62
|
}
|
|
61
63
|
if (field.config.type === 'Relation')
|
|
62
64
|
return 'object';
|
|
65
|
+
if (field.config.ui === 'SimpleList')
|
|
66
|
+
return 'array';
|
|
63
67
|
if (isListField(field))
|
|
64
68
|
return 'array';
|
|
65
69
|
return 'unknown';
|
|
@@ -69,6 +73,29 @@ const isCompatibleSourceKind = (sourceKind, targetField) => {
|
|
|
69
73
|
return sourceKind !== 'object' && sourceKind !== 'array';
|
|
70
74
|
return sourceKind === getFieldKind(targetField);
|
|
71
75
|
};
|
|
76
|
+
const getMultipleFileField = (field) => field.config.type === 'File' &&
|
|
77
|
+
field.isMultiple
|
|
78
|
+
? field
|
|
79
|
+
: undefined;
|
|
80
|
+
const areFieldKindsCompatible = (sourceField, targetField) => {
|
|
81
|
+
const sourceKind = getFieldKind(sourceField);
|
|
82
|
+
if (!targetField)
|
|
83
|
+
return isCompatibleSourceKind(sourceKind);
|
|
84
|
+
const targetKind = getFieldKind(targetField);
|
|
85
|
+
if (sourceKind !== targetKind)
|
|
86
|
+
return false;
|
|
87
|
+
if (sourceKind !== 'array')
|
|
88
|
+
return true;
|
|
89
|
+
const sourceFile = getMultipleFileField(sourceField);
|
|
90
|
+
const targetFile = getMultipleFileField(targetField);
|
|
91
|
+
if (!sourceFile && !targetFile)
|
|
92
|
+
return true;
|
|
93
|
+
if (!sourceFile || !targetFile)
|
|
94
|
+
return false;
|
|
95
|
+
return (sourceFile.mediaType === 'Any' ||
|
|
96
|
+
targetFile.mediaType === 'Any' ||
|
|
97
|
+
sourceFile.mediaType === targetFile.mediaType);
|
|
98
|
+
};
|
|
72
99
|
const fieldLabel = (path) => path.startsWith('_seo.') ? `seo.${path.slice('_seo.'.length)}` : path;
|
|
73
100
|
const isSeoPath = (path) => path === '_seo' ||
|
|
74
101
|
path === 'seo' ||
|
|
@@ -121,10 +148,22 @@ const nestedSourceFieldOptions = ({ contentType, prefix = '', targetField, depth
|
|
|
121
148
|
depth: depth + 1,
|
|
122
149
|
});
|
|
123
150
|
}
|
|
151
|
+
if (field.config.type === 'File' &&
|
|
152
|
+
field.isMultiple) {
|
|
153
|
+
if (!areFieldKindsCompatible(field, targetField))
|
|
154
|
+
return [];
|
|
155
|
+
return [
|
|
156
|
+
{
|
|
157
|
+
label: fieldLabel(path),
|
|
158
|
+
value: path,
|
|
159
|
+
kind,
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
}
|
|
124
163
|
if (field.config.type === 'File') {
|
|
125
164
|
return fileFieldOptions(path, targetField);
|
|
126
165
|
}
|
|
127
|
-
if (!
|
|
166
|
+
if (!areFieldKindsCompatible(field, targetField))
|
|
128
167
|
return [];
|
|
129
168
|
return [
|
|
130
169
|
{
|
|
@@ -149,6 +188,26 @@ const createSource = (contentType, value, id) => value === '$href'
|
|
|
149
188
|
: { contentType, id, path: value };
|
|
150
189
|
const sourceValue = (source) => source?.virtual === 'href' ? '$href' : source?.path || '';
|
|
151
190
|
const sourceLabel = (source) => source?.virtual === 'href' ? 'href' : fieldLabel(source?.path || '');
|
|
191
|
+
const mappingSourceSummary = (source, currentSourceName) => {
|
|
192
|
+
if (!source) {
|
|
193
|
+
return {
|
|
194
|
+
mode: 'Not configured',
|
|
195
|
+
detail: 'Choose a mapping source',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (!isRelatedCollectionSource(source)) {
|
|
199
|
+
return {
|
|
200
|
+
mode: 'Direct field',
|
|
201
|
+
detail: `${currentSourceName}.${sourceLabel(source)}`,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const sort = Object.entries(source.sort ?? {})[0];
|
|
205
|
+
const sortSummary = sort ? ` · sort ${sort[0]} ${sort[1]}` : '';
|
|
206
|
+
return {
|
|
207
|
+
mode: 'Related collection',
|
|
208
|
+
detail: `${source.contentType}.${source.relation} → ${currentSourceName} · collect ${source.path} · limit ${source.limit}${sortSummary}`,
|
|
209
|
+
};
|
|
210
|
+
};
|
|
152
211
|
const sourceContentTypeLabel = (source, documentContentTypeName) => source.contentType === documentContentTypeName && !source.id
|
|
153
212
|
? 'Current document'
|
|
154
213
|
: source.contentType;
|
|
@@ -216,6 +275,7 @@ const bindingSummary = ({ list, fieldBinding, listBinding, documentContentTypeNa
|
|
|
216
275
|
return `${sourceContentTypeLabel(fieldBinding, documentContentTypeName)} -> ${sourceLabel(fieldBinding)}`;
|
|
217
276
|
};
|
|
218
277
|
const PanelSection = ({ title, children, }) => ((0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 rounded-md border border-border bg-muted/20 p-3', children: [(0, jsx_runtime_1.jsx)("div", { className: 'text-xs font-semibold uppercase text-muted-foreground', children: title }), children] }));
|
|
278
|
+
const ControlLabel = ({ children, help, }) => ((0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-1.5', children: [(0, jsx_runtime_1.jsx)(label_1.Label, { children: children }), (0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, { children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipTrigger, { asChild: true, children: (0, jsx_runtime_1.jsx)("button", { type: 'button', className: 'rounded-sm text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring', "aria-label": help, children: (0, jsx_runtime_1.jsx)(lucide_react_1.HelpCircle, { className: 'h-3.5 w-3.5' }) }) }), (0, jsx_runtime_1.jsx)(tooltip_1.TooltipContent, { side: 'top', sideOffset: 6, className: 'max-w-xs', children: help })] })] }));
|
|
219
279
|
const FieldBindingEditor = ({ contentTypes, documentContentType, currentDocumentSourceEnabled, targetField, binding, onChange, }) => {
|
|
220
280
|
const { language } = (0, LanguageClientProvider_1.useLanguage)();
|
|
221
281
|
const [sourceType, setSourceType] = (0, react_1.useState)(binding?.contentType || '');
|
|
@@ -245,6 +305,9 @@ const FieldBindingEditor = ({ contentTypes, documentContentType, currentDocument
|
|
|
245
305
|
selectedContentType?.dynamicDataSource === true,
|
|
246
306
|
});
|
|
247
307
|
const items = (sourceItemsQuery.data?.items ?? []);
|
|
308
|
+
const hasCurrentDocumentItem = currentDocumentSourceEnabled && sourceType === documentContentType.name;
|
|
309
|
+
const hasItemOptions = hasCurrentDocumentItem || items.length > 0;
|
|
310
|
+
const fieldOptions = sourceFieldOptions(selectedContentType, targetField);
|
|
248
311
|
const emit = (nextType = sourceType, nextId = sourceId, nextPath = fieldPath) => {
|
|
249
312
|
const usesCurrentDocument = currentDocumentSourceEnabled &&
|
|
250
313
|
nextType === documentContentType.name &&
|
|
@@ -255,7 +318,7 @@ const FieldBindingEditor = ({ contentTypes, documentContentType, currentDocument
|
|
|
255
318
|
}
|
|
256
319
|
onChange(createSource(nextType, nextPath, usesCurrentDocument ? undefined : nextId));
|
|
257
320
|
};
|
|
258
|
-
return ((0, jsx_runtime_1.jsx)(PanelSection, { title: 'Source value', children: (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 md:grid-cols-[1fr_1.4fr_1fr]', children: [(0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Source", (0, jsx_runtime_1.jsxs)(select_1.Select, { value: sourceType, onValueChange: (value) => {
|
|
321
|
+
return ((0, jsx_runtime_1.jsx)(PanelSection, { title: 'Source value', children: (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 md:grid-cols-[1fr_1.4fr_1fr]', children: [(0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Source", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: contentTypes.length === 0, value: sourceType, onValueChange: (value) => {
|
|
259
322
|
const nextId = currentDocumentSourceEnabled &&
|
|
260
323
|
value === documentContentType.name
|
|
261
324
|
? CURRENT_DOCUMENT_ID
|
|
@@ -264,24 +327,23 @@ const FieldBindingEditor = ({ contentTypes, documentContentType, currentDocument
|
|
|
264
327
|
setSourceId(nextId);
|
|
265
328
|
setFieldPath('');
|
|
266
329
|
onChange(undefined);
|
|
267
|
-
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'Content type' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: contentTypes.map((ct) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: ct.name, children: currentDocumentSourceEnabled &&
|
|
330
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: contentTypes.length > 0 ? 'Content type' : 'No content types' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: contentTypes.map((ct) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: ct.name, children: currentDocumentSourceEnabled &&
|
|
268
331
|
ct.name === documentContentType.name
|
|
269
332
|
? `${ct.name} (current document)`
|
|
270
|
-
: ct.name }, ct.name))) }) })] })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Item", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sourceType, value: sourceId, onValueChange: (value) => {
|
|
333
|
+
: ct.name }, ct.name))) }) })] })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Item", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sourceType || !hasItemOptions, value: sourceId, onValueChange: (value) => {
|
|
271
334
|
setSourceId(value);
|
|
272
335
|
emit(sourceType, value, fieldPath);
|
|
273
|
-
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'Item' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsxs)(select_1.SelectGroup, { children: [
|
|
274
|
-
sourceType === documentContentType.name ? ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: CURRENT_DOCUMENT_ID, children: "Current document" })) : null, items.map((item) => {
|
|
336
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: hasItemOptions ? 'Item' : 'No items available' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsxs)(select_1.SelectGroup, { children: [hasCurrentDocumentItem ? ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: CURRENT_DOCUMENT_ID, children: "Current document" })) : null, items.map((item) => {
|
|
275
337
|
const label = selectedContentType
|
|
276
338
|
? (0, client_1.getListField)(item, selectedContentType.listFields ?? [])
|
|
277
339
|
: item._id;
|
|
278
340
|
return ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item._id, children: (0, client_1.isTranslatableObject)(label)
|
|
279
341
|
? label[language.code]
|
|
280
342
|
: String(label || item._id) }, item._id));
|
|
281
|
-
})] }) })] })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Field", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sourceType || !sourceId, value: fieldPath, onValueChange: (value) => {
|
|
343
|
+
})] }) })] })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Field", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sourceType || !sourceId || fieldOptions.length === 0, value: fieldPath, onValueChange: (value) => {
|
|
282
344
|
setFieldPath(value);
|
|
283
345
|
emit(sourceType, sourceId, value);
|
|
284
|
-
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'Field' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children:
|
|
346
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: fieldOptions.length > 0 ? 'Field' : 'No compatible fields' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: fieldOptions.map((field) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: field.value, children: field.label }, field.value))) }) })] })] })] }) }));
|
|
285
347
|
};
|
|
286
348
|
const getListTargetContentType = (field, itemName) => {
|
|
287
349
|
const entry = field.fields.find((item) => item.name === itemName);
|
|
@@ -289,15 +351,140 @@ const getListTargetContentType = (field, itemName) => {
|
|
|
289
351
|
return undefined;
|
|
290
352
|
return entry.field.contentType;
|
|
291
353
|
};
|
|
354
|
+
const getRelationContentType = (field) => {
|
|
355
|
+
if (field.isTranslatable)
|
|
356
|
+
return undefined;
|
|
357
|
+
if (field.config.type === 'Relation') {
|
|
358
|
+
return field.contentType;
|
|
359
|
+
}
|
|
360
|
+
if (field.config.ui === 'SimpleList') {
|
|
361
|
+
const itemField = field.field;
|
|
362
|
+
if (itemField.config.type === 'Relation') {
|
|
363
|
+
return itemField.contentType;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return undefined;
|
|
367
|
+
};
|
|
368
|
+
const getRelatedRelationOptions = (contentType, currentSource) => Object.entries(contentType.fields).flatMap(([name, field]) => {
|
|
369
|
+
if (!isDynamicVisibleField(field))
|
|
370
|
+
return [];
|
|
371
|
+
const relationContentType = getRelationContentType(field);
|
|
372
|
+
return relationContentType?.name === currentSource.name ? [name] : [];
|
|
373
|
+
});
|
|
374
|
+
const getRelatedPathOptions = (contentType, targetField) => sourceFieldOptions(contentType, targetField).filter((option) => option.kind === 'array' && option.value !== '$href');
|
|
375
|
+
const getSortFieldOptions = (contentType) => Object.entries(contentType.fields).flatMap(([name, field]) => {
|
|
376
|
+
if (!isDynamicVisibleField(field) ||
|
|
377
|
+
field.isTranslatable ||
|
|
378
|
+
name.startsWith('_') ||
|
|
379
|
+
isSeoPath(name)) {
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
const kind = getFieldKind(field);
|
|
383
|
+
const isSingleSelect = field.config.type === 'Select' &&
|
|
384
|
+
!field.isMultiple;
|
|
385
|
+
const isSortable = (field.config.type === 'String' && field.config.ui !== 'RichText') ||
|
|
386
|
+
field.config.type === 'Number' ||
|
|
387
|
+
field.config.type === 'Boolean' ||
|
|
388
|
+
field.config.type === 'Date' ||
|
|
389
|
+
isSingleSelect;
|
|
390
|
+
return isSortable ? [{ label: fieldLabel(name), value: name, kind }] : [];
|
|
391
|
+
});
|
|
392
|
+
const createRelatedCollectionSource = ({ contentType, currentSource, targetField, }) => {
|
|
393
|
+
const relation = getRelatedRelationOptions(contentType, currentSource)[0];
|
|
394
|
+
const path = getRelatedPathOptions(contentType, targetField)[0]?.value;
|
|
395
|
+
if (!relation || !path)
|
|
396
|
+
return undefined;
|
|
397
|
+
return {
|
|
398
|
+
kind: 'relatedCollection',
|
|
399
|
+
contentType: contentType.name,
|
|
400
|
+
relation,
|
|
401
|
+
path,
|
|
402
|
+
limit: 10,
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
const MappingSourceEditor = ({ contentTypes, currentSource, targetField, source, onChange, }) => {
|
|
406
|
+
const relatedSource = isRelatedCollectionSource(source) ? source : undefined;
|
|
407
|
+
const directSource = source && !isRelatedCollectionSource(source) ? source : undefined;
|
|
408
|
+
const directFieldOptions = sourceFieldOptions(currentSource, targetField);
|
|
409
|
+
const relatedContentTypes = contentTypes.filter((contentType) => getRelatedRelationOptions(contentType, currentSource).length > 0 &&
|
|
410
|
+
getRelatedPathOptions(contentType, targetField).length > 0);
|
|
411
|
+
const selectedRelatedContentType = relatedSource
|
|
412
|
+
? relatedContentTypes.find((contentType) => contentType.name === relatedSource.contentType)
|
|
413
|
+
: undefined;
|
|
414
|
+
const relationOptions = selectedRelatedContentType
|
|
415
|
+
? getRelatedRelationOptions(selectedRelatedContentType, currentSource)
|
|
416
|
+
: [];
|
|
417
|
+
const pathOptions = selectedRelatedContentType
|
|
418
|
+
? getRelatedPathOptions(selectedRelatedContentType, targetField)
|
|
419
|
+
: [];
|
|
420
|
+
const sortOptions = selectedRelatedContentType
|
|
421
|
+
? getSortFieldOptions(selectedRelatedContentType)
|
|
422
|
+
: [];
|
|
423
|
+
const sortEntry = Object.entries(relatedSource?.sort ?? {})[0];
|
|
424
|
+
const sortField = sortEntry?.[0] ?? '';
|
|
425
|
+
const sortDirection = sortEntry?.[1] ?? 'desc';
|
|
426
|
+
if (!relatedSource) {
|
|
427
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 md:grid-cols-[0.7fr_1.3fr]', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsx)(ControlLabel, { help: 'Choose a direct field or query a related collection.', children: "Mapping mode" }), (0, jsx_runtime_1.jsxs)(select_1.Select, { value: 'field', onValueChange: (value) => {
|
|
428
|
+
if (value !== 'relatedCollection')
|
|
429
|
+
return;
|
|
430
|
+
const next = relatedContentTypes
|
|
431
|
+
.map((contentType) => createRelatedCollectionSource({
|
|
432
|
+
contentType,
|
|
433
|
+
currentSource,
|
|
434
|
+
targetField,
|
|
435
|
+
}))
|
|
436
|
+
.find(Boolean);
|
|
437
|
+
onChange(next);
|
|
438
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, {}) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'field', children: "Direct field" }), relatedContentTypes.length > 0 ? ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'relatedCollection', children: "Related collection" })) : null] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsxs)(ControlLabel, { help: 'Its value will be assigned to this mapped property.', children: ["Field on ", currentSource.name] }), (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: directFieldOptions.length === 0, value: sourceValue(directSource), onValueChange: (value) => onChange(createSource(currentSource.name, value)), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: directFieldOptions.length > 0
|
|
439
|
+
? 'Select a source field'
|
|
440
|
+
: 'No compatible fields' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: directFieldOptions.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.value, children: item.label }, item.value))) }) })] })] })] }));
|
|
441
|
+
}
|
|
442
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 sm:grid-cols-2 lg:grid-cols-4', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsx)(ControlLabel, { help: 'Query records related to the current source item.', children: "Mapping mode" }), (0, jsx_runtime_1.jsxs)(select_1.Select, { value: 'relatedCollection', onValueChange: () => onChange(undefined), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, {}) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'field', children: "Direct field" }), (0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'relatedCollection', children: "Related collection" })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsx)(ControlLabel, { help: 'Content type whose records will be searched.', children: "Collection to query" }), (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: relatedContentTypes.length === 0, value: relatedSource.contentType, onValueChange: (value) => {
|
|
443
|
+
const contentType = relatedContentTypes.find((item) => item.name === value);
|
|
444
|
+
if (!contentType)
|
|
445
|
+
return;
|
|
446
|
+
onChange(createRelatedCollectionSource({
|
|
447
|
+
contentType,
|
|
448
|
+
currentSource,
|
|
449
|
+
targetField,
|
|
450
|
+
}));
|
|
451
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: relatedContentTypes.length > 0
|
|
452
|
+
? 'Select a content type'
|
|
453
|
+
: 'No compatible collections' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: relatedContentTypes.map((contentType) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: contentType.name, children: contentType.name }, contentType.name))) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsxs)(ControlLabel, { help: `Field on ${relatedSource.contentType} that points to ${currentSource.name}.`, children: ["Relation to ", currentSource.name] }), (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: relationOptions.length === 0, value: relatedSource.relation, onValueChange: (relation) => onChange({ ...relatedSource, relation }), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: relationOptions.length > 0
|
|
454
|
+
? 'Select a relation field'
|
|
455
|
+
: 'No compatible relations' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: relationOptions.map((relation) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: relation, children: relation }, relation))) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsx)(ControlLabel, { help: 'Arrays from matching records are flattened into one result.', children: "Array field to collect" }), (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: pathOptions.length === 0, value: relatedSource.path, onValueChange: (path) => onChange({ ...relatedSource, path }), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: pathOptions.length > 0
|
|
456
|
+
? 'Select an array field'
|
|
457
|
+
: 'No compatible array fields' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: pathOptions.map((option) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: option.value, children: option.label }, option.value))) })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 sm:grid-cols-[0.7fr_1fr_0.7fr]', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsx)(ControlLabel, { help: `Number of ${relatedSource.contentType} records queried per ${currentSource.name}. Minimum 1, maximum 100.`, children: "Maximum related records" }), (0, jsx_runtime_1.jsx)(input_1.Input, { type: 'number', min: 1, max: 100, value: String(relatedSource.limit), onChange: (event) => onChange({
|
|
458
|
+
...relatedSource,
|
|
459
|
+
limit: Math.min(100, Math.max(1, Number(event.target.value || 10))),
|
|
460
|
+
}) })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsx)(ControlLabel, { help: 'Optional. Only direct scalar fields can be used.', children: "Sort related records by" }), (0, jsx_runtime_1.jsxs)(select_1.Select, { value: sortField || '__none__', onValueChange: (value) => onChange({
|
|
461
|
+
...relatedSource,
|
|
462
|
+
sort: value === '__none__'
|
|
463
|
+
? undefined
|
|
464
|
+
: { [value]: sortDirection },
|
|
465
|
+
}), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'No sort' }) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: '__none__', children: "No sort" }), sortOptions.map((option) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: option.value, children: option.label }, option.value)))] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid content-start gap-1.5', children: [(0, jsx_runtime_1.jsx)(ControlLabel, { help: 'Applied before collecting and flattening the arrays.', children: "Sort direction" }), (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sortField, value: sortDirection, onValueChange: (direction) => sortField
|
|
466
|
+
? onChange({
|
|
467
|
+
...relatedSource,
|
|
468
|
+
sort: { [sortField]: direction },
|
|
469
|
+
})
|
|
470
|
+
: undefined, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, {}) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'asc', children: "Ascending" }), (0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'desc', children: "Descending" })] })] })] })] })] }));
|
|
471
|
+
};
|
|
292
472
|
const ListBindingEditor = ({ contentTypes, field, binding, onChange, }) => {
|
|
293
473
|
const [sourceType, setSourceType] = (0, react_1.useState)(binding?.contentType || '');
|
|
294
474
|
const [itemName, setItemName] = (0, react_1.useState)(binding?.itemName || field.fields[0]?.name || '');
|
|
295
475
|
const [filterState, setFilterState] = (0, react_1.useState)(readFilterState(binding?.query?.filter));
|
|
476
|
+
const [openMappingField, setOpenMappingField] = (0, react_1.useState)(null);
|
|
296
477
|
const selectedSource = contentTypes.find((ct) => ct.name === sourceType);
|
|
297
478
|
const targetContentType = getListTargetContentType(field, itemName);
|
|
298
479
|
const targetFields = (0, react_1.useMemo)(() => targetContentType
|
|
299
480
|
? Object.entries(targetContentType.fields).filter(([, targetField]) => isDynamicVisibleField(targetField))
|
|
300
481
|
: [], [targetContentType]);
|
|
482
|
+
const sortFieldOptions = selectedSource
|
|
483
|
+
? getSortFieldOptions(selectedSource)
|
|
484
|
+
: [];
|
|
485
|
+
const filterFieldOptions = selectedSource
|
|
486
|
+
? sourceFieldOptions(selectedSource).filter((item) => item.value !== '$href')
|
|
487
|
+
: [];
|
|
301
488
|
const emit = (patch) => {
|
|
302
489
|
const next = {
|
|
303
490
|
contentType: sourceType,
|
|
@@ -321,13 +508,13 @@ const ListBindingEditor = ({ contentTypes, field, binding, onChange, }) => {
|
|
|
321
508
|
},
|
|
322
509
|
});
|
|
323
510
|
};
|
|
324
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3', children: [(0, jsx_runtime_1.jsx)(PanelSection, { title: 'Collection', children: (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 md:grid-cols-2', children: [(0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Source", (0, jsx_runtime_1.jsxs)(select_1.Select, { value: sourceType, onValueChange: (value) => {
|
|
511
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3', children: [(0, jsx_runtime_1.jsx)(PanelSection, { title: 'Collection', children: (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 md:grid-cols-2', children: [(0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Source", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: contentTypes.length === 0, value: sourceType, onValueChange: (value) => {
|
|
325
512
|
setSourceType(value);
|
|
326
513
|
emit({ contentType: value, map: {} });
|
|
327
|
-
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'Content type' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: contentTypes.map((ct) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: ct.name, children: ct.name }, ct.name))) }) })] })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Item", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sourceType, value: itemName, onValueChange: (value) => {
|
|
514
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: contentTypes.length > 0 ? 'Content type' : 'No content types' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: contentTypes.map((ct) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: ct.name, children: ct.name }, ct.name))) }) })] })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Item", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sourceType || field.fields.length === 0, value: itemName, onValueChange: (value) => {
|
|
328
515
|
setItemName(value);
|
|
329
516
|
emit({ itemName: value, map: {} });
|
|
330
|
-
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'Item type' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: field.fields.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.name, children: item.name }, item.name))) }) })] })] })] }) }), (0, jsx_runtime_1.jsx)(PanelSection, { title: 'Query', children: (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 md:grid-cols-[0.7fr_1fr_1.1fr]', children: [(0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Limit", (0, jsx_runtime_1.jsx)(input_1.Input, { type: 'number', min: 1, value: String(binding?.query?.options?.limit ?? 10), onChange: (event) => emit({
|
|
517
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: field.fields.length > 0 ? 'Item type' : 'No item types' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: field.fields.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.name, children: item.name }, item.name))) }) })] })] })] }) }), (0, jsx_runtime_1.jsx)(PanelSection, { title: 'Query', children: (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 md:grid-cols-[0.7fr_1fr_1.1fr]', children: [(0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Limit", (0, jsx_runtime_1.jsx)(input_1.Input, { type: 'number', min: 1, value: String(binding?.query?.options?.limit ?? 10), onChange: (event) => emit({
|
|
331
518
|
query: {
|
|
332
519
|
...binding?.query,
|
|
333
520
|
options: {
|
|
@@ -335,7 +522,7 @@ const ListBindingEditor = ({ contentTypes, field, binding, onChange, }) => {
|
|
|
335
522
|
limit: Number(event.target.value || 10),
|
|
336
523
|
},
|
|
337
524
|
},
|
|
338
|
-
}) })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Sort", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled:
|
|
525
|
+
}) })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Sort", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: sortFieldOptions.length === 0, value: Object.keys(binding?.query?.options?.sort ?? {})[0] ?? '', onValueChange: (value) => emit({
|
|
339
526
|
query: {
|
|
340
527
|
...binding?.query,
|
|
341
528
|
options: {
|
|
@@ -343,17 +530,17 @@ const ListBindingEditor = ({ contentTypes, field, binding, onChange, }) => {
|
|
|
343
530
|
sort: value ? { [value]: 'desc' } : undefined,
|
|
344
531
|
},
|
|
345
532
|
},
|
|
346
|
-
}), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder:
|
|
347
|
-
|
|
348
|
-
.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.value, children: item.label }, item.value))) }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-1.5', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center justify-between gap-2', children: [(0, jsx_runtime_1.jsx)("span", { className: 'text-sm font-medium', children: "Filter" }), (0, jsx_runtime_1.jsxs)(button_1.Button, { type: 'button', size: 'icon', variant: 'ghost', className: 'size-7', onClick: () => updateFilter(undefined), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: 'h-4 w-4' }), (0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: "Clear filter" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-2 sm:grid-cols-[1fr_0.9fr_1fr]', children: [(0, jsx_runtime_1.jsxs)(select_1.Select, { disabled:
|
|
533
|
+
}), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: sortFieldOptions.length > 0
|
|
534
|
+
? 'Field'
|
|
535
|
+
: 'No sortable fields' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: sortFieldOptions.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.value, children: item.label }, item.value))) }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-1.5', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center justify-between gap-2', children: [(0, jsx_runtime_1.jsx)("span", { className: 'text-sm font-medium', children: "Filter" }), (0, jsx_runtime_1.jsxs)(button_1.Button, { type: 'button', size: 'icon', variant: 'ghost', className: 'size-7', onClick: () => updateFilter(undefined), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: 'h-4 w-4' }), (0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: "Clear filter" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-2 sm:grid-cols-[1fr_0.9fr_1fr]', children: [(0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: filterFieldOptions.length === 0, value: filterState?.field || '', onValueChange: (value) => updateFilter(value
|
|
349
536
|
? {
|
|
350
537
|
field: value,
|
|
351
538
|
operator: filterState?.operator ?? 'equals',
|
|
352
539
|
value: filterState?.value ?? '',
|
|
353
540
|
}
|
|
354
|
-
: undefined), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder:
|
|
355
|
-
|
|
356
|
-
.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.value, children: item.label }, item.value))) }) })] }), (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !filterState?.field, value: filterState?.operator || 'equals', onValueChange: (value) => filterState
|
|
541
|
+
: undefined), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: filterFieldOptions.length > 0
|
|
542
|
+
? 'Field'
|
|
543
|
+
: 'No filterable fields' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: filterFieldOptions.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.value, children: item.label }, item.value))) }) })] }), (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !filterState?.field, value: filterState?.operator || 'equals', onValueChange: (value) => filterState
|
|
357
544
|
? updateFilter({
|
|
358
545
|
...filterState,
|
|
359
546
|
operator: value,
|
|
@@ -369,12 +556,18 @@ const ListBindingEditor = ({ contentTypes, field, binding, onChange, }) => {
|
|
|
369
556
|
})
|
|
370
557
|
: undefined }))] })] })] }) }), (0, jsx_runtime_1.jsx)(PanelSection, { title: 'Mapping', children: (0, jsx_runtime_1.jsx)("div", { className: 'grid gap-2', children: targetFields.map(([targetField, targetFieldConfig]) => {
|
|
371
558
|
const source = binding?.map?.[targetField];
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
559
|
+
const summary = mappingSourceSummary(source, selectedSource?.name ?? 'Source');
|
|
560
|
+
const isOpen = openMappingField === targetField;
|
|
561
|
+
return ((0, jsx_runtime_1.jsx)(collapsible_1.Collapsible, { open: isOpen, onOpenChange: (open) => setOpenMappingField(open ? targetField : null), children: (0, jsx_runtime_1.jsxs)("div", { className: 'overflow-hidden rounded-md border border-border bg-background/70 transition-colors hover:border-foreground/20', children: [(0, jsx_runtime_1.jsx)(collapsible_1.CollapsibleTrigger, { asChild: true, children: (0, jsx_runtime_1.jsxs)("button", { type: 'button', disabled: !selectedSource, className: 'group grid w-full grid-cols-[minmax(6rem,0.45fr)_auto_minmax(0,1fr)_auto] items-center gap-3 px-3 py-2.5 text-left outline-none transition-colors hover:bg-muted/50 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', children: [(0, jsx_runtime_1.jsx)("span", { className: 'truncate text-sm font-semibold', children: targetField }), (0, jsx_runtime_1.jsx)(badge_1.Badge, { variant: source ? 'secondary' : 'outline', className: 'max-w-40 truncate', children: summary.mode }), (0, jsx_runtime_1.jsx)("span", { className: 'truncate text-sm text-muted-foreground', children: selectedSource
|
|
562
|
+
? summary.detail
|
|
563
|
+
: 'Select a source collection first' }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: 'h-4 w-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-90' }), (0, jsx_runtime_1.jsxs)("span", { className: 'sr-only', children: ["Configure ", targetField, " mapping"] })] }) }), (0, jsx_runtime_1.jsx)(collapsible_1.CollapsibleContent, { children: (0, jsx_runtime_1.jsx)("div", { className: 'border-t border-border bg-muted/20 p-3', children: selectedSource ? ((0, jsx_runtime_1.jsx)(MappingSourceEditor, { contentTypes: contentTypes, currentSource: selectedSource, targetField: targetFieldConfig, source: source, onChange: (nextSource) => {
|
|
564
|
+
const map = { ...(binding?.map ?? {}) };
|
|
565
|
+
if (nextSource)
|
|
566
|
+
map[targetField] = nextSource;
|
|
567
|
+
else
|
|
568
|
+
delete map[targetField];
|
|
569
|
+
emit({ map });
|
|
570
|
+
} })) : null }) })] }) }, targetField));
|
|
378
571
|
}) }) })] }));
|
|
379
572
|
};
|
|
380
573
|
const DynamicDataControl = ({ contentType, documentContentType, fieldName, field, contentTypes, bindings, onChange, open, onOpenChange, mode = 'full', }) => {
|