@firecms/collection_editor 3.1.0-canary.1df3b2c → 3.1.0-canary.75005e4
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/index.es.js +6984 -3759
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +6982 -3757
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +118 -0
- package/dist/locales/en.d.ts +118 -0
- package/dist/locales/es.d.ts +118 -0
- package/dist/locales/fr.d.ts +118 -0
- package/dist/locales/hi.d.ts +118 -0
- package/dist/locales/it.d.ts +118 -0
- package/dist/locales/pt.d.ts +118 -0
- package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +5 -1
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +4 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +2 -1
- package/package.json +12 -12
- package/src/ConfigControllerProvider.tsx +1 -0
- package/src/locales/de.ts +123 -0
- package/src/locales/en.ts +143 -0
- package/src/locales/es.ts +123 -0
- package/src/locales/fr.ts +123 -0
- package/src/locales/hi.ts +123 -0
- package/src/locales/it.ts +123 -0
- package/src/locales/pt.ts +123 -0
- package/src/ui/EditorCollectionAction.tsx +3 -3
- package/src/ui/EditorEntityAction.tsx +3 -2
- package/src/ui/NewCollectionButton.tsx +3 -1
- package/src/ui/NewCollectionCard.tsx +7 -4
- package/src/ui/PropertyAddColumnComponent.tsx +3 -2
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +29 -11
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +28 -25
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +55 -42
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +23 -19
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +12 -10
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +19 -17
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +11 -12
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +5 -6
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +5 -5
- package/src/ui/collection_editor/EnumForm.tsx +6 -2
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +8 -7
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +42 -42
- package/src/ui/collection_editor/GetCodeDialog.tsx +13 -12
- package/src/ui/collection_editor/KanbanConfigSection.tsx +11 -9
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +7 -4
- package/src/ui/collection_editor/PropertyEditView.tsx +75 -66
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +16 -19
- package/src/ui/collection_editor/ViewModeSwitch.tsx +8 -6
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +6 -3
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +5 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +6 -4
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +20 -18
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +5 -4
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -7
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +22 -23
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +5 -4
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +46 -51
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +12 -10
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +7 -4
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +8 -3
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +5 -2
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +7 -5
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +10 -7
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +11 -9
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +4 -1
- package/src/useCollectionEditorPlugin.tsx +22 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { EntityCollection, useNavigationController, useSnackbarController, AIIcon } from "@firecms/core";
|
|
2
|
+
import { EntityCollection, useNavigationController, useSnackbarController, AIIcon, useTranslation } from "@firecms/core";
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
5
|
CircularProgress,
|
|
@@ -48,6 +48,11 @@ export interface AICollectionGeneratorPopoverProps {
|
|
|
48
48
|
* Whether to show the label text
|
|
49
49
|
*/
|
|
50
50
|
showLabel?: boolean;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Optional analytics callback
|
|
54
|
+
*/
|
|
55
|
+
onAnalyticsEvent?: (event: string, params?: object) => void;
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
export function AICollectionGeneratorPopover({
|
|
@@ -56,13 +61,15 @@ export function AICollectionGeneratorPopover({
|
|
|
56
61
|
generateCollection,
|
|
57
62
|
trigger,
|
|
58
63
|
size = "small",
|
|
59
|
-
showLabel = true
|
|
64
|
+
showLabel = true,
|
|
65
|
+
onAnalyticsEvent
|
|
60
66
|
}: AICollectionGeneratorPopoverProps) {
|
|
61
67
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
62
68
|
const [prompt, setPrompt] = useState("");
|
|
63
69
|
const [loading, setLoading] = useState(false);
|
|
64
70
|
const [error, setError] = useState<string | null>(null);
|
|
65
71
|
|
|
72
|
+
const { t } = useTranslation();
|
|
66
73
|
const navigation = useNavigationController();
|
|
67
74
|
const snackbarController = useSnackbarController();
|
|
68
75
|
|
|
@@ -74,6 +81,9 @@ export function AICollectionGeneratorPopover({
|
|
|
74
81
|
setLoading(true);
|
|
75
82
|
setError(null);
|
|
76
83
|
|
|
84
|
+
const mode = existingCollection ? "modify" : "create";
|
|
85
|
+
onAnalyticsEvent?.("ai_collection_generate_start", { mode });
|
|
86
|
+
|
|
77
87
|
try {
|
|
78
88
|
const collectionsContext = existingCollections.map(c => ({
|
|
79
89
|
path: c.path,
|
|
@@ -100,6 +110,10 @@ export function AICollectionGeneratorPopover({
|
|
|
100
110
|
onGenerated(result.collection, result.operations);
|
|
101
111
|
setMenuOpen(false);
|
|
102
112
|
setPrompt("");
|
|
113
|
+
onAnalyticsEvent?.("ai_collection_generate_success", {
|
|
114
|
+
mode,
|
|
115
|
+
operationsCount: result.operations?.length
|
|
116
|
+
});
|
|
103
117
|
snackbarController.open({
|
|
104
118
|
type: "success",
|
|
105
119
|
message: existingCollection
|
|
@@ -112,6 +126,10 @@ export function AICollectionGeneratorPopover({
|
|
|
112
126
|
? e.message
|
|
113
127
|
: "Failed to generate collection. Please try again.";
|
|
114
128
|
setError(errorMessage);
|
|
129
|
+
onAnalyticsEvent?.("ai_collection_generate_error", {
|
|
130
|
+
mode,
|
|
131
|
+
error: errorMessage
|
|
132
|
+
});
|
|
115
133
|
snackbarController.open({
|
|
116
134
|
type: "error",
|
|
117
135
|
message: errorMessage
|
|
@@ -138,13 +156,13 @@ export function AICollectionGeneratorPopover({
|
|
|
138
156
|
: <AIIcon size="small" />
|
|
139
157
|
}
|
|
140
158
|
>
|
|
141
|
-
|
|
159
|
+
{t("ai_assist")}
|
|
142
160
|
</Button>
|
|
143
161
|
) : (
|
|
144
162
|
<IconButton
|
|
145
163
|
size={size}
|
|
146
164
|
disabled={loading}
|
|
147
|
-
aria-label="
|
|
165
|
+
aria-label={t("ai_assist")}
|
|
148
166
|
>
|
|
149
167
|
{loading
|
|
150
168
|
? <CircularProgress size="smallest" />
|
|
@@ -168,14 +186,14 @@ export function AICollectionGeneratorPopover({
|
|
|
168
186
|
<div className="flex items-center gap-2">
|
|
169
187
|
<AIIcon size="small" />
|
|
170
188
|
<Typography variant="subtitle2">
|
|
171
|
-
{existingCollection ? "
|
|
189
|
+
{existingCollection ? t("modify_collection_with_ai") : t("generate_collection_with_ai")}
|
|
172
190
|
</Typography>
|
|
173
191
|
</div>
|
|
174
192
|
|
|
175
193
|
<Typography variant="caption" color="secondary">
|
|
176
194
|
{existingCollection
|
|
177
|
-
? "
|
|
178
|
-
: "
|
|
195
|
+
? t("describe_changes_to_make")
|
|
196
|
+
: t("describe_collection_to_create")
|
|
179
197
|
}
|
|
180
198
|
</Typography>
|
|
181
199
|
|
|
@@ -188,8 +206,8 @@ export function AICollectionGeneratorPopover({
|
|
|
188
206
|
onChange={(e) => setPrompt(e.target.value)}
|
|
189
207
|
onKeyDown={handleKeyDown}
|
|
190
208
|
placeholder={existingCollection
|
|
191
|
-
? "
|
|
192
|
-
: "
|
|
209
|
+
? t("ai_placeholder_modify")
|
|
210
|
+
: t("ai_placeholder_create")
|
|
193
211
|
}
|
|
194
212
|
disabled={loading}
|
|
195
213
|
/>
|
|
@@ -207,7 +225,7 @@ export function AICollectionGeneratorPopover({
|
|
|
207
225
|
onClick={() => setMenuOpen(false)}
|
|
208
226
|
disabled={loading}
|
|
209
227
|
>
|
|
210
|
-
|
|
228
|
+
{t("cancel")}
|
|
211
229
|
</Button>
|
|
212
230
|
<Button
|
|
213
231
|
variant="filled"
|
|
@@ -216,7 +234,7 @@ export function AICollectionGeneratorPopover({
|
|
|
216
234
|
disabled={!prompt.trim() || loading}
|
|
217
235
|
startIcon={loading ? <CircularProgress size="smallest" /> : undefined}
|
|
218
236
|
>
|
|
219
|
-
{loading ? "
|
|
237
|
+
{loading ? t("generating") : t("generate_with_ai")}
|
|
220
238
|
</Button>
|
|
221
239
|
</div>
|
|
222
240
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useMemo, useState } from "react";
|
|
2
|
-
import { EntityCollection, FieldCaption, getFieldConfig, IconForView, Property, PropertyConfigBadge, resolveCollection, SearchIconsView, singular, toSnakeCase, unslugify, useAuthController, useCustomizationController } from "@firecms/core";
|
|
2
|
+
import { EntityCollection, FieldCaption, getFieldConfig, IconForView, Property, PropertyConfigBadge, resolveCollection, SearchIconsView, singular, toSnakeCase, unslugify, useAuthController, useCustomizationController, useTranslation } from "@firecms/core";
|
|
3
3
|
import {
|
|
4
4
|
BooleanSwitchWithLabel,
|
|
5
5
|
Chip,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Container,
|
|
9
9
|
DebouncedTextField,
|
|
10
10
|
Dialog,
|
|
11
|
+
HistoryIcon,
|
|
11
12
|
IconButton,
|
|
12
13
|
Select,
|
|
13
14
|
SelectItem,
|
|
@@ -60,6 +61,8 @@ export function CollectionDetailsForm({
|
|
|
60
61
|
const [iconDialogOpen, setIconDialogOpen] = useState(false);
|
|
61
62
|
const [orderPropertyDialogOpen, setOrderPropertyDialogOpen] = useState(false);
|
|
62
63
|
|
|
64
|
+
const { t } = useTranslation();
|
|
65
|
+
|
|
63
66
|
const authController = useAuthController();
|
|
64
67
|
const customizationController = useCustomizationController();
|
|
65
68
|
|
|
@@ -136,12 +139,12 @@ export function CollectionDetailsForm({
|
|
|
136
139
|
<div
|
|
137
140
|
className="flex flex-row gap-2 py-2 pt-3 items-center">
|
|
138
141
|
<Typography variant={!isNewCollection ? "h5" : "h4"} className={"flex-grow"}>
|
|
139
|
-
{isNewCollection ? "
|
|
142
|
+
{isNewCollection ? t("new_collection") : t("collection_with_name", { name: values?.name || "" })}
|
|
140
143
|
</Typography>
|
|
141
144
|
<DefaultDatabaseField databaseId={values.databaseId}
|
|
142
145
|
onDatabaseIdUpdate={updateDatabaseId} />
|
|
143
146
|
|
|
144
|
-
<Tooltip title={"
|
|
147
|
+
<Tooltip title={t("change_icon")}
|
|
145
148
|
asChild={true}>
|
|
146
149
|
<IconButton
|
|
147
150
|
shape={"square"}
|
|
@@ -153,7 +156,7 @@ export function CollectionDetailsForm({
|
|
|
153
156
|
|
|
154
157
|
{parentCollection && <Chip colorScheme={"tealDarker"}>
|
|
155
158
|
<Typography variant={"caption"}>
|
|
156
|
-
|
|
159
|
+
{t("is_subcollection_of")} <b>{parentCollection.name}</b>
|
|
157
160
|
</Typography>
|
|
158
161
|
</Chip>}
|
|
159
162
|
|
|
@@ -164,26 +167,26 @@ export function CollectionDetailsForm({
|
|
|
164
167
|
<TextField
|
|
165
168
|
value={values.name ?? ""}
|
|
166
169
|
onChange={(e: any) => updateName(e.target.value)}
|
|
167
|
-
label={"
|
|
170
|
+
label={t("name")}
|
|
168
171
|
autoFocus={true}
|
|
169
172
|
required
|
|
170
173
|
error={showErrors && Boolean(errors.name)} />
|
|
171
174
|
<FieldCaption error={touched.name && Boolean(errors.name)}>
|
|
172
|
-
{touched.name && Boolean(errors.name) ? errors.name : "
|
|
175
|
+
{touched.name && Boolean(errors.name) ? errors.name : t("collection_name_description")}
|
|
173
176
|
</FieldCaption>
|
|
174
177
|
</div>
|
|
175
178
|
|
|
176
179
|
<div className={cls("col-span-12 ")}>
|
|
177
180
|
<Field name={"path"}
|
|
178
181
|
as={DebouncedTextField}
|
|
179
|
-
label={"
|
|
182
|
+
label={t("path")}
|
|
180
183
|
required
|
|
181
184
|
error={showErrors && Boolean(errors.path)} />
|
|
182
185
|
|
|
183
186
|
<FieldCaption error={touched.path && Boolean(errors.path)}>
|
|
184
187
|
{touched.path && Boolean(errors.path)
|
|
185
188
|
? errors.path
|
|
186
|
-
: isSubcollection ? "
|
|
189
|
+
: isSubcollection ? t("relative_path_to_parent") : t("path_in_database")}
|
|
187
190
|
</FieldCaption>
|
|
188
191
|
|
|
189
192
|
</div>
|
|
@@ -246,7 +249,7 @@ export function CollectionDetailsForm({
|
|
|
246
249
|
<Select
|
|
247
250
|
key={`order-select-${numberProperties.length}`}
|
|
248
251
|
name="orderProperty"
|
|
249
|
-
label="
|
|
252
|
+
label={t("order_property")}
|
|
250
253
|
size={"large"}
|
|
251
254
|
fullWidth={true}
|
|
252
255
|
position={"item-aligned"}
|
|
@@ -258,10 +261,10 @@ export function CollectionDetailsForm({
|
|
|
258
261
|
}}
|
|
259
262
|
renderValue={(value) => {
|
|
260
263
|
if (orderPropertyMissing) {
|
|
261
|
-
return <span className="text-red-500">{value} (
|
|
264
|
+
return <span className="text-red-500">{value} ({t("not_found_suffix")})</span>;
|
|
262
265
|
}
|
|
263
266
|
const prop = numberProperties.find(p => p.key === value);
|
|
264
|
-
if (!prop) return "
|
|
267
|
+
if (!prop) return t("select_a_property");
|
|
265
268
|
const fieldConfig = getFieldConfig(prop.property, customizationController.propertyConfigs);
|
|
266
269
|
return (
|
|
267
270
|
<div className="flex items-center gap-2">
|
|
@@ -291,7 +294,7 @@ export function CollectionDetailsForm({
|
|
|
291
294
|
<div>
|
|
292
295
|
<div>{prop.label}</div>
|
|
293
296
|
<Typography variant="caption" color="secondary">
|
|
294
|
-
{fieldConfig?.name || "
|
|
297
|
+
{fieldConfig?.name || t("number")}
|
|
295
298
|
</Typography>
|
|
296
299
|
</div>
|
|
297
300
|
</div>
|
|
@@ -301,10 +304,10 @@ export function CollectionDetailsForm({
|
|
|
301
304
|
</Select>
|
|
302
305
|
<FieldCaption error={orderPropertyMissing}>
|
|
303
306
|
{orderPropertyMissing
|
|
304
|
-
?
|
|
307
|
+
? t("order_property_not_found", { property: values.orderProperty ?? "" })
|
|
305
308
|
: numberProperties.length === 0
|
|
306
|
-
? "
|
|
307
|
-
: "
|
|
309
|
+
? t("no_number_properties")
|
|
310
|
+
: t("order_property_description")
|
|
308
311
|
}
|
|
309
312
|
</FieldCaption>
|
|
310
313
|
</>
|
|
@@ -322,7 +325,7 @@ export function CollectionDetailsForm({
|
|
|
322
325
|
: "__order";
|
|
323
326
|
const dialogPropertyName = orderPropertyMissing && values.orderProperty
|
|
324
327
|
? unslugify(values.orderProperty)
|
|
325
|
-
: "
|
|
328
|
+
: t("order_label");
|
|
326
329
|
|
|
327
330
|
if (!showCreateButton) return null;
|
|
328
331
|
|
|
@@ -333,7 +336,7 @@ export function CollectionDetailsForm({
|
|
|
333
336
|
className="ml-3.5 text-sm text-primary hover:text-primary-dark mt-2"
|
|
334
337
|
onClick={() => setOrderPropertyDialogOpen(true)}
|
|
335
338
|
>
|
|
336
|
-
|
|
339
|
+
{t("create_property", { property: dialogPropertyKey })}
|
|
337
340
|
</button>
|
|
338
341
|
<PropertyFormDialog
|
|
339
342
|
open={orderPropertyDialogOpen}
|
|
@@ -375,16 +378,14 @@ export function CollectionDetailsForm({
|
|
|
375
378
|
position={"start"}
|
|
376
379
|
size={"large"}
|
|
377
380
|
allowIndeterminate={true}
|
|
378
|
-
label={values.history === null || values.history === undefined ? "
|
|
379
|
-
values.history ? "
|
|
380
|
-
)}
|
|
381
|
+
label={<span className="flex items-center gap-2"><HistoryIcon size={"smallest"} />{values.history === null || values.history === undefined ? t("doc_history_global") : (
|
|
382
|
+
values.history ? t("doc_history_enabled") : t("doc_history_not_enabled")
|
|
383
|
+
)}</span>}
|
|
381
384
|
onValueChange={(v) => setFieldValue("history", v)}
|
|
382
385
|
value={values.history === undefined ? null : values.history}
|
|
383
386
|
/>
|
|
384
387
|
<FieldCaption>
|
|
385
|
-
|
|
386
|
-
This is useful for auditing purposes. The data is stored in a subcollection of the document
|
|
387
|
-
in your database, called <b>__history</b>.
|
|
388
|
+
{t("doc_history_description")}
|
|
388
389
|
</FieldCaption>
|
|
389
390
|
</div>
|
|
390
391
|
|
|
@@ -423,7 +424,9 @@ function DefaultDatabaseField({
|
|
|
423
424
|
onDatabaseIdUpdate
|
|
424
425
|
}: { databaseId?: string, onDatabaseIdUpdate: (databaseId: string) => void }) {
|
|
425
426
|
|
|
426
|
-
|
|
427
|
+
const { t } = useTranslation();
|
|
428
|
+
|
|
429
|
+
return <Tooltip title={t("database_id")}
|
|
427
430
|
side={"top"}
|
|
428
431
|
align={"start"}>
|
|
429
432
|
<TextField size={"small"}
|
|
@@ -431,6 +434,6 @@ function DefaultDatabaseField({
|
|
|
431
434
|
inputClassName={"text-end"}
|
|
432
435
|
value={databaseId ?? ""}
|
|
433
436
|
onChange={(e: any) => onDatabaseIdUpdate(e.target.value)}
|
|
434
|
-
placeholder={"
|
|
437
|
+
placeholder={t("default_text")}></TextField>
|
|
435
438
|
</Tooltip>
|
|
436
439
|
}
|
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
useCustomizationController,
|
|
23
23
|
useNavigationController,
|
|
24
24
|
User,
|
|
25
|
-
useSnackbarController
|
|
25
|
+
useSnackbarController,
|
|
26
|
+
useTranslation,
|
|
26
27
|
} from "@firecms/core";
|
|
27
28
|
import {
|
|
28
29
|
ArrowBackIcon,
|
|
@@ -106,9 +107,15 @@ export interface CollectionEditorDialogProps {
|
|
|
106
107
|
* The plugin is API-agnostic - the consumer provides the implementation.
|
|
107
108
|
*/
|
|
108
109
|
generateCollection?: CollectionGenerationCallback;
|
|
110
|
+
/**
|
|
111
|
+
* Optional analytics callback
|
|
112
|
+
*/
|
|
113
|
+
onAnalyticsEvent?: (event: string, params?: object) => void;
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
117
|
+
const { t } = useTranslation();
|
|
118
|
+
|
|
112
119
|
|
|
113
120
|
const open = props.open;
|
|
114
121
|
|
|
@@ -139,7 +146,7 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
139
146
|
maxWidth={"7xl"}
|
|
140
147
|
onOpenChange={(open) => !open ? handleCancel() : undefined}
|
|
141
148
|
>
|
|
142
|
-
<DialogTitle hidden>
|
|
149
|
+
<DialogTitle hidden>{t("collection_editor")}</DialogTitle>
|
|
143
150
|
<AIModifiedPathsProvider>
|
|
144
151
|
{open && <CollectionEditor {...props}
|
|
145
152
|
handleCancel={handleCancel}
|
|
@@ -149,7 +156,7 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
149
156
|
open={unsavedChangesDialogOpen}
|
|
150
157
|
handleOk={() => props.handleClose(undefined)}
|
|
151
158
|
handleCancel={() => setUnsavedChangesDialogOpen(false)}
|
|
152
|
-
body={"
|
|
159
|
+
body={t("unsaved_changes_in_collection")} />
|
|
153
160
|
</AIModifiedPathsProvider>
|
|
154
161
|
</Dialog>
|
|
155
162
|
);
|
|
@@ -290,7 +297,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
290
297
|
existingEntities,
|
|
291
298
|
initialView: initialViewProp,
|
|
292
299
|
expandKanban,
|
|
293
|
-
generateCollection
|
|
300
|
+
generateCollection,
|
|
301
|
+
onAnalyticsEvent
|
|
294
302
|
}: CollectionEditorDialogProps & {
|
|
295
303
|
handleCancel: () => void,
|
|
296
304
|
setFormDirty: (dirty: boolean) => void,
|
|
@@ -301,10 +309,11 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
301
309
|
collection: PersistedCollection<M> | undefined,
|
|
302
310
|
setCollection: (collection: PersistedCollection<M>) => void,
|
|
303
311
|
propertyConfigs: Record<string, PropertyConfig<any>>,
|
|
304
|
-
groups: string[],
|
|
312
|
+
groups: (string | null)[],
|
|
305
313
|
}
|
|
306
314
|
) {
|
|
307
315
|
|
|
316
|
+
const { t } = useTranslation();
|
|
308
317
|
const importConfig = useImportConfig();
|
|
309
318
|
const navigation = useNavigationController();
|
|
310
319
|
const snackbarController = useSnackbarController();
|
|
@@ -337,7 +346,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
337
346
|
console.error(e);
|
|
338
347
|
snackbarController.open({
|
|
339
348
|
type: "error",
|
|
340
|
-
message: "
|
|
349
|
+
message: t("error_persisting_collection", { error: e.message ?? t("details_in_console") })
|
|
341
350
|
});
|
|
342
351
|
return false;
|
|
343
352
|
});
|
|
@@ -418,7 +427,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
418
427
|
console.error(e);
|
|
419
428
|
snackbarController.open({
|
|
420
429
|
type: "error",
|
|
421
|
-
message: "
|
|
430
|
+
message: t("error_inferring_collection", { error: e.message ?? t("details_in_console") })
|
|
422
431
|
});
|
|
423
432
|
return newCollection;
|
|
424
433
|
}
|
|
@@ -481,7 +490,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
481
490
|
} catch (e: any) {
|
|
482
491
|
snackbarController.open({
|
|
483
492
|
type: "error",
|
|
484
|
-
message: "
|
|
493
|
+
message: t("error_persisting_collection", { error: e.message ?? t("details_in_console") })
|
|
485
494
|
});
|
|
486
495
|
console.error(e);
|
|
487
496
|
formexController.resetForm({ values: newCollectionState });
|
|
@@ -505,11 +514,11 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
505
514
|
errors = { ...errors, ...propertyErrorsRef.current };
|
|
506
515
|
}
|
|
507
516
|
if (currentView === "general") {
|
|
508
|
-
const pathError = validatePath(col.path, isNewCollection, existingPaths, col.id);
|
|
517
|
+
const pathError = validatePath(t, col.path, isNewCollection, existingPaths, col.id);
|
|
509
518
|
if (pathError) {
|
|
510
519
|
errors.path = pathError;
|
|
511
520
|
}
|
|
512
|
-
const idError = validateId(col.id, isNewCollection, existingPaths, existingIds);
|
|
521
|
+
const idError = validateId(t, col.id, isNewCollection, existingPaths, existingIds);
|
|
513
522
|
if (idError) {
|
|
514
523
|
errors.id = idError;
|
|
515
524
|
}
|
|
@@ -534,7 +543,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
534
543
|
|
|
535
544
|
const path = values.path;
|
|
536
545
|
const updatedFullPath = fullPath?.includes("/") ? fullPath?.split("/").slice(0, -1).join("/") + "/" + path : path; // TODO: this path is wrong
|
|
537
|
-
const pathError = validatePath(path, isNewCollection, existingPaths, values.id);
|
|
546
|
+
const pathError = validatePath(t, path, isNewCollection, existingPaths, values.id);
|
|
538
547
|
|
|
539
548
|
const parentPaths = !pathError && parentCollectionIds ? navigation.convertIdsToPaths(parentCollectionIds) : undefined;
|
|
540
549
|
const resolvedPath = !pathError ? navigation.resolveIdsFrom(updatedFullPath) : undefined;
|
|
@@ -596,7 +605,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
596
605
|
setDeleteRequested(false);
|
|
597
606
|
handleCancel();
|
|
598
607
|
snackbarController.open({
|
|
599
|
-
message: "
|
|
608
|
+
message: t("collection_deleted"),
|
|
600
609
|
type: "success"
|
|
601
610
|
});
|
|
602
611
|
});
|
|
@@ -630,21 +639,22 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
630
639
|
existingCollection={values}
|
|
631
640
|
onGenerated={handleAIGenerated}
|
|
632
641
|
generateCollection={generateCollection}
|
|
642
|
+
onAnalyticsEvent={onAnalyticsEvent}
|
|
633
643
|
/>
|
|
634
644
|
)}
|
|
635
645
|
<Tabs value={currentView}
|
|
636
646
|
onValueChange={(v) => setCurrentView(v as EditorView)}>
|
|
637
647
|
<Tab value={"general"}>
|
|
638
|
-
|
|
648
|
+
{t("tab_general")}
|
|
639
649
|
</Tab>
|
|
640
650
|
<Tab value={"display"}>
|
|
641
|
-
|
|
651
|
+
{t("tab_display")}
|
|
642
652
|
</Tab>
|
|
643
653
|
<Tab value={"properties"}>
|
|
644
|
-
|
|
654
|
+
{t("tab_properties")}
|
|
645
655
|
</Tab>
|
|
646
656
|
<Tab value={"extend"}>
|
|
647
|
-
|
|
657
|
+
{t("tab_extend")}
|
|
648
658
|
</Tab>
|
|
649
659
|
</Tabs>
|
|
650
660
|
</div>}
|
|
@@ -669,7 +679,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
669
679
|
onContinue={onWelcomeScreenContinue}
|
|
670
680
|
existingCollectionPaths={existingPaths}
|
|
671
681
|
parentCollection={parentCollection}
|
|
672
|
-
generateCollection={generateCollection}
|
|
682
|
+
generateCollection={generateCollection}
|
|
683
|
+
onAnalyticsEvent={onAnalyticsEvent} />}
|
|
673
684
|
|
|
674
685
|
{currentView === "import_data_mapping" && importConfig &&
|
|
675
686
|
<CollectionEditorImportMapping importConfig={importConfig}
|
|
@@ -688,7 +699,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
688
699
|
onImportSuccess={async (importedCollection) => {
|
|
689
700
|
snackbarController.open({
|
|
690
701
|
type: "info",
|
|
691
|
-
message: "
|
|
702
|
+
message: t("data_imported_successfully_msg")
|
|
692
703
|
});
|
|
693
704
|
await saveCollection(values);
|
|
694
705
|
handleClose(importedCollection);
|
|
@@ -756,7 +767,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
756
767
|
importConfig.setInUse(false);
|
|
757
768
|
return setCurrentView("welcome");
|
|
758
769
|
}}>
|
|
759
|
-
|
|
770
|
+
{t("back")}
|
|
760
771
|
</Button>}
|
|
761
772
|
|
|
762
773
|
{isNewCollection && includeTemplates && currentView === "import_data_preview" &&
|
|
@@ -765,14 +776,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
765
776
|
onClick={() => {
|
|
766
777
|
setCurrentView("import_data_mapping");
|
|
767
778
|
}}>
|
|
768
|
-
|
|
779
|
+
{t("back")}
|
|
769
780
|
</Button>}
|
|
770
781
|
|
|
771
782
|
{isNewCollection && includeTemplates && currentView === "general" &&
|
|
772
783
|
<Button variant={"text"}
|
|
773
784
|
type="button"
|
|
774
785
|
onClick={() => setCurrentView("welcome")}>
|
|
775
|
-
|
|
786
|
+
{t("back")}
|
|
776
787
|
</Button>}
|
|
777
788
|
|
|
778
789
|
{isNewCollection && currentView === "properties" && <Button variant={"text"}
|
|
@@ -780,7 +791,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
780
791
|
color={"neutral"}
|
|
781
792
|
onClick={() => setCurrentView("general")}>
|
|
782
793
|
<ArrowBackIcon />
|
|
783
|
-
|
|
794
|
+
{t("back")}
|
|
784
795
|
</Button>}
|
|
785
796
|
|
|
786
797
|
<Button variant={"text"}
|
|
@@ -788,12 +799,12 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
788
799
|
onClick={() => {
|
|
789
800
|
handleCancel();
|
|
790
801
|
}}>
|
|
791
|
-
|
|
802
|
+
{t("cancel")}
|
|
792
803
|
</Button>
|
|
793
804
|
|
|
794
805
|
{currentView === "welcome" &&
|
|
795
806
|
<Button variant={"text"} onClick={() => onWelcomeScreenContinue()}>
|
|
796
|
-
|
|
807
|
+
{t("continue_from_scratch")}
|
|
797
808
|
</Button>}
|
|
798
809
|
|
|
799
810
|
{isNewCollection && currentView === "import_data_mapping" &&
|
|
@@ -802,7 +813,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
802
813
|
color="primary"
|
|
803
814
|
onClick={onImportMappingComplete}
|
|
804
815
|
>
|
|
805
|
-
|
|
816
|
+
{t("next")}
|
|
806
817
|
</Button>}
|
|
807
818
|
|
|
808
819
|
{isNewCollection && currentView === "import_data_preview" &&
|
|
@@ -813,7 +824,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
813
824
|
setNextMode();
|
|
814
825
|
}}
|
|
815
826
|
>
|
|
816
|
-
|
|
827
|
+
{t("next")}
|
|
817
828
|
</Button>}
|
|
818
829
|
|
|
819
830
|
{isNewCollection && (currentView === "general" || currentView === "properties") &&
|
|
@@ -827,8 +838,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
827
838
|
? <CheckIcon />
|
|
828
839
|
: undefined}
|
|
829
840
|
>
|
|
830
|
-
{currentView === "general" && "
|
|
831
|
-
{currentView === "properties" && "
|
|
841
|
+
{currentView === "general" && t("next")}
|
|
842
|
+
{currentView === "properties" && t("create_collection")}
|
|
832
843
|
</LoadingButton>}
|
|
833
844
|
|
|
834
845
|
{!isNewCollection && <LoadingButton
|
|
@@ -837,7 +848,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
837
848
|
type="submit"
|
|
838
849
|
loading={isSubmitting}
|
|
839
850
|
>
|
|
840
|
-
|
|
851
|
+
{t("update_collection")}
|
|
841
852
|
</LoadingButton>}
|
|
842
853
|
|
|
843
854
|
</DialogActions>
|
|
@@ -850,10 +861,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
850
861
|
open={deleteRequested}
|
|
851
862
|
onAccept={deleteCollection}
|
|
852
863
|
onCancel={() => setDeleteRequested(false)}
|
|
853
|
-
title={<>
|
|
854
|
-
body={<>
|
|
855
|
-
delete any data</b>, only
|
|
856
|
-
the stored config, and reset to the code state.</>} />
|
|
864
|
+
title={<>{t("delete_stored_config")}</>}
|
|
865
|
+
body={<>{t("delete_stored_config_body")}</>} />
|
|
857
866
|
|
|
858
867
|
</DialogContent>
|
|
859
868
|
|
|
@@ -867,7 +876,9 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
|
|
|
867
876
|
const propertiesResult: PropertiesOrBuilders<any> = {};
|
|
868
877
|
if (properties) {
|
|
869
878
|
Object.keys(properties).forEach((key) => {
|
|
870
|
-
|
|
879
|
+
const prop = properties[key];
|
|
880
|
+
if (prop == null) return;
|
|
881
|
+
propertiesResult[key] = applyPropertiesConfig(prop as PropertyOrBuilder, propertyConfigs);
|
|
871
882
|
});
|
|
872
883
|
}
|
|
873
884
|
|
|
@@ -879,7 +890,7 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
|
|
|
879
890
|
|
|
880
891
|
function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
|
|
881
892
|
let internalProperty = property;
|
|
882
|
-
if (propertyConfigs && typeof internalProperty === "object" && internalProperty.propertyConfig) {
|
|
893
|
+
if (propertyConfigs && internalProperty && typeof internalProperty === "object" && internalProperty.propertyConfig) {
|
|
883
894
|
const propertyConfig = propertyConfigs[internalProperty.propertyConfig];
|
|
884
895
|
if (propertyConfig && isPropertyBuilder(propertyConfig.property)) {
|
|
885
896
|
internalProperty = propertyConfig.property;
|
|
@@ -906,30 +917,32 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
|
|
|
906
917
|
|
|
907
918
|
}
|
|
908
919
|
|
|
909
|
-
|
|
920
|
+
type TranslateFn = (key: string, params?: Record<string, string>) => string;
|
|
921
|
+
|
|
922
|
+
const validatePath = (t: TranslateFn, value: string, isNewCollection: boolean, existingPaths: string[], idValue?: string) => {
|
|
910
923
|
let error;
|
|
911
924
|
if (!value) {
|
|
912
|
-
error = "
|
|
925
|
+
error = t("must_specify_path");
|
|
913
926
|
}
|
|
914
927
|
// if (isNewCollection && existingIds?.includes(value.trim().toLowerCase()))
|
|
915
928
|
// error = "There is already a collection which uses this path as an id";
|
|
916
929
|
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()) && !idValue)
|
|
917
|
-
error = "
|
|
930
|
+
error = t("collection_path_already_exists");
|
|
918
931
|
|
|
919
932
|
const subpaths = removeInitialAndTrailingSlashes(value).split("/");
|
|
920
933
|
if (subpaths.length % 2 === 0) {
|
|
921
|
-
error =
|
|
934
|
+
error = t("collection_path_odd_segments", { path: value });
|
|
922
935
|
}
|
|
923
936
|
return error;
|
|
924
937
|
};
|
|
925
938
|
|
|
926
|
-
const validateId = (value: string, isNewCollection: boolean, existingPaths: string[], existingIds: string[]) => {
|
|
939
|
+
const validateId = (t: TranslateFn, value: string, isNewCollection: boolean, existingPaths: string[], existingIds: string[]) => {
|
|
927
940
|
if (!value) return undefined;
|
|
928
941
|
let error;
|
|
929
942
|
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()))
|
|
930
|
-
error = "
|
|
943
|
+
error = t("collection_uses_path_as_id");
|
|
931
944
|
if (isNewCollection && existingIds?.includes(value.trim().toLowerCase()))
|
|
932
|
-
error = "
|
|
945
|
+
error = t("collection_uses_this_id");
|
|
933
946
|
// if (error) {
|
|
934
947
|
// setAdvancedPanelExpanded(true);
|
|
935
948
|
// }
|