@firecms/collection_editor 3.0.1 → 3.1.0-canary.02232f4

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