@firecms/collection_editor 3.0.0 → 3.1.0-canary.1df3b2c
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/ConfigControllerProvider.d.ts +6 -0
- package/dist/api/generateCollectionApi.d.ts +71 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +9677 -5837
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +9653 -5813
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +14 -0
- package/dist/types/collection_inference.d.ts +8 -2
- package/dist/types/config_controller.d.ts +31 -1
- package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
- package/dist/ui/KanbanSetupAction.d.ts +10 -0
- package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +33 -0
- package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
- package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
- package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
- package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
- package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
- package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
- package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
- package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
- package/dist/useCollectionEditorPlugin.d.ts +7 -1
- package/dist/utils/validateCollectionJson.d.ts +22 -0
- package/package.json +11 -11
- package/src/ConfigControllerProvider.tsx +81 -47
- package/src/api/generateCollectionApi.ts +119 -0
- package/src/api/index.ts +1 -0
- package/src/index.ts +28 -1
- package/src/types/collection_editor_controller.tsx +16 -3
- package/src/types/collection_inference.ts +15 -2
- package/src/types/config_controller.tsx +37 -1
- package/src/ui/AddKanbanColumnAction.tsx +203 -0
- package/src/ui/EditorCollectionActionStart.tsx +1 -2
- package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
- package/src/ui/KanbanSetupAction.tsx +38 -0
- package/src/ui/MissingReferenceWidget.tsx +1 -1
- package/src/ui/NewCollectionButton.tsx +1 -1
- package/src/ui/PropertyAddColumnComponent.tsx +1 -1
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +225 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +209 -258
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +226 -173
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +130 -67
- package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +190 -91
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +333 -0
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -96
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +6 -7
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +1 -3
- package/src/ui/collection_editor/EnumForm.tsx +147 -100
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +93 -0
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +57 -36
- package/src/ui/collection_editor/KanbanConfigSection.tsx +207 -0
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +22 -41
- package/src/ui/collection_editor/PropertyEditView.tsx +205 -141
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
- package/src/ui/collection_editor/PropertyTree.tsx +130 -58
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +171 -162
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
- package/src/ui/collection_editor/ViewModeSwitch.tsx +41 -0
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +1 -0
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +117 -35
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +28 -21
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +115 -39
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +1 -5
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +23 -2
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +861 -0
- package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
- package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
- package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
- package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +1 -1
- package/src/useCollectionEditorPlugin.tsx +32 -17
- package/src/utils/validateCollectionJson.ts +380 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import React, { useMemo, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
EntityCollection,
|
|
4
|
+
FieldCaption,
|
|
5
|
+
getFieldConfig,
|
|
6
|
+
Property,
|
|
7
|
+
PropertyConfigBadge,
|
|
8
|
+
resolveCollection,
|
|
9
|
+
unslugify,
|
|
10
|
+
useAuthController,
|
|
11
|
+
useCustomizationController
|
|
12
|
+
} from "@firecms/core";
|
|
13
|
+
import {
|
|
14
|
+
BooleanSwitchWithLabel,
|
|
15
|
+
CloseIcon,
|
|
16
|
+
Container,
|
|
17
|
+
IconButton,
|
|
18
|
+
Select,
|
|
19
|
+
SelectItem,
|
|
20
|
+
TextField,
|
|
21
|
+
Typography
|
|
22
|
+
} from "@firecms/ui";
|
|
23
|
+
|
|
24
|
+
import { useFormex } from "@firecms/formex";
|
|
25
|
+
import { LayoutModeSwitch } from "./LayoutModeSwitch";
|
|
26
|
+
import { ViewModeSwitch } from "./ViewModeSwitch";
|
|
27
|
+
import { KanbanConfigSection } from "./KanbanConfigSection";
|
|
28
|
+
import { PropertyFormDialog } from "./PropertyEditView";
|
|
29
|
+
|
|
30
|
+
export function DisplaySettingsForm({
|
|
31
|
+
expandKanban
|
|
32
|
+
}: {
|
|
33
|
+
expandKanban?: boolean;
|
|
34
|
+
}) {
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
values,
|
|
38
|
+
setFieldValue,
|
|
39
|
+
handleChange,
|
|
40
|
+
submitCount
|
|
41
|
+
} = useFormex<EntityCollection>();
|
|
42
|
+
|
|
43
|
+
const [orderPropertyDialogOpen, setOrderPropertyDialogOpen] = useState(false);
|
|
44
|
+
|
|
45
|
+
const authController = useAuthController();
|
|
46
|
+
const customizationController = useCustomizationController();
|
|
47
|
+
|
|
48
|
+
// Resolve collection to get properties for order property select
|
|
49
|
+
const resolvedCollection = useMemo(() => resolveCollection({
|
|
50
|
+
collection: values,
|
|
51
|
+
path: values.path,
|
|
52
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
53
|
+
authController
|
|
54
|
+
}), [values, customizationController.propertyConfigs, authController]);
|
|
55
|
+
|
|
56
|
+
// Get number properties (for orderProperty)
|
|
57
|
+
const numberProperties = useMemo(() => {
|
|
58
|
+
const result: { key: string; label: string; property: Property; }[] = [];
|
|
59
|
+
if (!resolvedCollection.properties) return result;
|
|
60
|
+
|
|
61
|
+
Object.entries(resolvedCollection.properties).forEach(([key, prop]) => {
|
|
62
|
+
if (prop && 'dataType' in prop && prop.dataType === 'number') {
|
|
63
|
+
result.push({
|
|
64
|
+
key,
|
|
65
|
+
label: (prop as Property).name || key,
|
|
66
|
+
property: prop as Property
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return result;
|
|
71
|
+
}, [resolvedCollection.properties]);
|
|
72
|
+
|
|
73
|
+
const showErrors = submitCount > 0;
|
|
74
|
+
|
|
75
|
+
// Document ID generation value
|
|
76
|
+
let customIdValue: "true" | "false" | "optional" | "code_defined" | undefined;
|
|
77
|
+
if (typeof values.customId === "object") {
|
|
78
|
+
customIdValue = "code_defined";
|
|
79
|
+
} else if (values.customId === true) {
|
|
80
|
+
customIdValue = "true";
|
|
81
|
+
} else if (values.customId === false) {
|
|
82
|
+
customIdValue = "false";
|
|
83
|
+
} else if (values.customId === "optional") {
|
|
84
|
+
customIdValue = "optional";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className={"overflow-auto my-auto"}>
|
|
89
|
+
<Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
90
|
+
|
|
91
|
+
<div>
|
|
92
|
+
<Typography variant={"h5"} className={"flex-grow"}>
|
|
93
|
+
Display settings
|
|
94
|
+
</Typography>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div className={"grid grid-cols-12 gap-4"}>
|
|
98
|
+
|
|
99
|
+
{/* Layout Mode (Side dialog vs Full screen) */}
|
|
100
|
+
<LayoutModeSwitch
|
|
101
|
+
className={"col-span-12"}
|
|
102
|
+
value={values.openEntityMode ?? "side_panel"}
|
|
103
|
+
onChange={(value) => setFieldValue("openEntityMode", value)} />
|
|
104
|
+
|
|
105
|
+
{/* View Mode (Table/Cards/Kanban) */}
|
|
106
|
+
<ViewModeSwitch
|
|
107
|
+
className={"col-span-12"}
|
|
108
|
+
value={values.defaultViewMode ?? "table"}
|
|
109
|
+
onChange={(value) => setFieldValue("defaultViewMode", value)} />
|
|
110
|
+
|
|
111
|
+
{/* Kanban Column Property */}
|
|
112
|
+
<KanbanConfigSection className={"col-span-12"} forceExpanded={expandKanban} />
|
|
113
|
+
|
|
114
|
+
{/* Order Property */}
|
|
115
|
+
<div className={"col-span-12 mt-4"}>
|
|
116
|
+
{(() => {
|
|
117
|
+
const orderPropertyMissing = Boolean(values.orderProperty) &&
|
|
118
|
+
!numberProperties.some(p => p.key === values.orderProperty);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<>
|
|
122
|
+
<Select
|
|
123
|
+
key={`order-select-${numberProperties.length}`}
|
|
124
|
+
name="orderProperty"
|
|
125
|
+
label="Order Property"
|
|
126
|
+
size={"large"}
|
|
127
|
+
fullWidth={true}
|
|
128
|
+
position={"item-aligned"}
|
|
129
|
+
disabled={numberProperties.length === 0}
|
|
130
|
+
error={orderPropertyMissing}
|
|
131
|
+
value={values.orderProperty ?? ""}
|
|
132
|
+
onValueChange={(v) => {
|
|
133
|
+
setFieldValue("orderProperty", v || undefined);
|
|
134
|
+
}}
|
|
135
|
+
renderValue={(value) => {
|
|
136
|
+
if (orderPropertyMissing) {
|
|
137
|
+
return <span className="text-red-500">{value} (not found)</span>;
|
|
138
|
+
}
|
|
139
|
+
const prop = numberProperties.find(p => p.key === value);
|
|
140
|
+
if (!prop) return "Select a property";
|
|
141
|
+
const fieldConfig = getFieldConfig(prop.property, customizationController.propertyConfigs);
|
|
142
|
+
return (
|
|
143
|
+
<div className="flex items-center gap-2">
|
|
144
|
+
<PropertyConfigBadge propertyConfig={fieldConfig} />
|
|
145
|
+
<span>{prop.label}</span>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}}
|
|
149
|
+
endAdornment={values.orderProperty ? (
|
|
150
|
+
<IconButton
|
|
151
|
+
size="small"
|
|
152
|
+
onClick={(e) => {
|
|
153
|
+
e.stopPropagation();
|
|
154
|
+
setFieldValue("orderProperty", undefined);
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
<CloseIcon size="small" />
|
|
158
|
+
</IconButton>
|
|
159
|
+
) : undefined}
|
|
160
|
+
>
|
|
161
|
+
{numberProperties.map((prop) => {
|
|
162
|
+
const fieldConfig = getFieldConfig(prop.property, customizationController.propertyConfigs);
|
|
163
|
+
return (
|
|
164
|
+
<SelectItem key={prop.key} value={prop.key}>
|
|
165
|
+
<div className="flex items-center gap-3">
|
|
166
|
+
<PropertyConfigBadge propertyConfig={fieldConfig} />
|
|
167
|
+
<div>
|
|
168
|
+
<div>{prop.label}</div>
|
|
169
|
+
<Typography variant="caption" color="secondary">
|
|
170
|
+
{fieldConfig?.name || "Number"}
|
|
171
|
+
</Typography>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</SelectItem>
|
|
175
|
+
);
|
|
176
|
+
})}
|
|
177
|
+
</Select>
|
|
178
|
+
<FieldCaption error={orderPropertyMissing}>
|
|
179
|
+
{orderPropertyMissing
|
|
180
|
+
? `Property "${values.orderProperty}" does not exist or is not a number property. Please select a valid property or clear the selection.`
|
|
181
|
+
: numberProperties.length === 0
|
|
182
|
+
? "No number properties found. Add a number property to enable ordering."
|
|
183
|
+
: "Select a number property to persist the order of items"
|
|
184
|
+
}
|
|
185
|
+
</FieldCaption>
|
|
186
|
+
</>
|
|
187
|
+
);
|
|
188
|
+
})()}
|
|
189
|
+
{(() => {
|
|
190
|
+
const orderPropertyMissing = Boolean(values.orderProperty) &&
|
|
191
|
+
!numberProperties.some(p => p.key === values.orderProperty);
|
|
192
|
+
const showCreateButton = !values.orderProperty || orderPropertyMissing;
|
|
193
|
+
|
|
194
|
+
const dialogPropertyKey = orderPropertyMissing && values.orderProperty
|
|
195
|
+
? values.orderProperty
|
|
196
|
+
: "__order";
|
|
197
|
+
const dialogPropertyName = orderPropertyMissing && values.orderProperty
|
|
198
|
+
? unslugify(values.orderProperty)
|
|
199
|
+
: "Order";
|
|
200
|
+
|
|
201
|
+
if (!showCreateButton) return null;
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<>
|
|
205
|
+
<button
|
|
206
|
+
type="button"
|
|
207
|
+
className="ml-3.5 text-sm text-primary hover:text-primary-dark mt-2"
|
|
208
|
+
onClick={() => setOrderPropertyDialogOpen(true)}
|
|
209
|
+
>
|
|
210
|
+
+ Create "{dialogPropertyKey}" property
|
|
211
|
+
</button>
|
|
212
|
+
<PropertyFormDialog
|
|
213
|
+
open={orderPropertyDialogOpen}
|
|
214
|
+
onCancel={() => setOrderPropertyDialogOpen(false)}
|
|
215
|
+
property={{
|
|
216
|
+
dataType: "number",
|
|
217
|
+
name: dialogPropertyName,
|
|
218
|
+
disabled: true,
|
|
219
|
+
hideFromCollection: true
|
|
220
|
+
}}
|
|
221
|
+
propertyKey={dialogPropertyKey}
|
|
222
|
+
existingProperty={false}
|
|
223
|
+
autoOpenTypeSelect={false}
|
|
224
|
+
autoUpdateId={false}
|
|
225
|
+
inArray={false}
|
|
226
|
+
allowDataInference={false}
|
|
227
|
+
propertyConfigs={customizationController.propertyConfigs}
|
|
228
|
+
collectionEditable={true}
|
|
229
|
+
existingPropertyKeys={Object.keys(values.properties ?? {})}
|
|
230
|
+
onPropertyChanged={({ id, property }) => {
|
|
231
|
+
const newProperties = {
|
|
232
|
+
...values.properties,
|
|
233
|
+
[id!]: property
|
|
234
|
+
};
|
|
235
|
+
const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties ?? {})), id];
|
|
236
|
+
setFieldValue("properties", newProperties);
|
|
237
|
+
setFieldValue("propertiesOrder", newPropertiesOrder);
|
|
238
|
+
setFieldValue("orderProperty", id);
|
|
239
|
+
setOrderPropertyDialogOpen(false);
|
|
240
|
+
}}
|
|
241
|
+
/>
|
|
242
|
+
</>
|
|
243
|
+
);
|
|
244
|
+
})()}
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
{/* Default row size */}
|
|
248
|
+
<div className={"col-span-12"}>
|
|
249
|
+
<Select
|
|
250
|
+
name="defaultSize"
|
|
251
|
+
size={"large"}
|
|
252
|
+
fullWidth={true}
|
|
253
|
+
label="Default row size"
|
|
254
|
+
position={"item-aligned"}
|
|
255
|
+
onChange={handleChange}
|
|
256
|
+
value={values.defaultSize ?? ""}
|
|
257
|
+
renderValue={(value: any) => value.toUpperCase()}
|
|
258
|
+
>
|
|
259
|
+
{["xs", "s", "m", "l", "xl"].map((value) => (
|
|
260
|
+
<SelectItem
|
|
261
|
+
key={`size-select-${value}`}
|
|
262
|
+
value={value}>
|
|
263
|
+
{value.toUpperCase()}
|
|
264
|
+
</SelectItem>
|
|
265
|
+
))}
|
|
266
|
+
</Select>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
{/* Side dialog width */}
|
|
270
|
+
<div className={"col-span-12"}>
|
|
271
|
+
<TextField
|
|
272
|
+
name={"sideDialogWidth"}
|
|
273
|
+
type={"number"}
|
|
274
|
+
aria-describedby={"sideDialogWidth-helper"}
|
|
275
|
+
onChange={(e) => {
|
|
276
|
+
const value = e.target.value;
|
|
277
|
+
if (!value) {
|
|
278
|
+
setFieldValue("sideDialogWidth", null);
|
|
279
|
+
} else if (!isNaN(Number(value))) {
|
|
280
|
+
setFieldValue("sideDialogWidth", Number(value));
|
|
281
|
+
}
|
|
282
|
+
}}
|
|
283
|
+
endAdornment={<IconButton
|
|
284
|
+
size={"small"}
|
|
285
|
+
onClick={() => {
|
|
286
|
+
setFieldValue("sideDialogWidth", null);
|
|
287
|
+
}}
|
|
288
|
+
disabled={!values.sideDialogWidth}>
|
|
289
|
+
<CloseIcon size={"small"} />
|
|
290
|
+
</IconButton>}
|
|
291
|
+
value={values.sideDialogWidth ?? ""}
|
|
292
|
+
label={"Side dialog width"} />
|
|
293
|
+
<FieldCaption>
|
|
294
|
+
Optionally define the width (in pixels) of entities side dialog. Default is 768px
|
|
295
|
+
</FieldCaption>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
{/* Inline editing */}
|
|
299
|
+
<div className={"col-span-12"}>
|
|
300
|
+
<BooleanSwitchWithLabel
|
|
301
|
+
position={"start"}
|
|
302
|
+
size={"large"}
|
|
303
|
+
label={values.inlineEditing === undefined || values.inlineEditing ? "Data can be edited directly in the table view" : "Data can be edited only in the form view"}
|
|
304
|
+
onValueChange={(v) => setFieldValue("inlineEditing", v)}
|
|
305
|
+
value={values.inlineEditing === undefined ? true : values.inlineEditing}
|
|
306
|
+
/>
|
|
307
|
+
<FieldCaption>
|
|
308
|
+
Allow editing data directly in the table view, without opening the form view.
|
|
309
|
+
</FieldCaption>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
{/* Include JSON view */}
|
|
313
|
+
<div className={"col-span-12"}>
|
|
314
|
+
<BooleanSwitchWithLabel
|
|
315
|
+
position={"start"}
|
|
316
|
+
size={"large"}
|
|
317
|
+
label={values.includeJsonView === undefined || values.includeJsonView ? "Include JSON view" : "Do not include JSON view"}
|
|
318
|
+
onValueChange={(v) => setFieldValue("includeJsonView", v)}
|
|
319
|
+
value={values.includeJsonView === undefined ? true : values.includeJsonView}
|
|
320
|
+
/>
|
|
321
|
+
<FieldCaption>
|
|
322
|
+
Include the JSON representation of the document.
|
|
323
|
+
</FieldCaption>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<div style={{ height: "52px" }} />
|
|
329
|
+
|
|
330
|
+
</Container>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
@@ -26,9 +26,11 @@ import { useFormex } from "@firecms/formex";
|
|
|
26
26
|
import { EntityActionsSelectDialog } from "./EntityActionsSelectDialog";
|
|
27
27
|
|
|
28
28
|
export function EntityActionsEditTab({
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
collection,
|
|
30
|
+
embedded = false
|
|
31
|
+
}: {
|
|
31
32
|
collection: PersistedCollection,
|
|
33
|
+
embedded?: boolean;
|
|
32
34
|
}) {
|
|
33
35
|
|
|
34
36
|
const { entityActions: contextEntityActions } = useCustomizationController();
|
|
@@ -47,103 +49,111 @@ export function EntityActionsEditTab({
|
|
|
47
49
|
const hardCodedEntityActions = collection.entityActions?.filter((e): e is EntityAction<any> => typeof e !== "string") ?? [];
|
|
48
50
|
const totalEntityActions = resolvedEntityActions.length + hardCodedEntityActions.length;
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
<div className={"
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
</div>
|
|
52
|
+
const content = (
|
|
53
|
+
<div className={"flex flex-col gap-16"}>
|
|
54
|
+
<div className={"flex-grow flex flex-col gap-4 items-start"}>
|
|
55
|
+
<Typography variant={"h6"}>
|
|
56
|
+
Custom actions
|
|
57
|
+
</Typography>
|
|
58
|
+
|
|
59
|
+
{totalEntityActions === 0 &&
|
|
60
|
+
<Alert action={<Button variant="text"
|
|
61
|
+
size={"small"}
|
|
62
|
+
href={"https://firecms.co/docs/custom_actions"}
|
|
63
|
+
component={"a"}
|
|
64
|
+
rel="noopener noreferrer"
|
|
65
|
+
target="_blank">More info</Button>}>
|
|
66
|
+
Define your own custom actions by uploading them with the CLI.
|
|
67
|
+
</Alert>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
{<>
|
|
71
|
+
<Paper className={"flex flex-col gap-4 p-2 w-full"}>
|
|
72
|
+
<Table>
|
|
73
|
+
<TableBody>
|
|
74
|
+
{resolvedEntityActions.map((action) => (
|
|
75
|
+
<TableRow key={action.key}>
|
|
76
|
+
<TableCell
|
|
77
|
+
align="left">
|
|
78
|
+
<Typography variant={"subtitle2"} className={"flex-grow"}>
|
|
79
|
+
{action.name}
|
|
80
|
+
</Typography>
|
|
81
|
+
</TableCell>
|
|
82
|
+
<TableCell
|
|
83
|
+
align="right">
|
|
84
|
+
<Tooltip title={"Remove"}
|
|
85
|
+
asChild={true}>
|
|
86
|
+
<IconButton size="small"
|
|
87
|
+
onClick={(e) => {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
e.stopPropagation();
|
|
90
|
+
setActionToDelete(action.key);
|
|
91
|
+
}}
|
|
92
|
+
color="inherit">
|
|
93
|
+
<DeleteIcon size={"small"} />
|
|
94
|
+
</IconButton>
|
|
95
|
+
</Tooltip>
|
|
96
|
+
</TableCell>
|
|
97
|
+
</TableRow>
|
|
98
|
+
))}
|
|
99
|
+
{hardCodedEntityActions.map((action) => (
|
|
100
|
+
<TableRow key={action.key}>
|
|
101
|
+
<TableCell
|
|
102
|
+
align="left">
|
|
103
|
+
<Typography variant={"subtitle2"} className={"flex-grow"}>
|
|
104
|
+
{action.name}
|
|
105
|
+
</Typography>
|
|
106
|
+
<Typography variant={"caption"} className={"flex-grow"}>
|
|
107
|
+
This action is defined in code with
|
|
108
|
+
key <code>{action.key}</code>
|
|
109
|
+
</Typography>
|
|
110
|
+
</TableCell>
|
|
111
|
+
</TableRow>
|
|
112
|
+
))}
|
|
113
|
+
</TableBody>
|
|
114
|
+
</Table>
|
|
115
|
+
|
|
116
|
+
<Button
|
|
117
|
+
onClick={() => {
|
|
118
|
+
setAddEntityActionDialogOpen(true);
|
|
119
|
+
}}
|
|
120
|
+
startIcon={<AddIcon />}>
|
|
121
|
+
Add custom entity action
|
|
122
|
+
</Button>
|
|
123
|
+
</Paper>
|
|
124
|
+
|
|
125
|
+
</>}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
</div>
|
|
130
129
|
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
133
132
|
|
|
134
|
-
|
|
133
|
+
return (
|
|
134
|
+
<>
|
|
135
|
+
{embedded ? (
|
|
136
|
+
content
|
|
137
|
+
) : (
|
|
138
|
+
<div className={"overflow-auto my-auto"}>
|
|
139
|
+
<Container maxWidth={"2xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
140
|
+
{content}
|
|
141
|
+
</Container>
|
|
142
|
+
<div style={{ height: "52px" }} />
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
135
145
|
|
|
136
146
|
{actionToDelete &&
|
|
137
147
|
<ConfirmationDialog open={Boolean(actionToDelete)}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
onAccept={() => {
|
|
149
|
+
setFieldValue("entityActions", values.entityActions?.filter(e => e !== actionToDelete));
|
|
150
|
+
setActionToDelete(undefined);
|
|
151
|
+
}}
|
|
152
|
+
onCancel={() => setActionToDelete(undefined)}
|
|
153
|
+
title={<>Remove this action?</>}
|
|
154
|
+
body={<>This will <b>not
|
|
155
|
+
delete any data</b>, only
|
|
156
|
+
the action in the CMS</>} />}
|
|
147
157
|
|
|
148
158
|
<EntityActionsSelectDialog
|
|
149
159
|
open={addEntityActionDialogOpen}
|
|
@@ -157,7 +167,7 @@ export function EntityActionsEditTab({
|
|
|
157
167
|
setFieldValue("entityActions", value);
|
|
158
168
|
}
|
|
159
169
|
setAddEntityActionDialogOpen(false);
|
|
160
|
-
}}/>
|
|
161
|
-
|
|
170
|
+
}} />
|
|
171
|
+
</>
|
|
162
172
|
);
|
|
163
173
|
}
|
|
@@ -3,9 +3,9 @@ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography }
|
|
|
3
3
|
import React from "react";
|
|
4
4
|
|
|
5
5
|
export function EntityActionsSelectDialog({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
open,
|
|
7
|
+
onClose
|
|
8
|
+
}: { open: boolean, onClose: (selectedActionKey?: string) => void }) {
|
|
9
9
|
const {
|
|
10
10
|
entityActions
|
|
11
11
|
} = useCustomizationController();
|
|
@@ -27,15 +27,14 @@ export function EntityActionsSelectDialog({
|
|
|
27
27
|
})}
|
|
28
28
|
{(entityActions ?? []).length === 0 &&
|
|
29
29
|
<Typography variant={"body2"}>
|
|
30
|
-
No custom actions defined. Define your custom actions in the customization settings, before using
|
|
30
|
+
No custom actions defined. Define your custom actions in the customization settings, before using
|
|
31
|
+
this
|
|
31
32
|
dialog.
|
|
32
33
|
</Typography>
|
|
33
34
|
}
|
|
34
35
|
</DialogContent>
|
|
35
36
|
<DialogActions>
|
|
36
|
-
<Button
|
|
37
|
-
color={"primary"}
|
|
38
|
-
onClick={() => onClose()}>Cancel</Button>
|
|
37
|
+
<Button onClick={() => onClose()}>Cancel</Button>
|
|
39
38
|
</DialogActions>
|
|
40
39
|
</Dialog>
|
|
41
40
|
}
|
|
@@ -33,9 +33,7 @@ export function EntityCustomViewsSelectDialog({
|
|
|
33
33
|
}
|
|
34
34
|
</DialogContent>
|
|
35
35
|
<DialogActions>
|
|
36
|
-
<Button
|
|
37
|
-
color={"primary"}
|
|
38
|
-
onClick={() => onClose()}>Cancel</Button>
|
|
36
|
+
<Button onClick={() => onClose()}>Cancel</Button>
|
|
39
37
|
</DialogActions>
|
|
40
38
|
</Dialog>
|
|
41
39
|
}
|