@firecms/collection_editor 3.1.0 → 3.2.0-canary.44dc65b
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 +6887 -3709
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +6885 -3707
- 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/package.json +8 -8
- 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 +11 -10
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +26 -24
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +42 -38
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +19 -18
- 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 +36 -38
- 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 +74 -65
- 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,
|
|
@@ -69,6 +69,7 @@ export function AICollectionGeneratorPopover({
|
|
|
69
69
|
const [loading, setLoading] = useState(false);
|
|
70
70
|
const [error, setError] = useState<string | null>(null);
|
|
71
71
|
|
|
72
|
+
const { t } = useTranslation();
|
|
72
73
|
const navigation = useNavigationController();
|
|
73
74
|
const snackbarController = useSnackbarController();
|
|
74
75
|
|
|
@@ -155,13 +156,13 @@ export function AICollectionGeneratorPopover({
|
|
|
155
156
|
: <AIIcon size="small" />
|
|
156
157
|
}
|
|
157
158
|
>
|
|
158
|
-
|
|
159
|
+
{t("ai_assist")}
|
|
159
160
|
</Button>
|
|
160
161
|
) : (
|
|
161
162
|
<IconButton
|
|
162
163
|
size={size}
|
|
163
164
|
disabled={loading}
|
|
164
|
-
aria-label="
|
|
165
|
+
aria-label={t("ai_assist")}
|
|
165
166
|
>
|
|
166
167
|
{loading
|
|
167
168
|
? <CircularProgress size="smallest" />
|
|
@@ -185,14 +186,14 @@ export function AICollectionGeneratorPopover({
|
|
|
185
186
|
<div className="flex items-center gap-2">
|
|
186
187
|
<AIIcon size="small" />
|
|
187
188
|
<Typography variant="subtitle2">
|
|
188
|
-
{existingCollection ? "
|
|
189
|
+
{existingCollection ? t("modify_collection_with_ai") : t("generate_collection_with_ai")}
|
|
189
190
|
</Typography>
|
|
190
191
|
</div>
|
|
191
192
|
|
|
192
193
|
<Typography variant="caption" color="secondary">
|
|
193
194
|
{existingCollection
|
|
194
|
-
? "
|
|
195
|
-
: "
|
|
195
|
+
? t("describe_changes_to_make")
|
|
196
|
+
: t("describe_collection_to_create")
|
|
196
197
|
}
|
|
197
198
|
</Typography>
|
|
198
199
|
|
|
@@ -205,8 +206,8 @@ export function AICollectionGeneratorPopover({
|
|
|
205
206
|
onChange={(e) => setPrompt(e.target.value)}
|
|
206
207
|
onKeyDown={handleKeyDown}
|
|
207
208
|
placeholder={existingCollection
|
|
208
|
-
? "
|
|
209
|
-
: "
|
|
209
|
+
? t("ai_placeholder_modify")
|
|
210
|
+
: t("ai_placeholder_create")
|
|
210
211
|
}
|
|
211
212
|
disabled={loading}
|
|
212
213
|
/>
|
|
@@ -224,7 +225,7 @@ export function AICollectionGeneratorPopover({
|
|
|
224
225
|
onClick={() => setMenuOpen(false)}
|
|
225
226
|
disabled={loading}
|
|
226
227
|
>
|
|
227
|
-
|
|
228
|
+
{t("cancel")}
|
|
228
229
|
</Button>
|
|
229
230
|
<Button
|
|
230
231
|
variant="filled"
|
|
@@ -233,7 +234,7 @@ export function AICollectionGeneratorPopover({
|
|
|
233
234
|
disabled={!prompt.trim() || loading}
|
|
234
235
|
startIcon={loading ? <CircularProgress size="smallest" /> : undefined}
|
|
235
236
|
>
|
|
236
|
-
{loading ? "
|
|
237
|
+
{loading ? t("generating") : t("generate_with_ai")}
|
|
237
238
|
</Button>
|
|
238
239
|
</div>
|
|
239
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,
|
|
@@ -61,6 +61,8 @@ export function CollectionDetailsForm({
|
|
|
61
61
|
const [iconDialogOpen, setIconDialogOpen] = useState(false);
|
|
62
62
|
const [orderPropertyDialogOpen, setOrderPropertyDialogOpen] = useState(false);
|
|
63
63
|
|
|
64
|
+
const { t } = useTranslation();
|
|
65
|
+
|
|
64
66
|
const authController = useAuthController();
|
|
65
67
|
const customizationController = useCustomizationController();
|
|
66
68
|
|
|
@@ -137,12 +139,12 @@ export function CollectionDetailsForm({
|
|
|
137
139
|
<div
|
|
138
140
|
className="flex flex-row gap-2 py-2 pt-3 items-center">
|
|
139
141
|
<Typography variant={!isNewCollection ? "h5" : "h4"} className={"flex-grow"}>
|
|
140
|
-
{isNewCollection ? "
|
|
142
|
+
{isNewCollection ? t("new_collection") : t("collection_with_name", { name: values?.name || "" })}
|
|
141
143
|
</Typography>
|
|
142
144
|
<DefaultDatabaseField databaseId={values.databaseId}
|
|
143
145
|
onDatabaseIdUpdate={updateDatabaseId} />
|
|
144
146
|
|
|
145
|
-
<Tooltip title={"
|
|
147
|
+
<Tooltip title={t("change_icon")}
|
|
146
148
|
asChild={true}>
|
|
147
149
|
<IconButton
|
|
148
150
|
shape={"square"}
|
|
@@ -154,7 +156,7 @@ export function CollectionDetailsForm({
|
|
|
154
156
|
|
|
155
157
|
{parentCollection && <Chip colorScheme={"tealDarker"}>
|
|
156
158
|
<Typography variant={"caption"}>
|
|
157
|
-
|
|
159
|
+
{t("is_subcollection_of")} <b>{parentCollection.name}</b>
|
|
158
160
|
</Typography>
|
|
159
161
|
</Chip>}
|
|
160
162
|
|
|
@@ -165,26 +167,26 @@ export function CollectionDetailsForm({
|
|
|
165
167
|
<TextField
|
|
166
168
|
value={values.name ?? ""}
|
|
167
169
|
onChange={(e: any) => updateName(e.target.value)}
|
|
168
|
-
label={"
|
|
170
|
+
label={t("name")}
|
|
169
171
|
autoFocus={true}
|
|
170
172
|
required
|
|
171
173
|
error={showErrors && Boolean(errors.name)} />
|
|
172
174
|
<FieldCaption error={touched.name && Boolean(errors.name)}>
|
|
173
|
-
{touched.name && Boolean(errors.name) ? errors.name : "
|
|
175
|
+
{touched.name && Boolean(errors.name) ? errors.name : t("collection_name_description")}
|
|
174
176
|
</FieldCaption>
|
|
175
177
|
</div>
|
|
176
178
|
|
|
177
179
|
<div className={cls("col-span-12 ")}>
|
|
178
180
|
<Field name={"path"}
|
|
179
181
|
as={DebouncedTextField}
|
|
180
|
-
label={"
|
|
182
|
+
label={t("path")}
|
|
181
183
|
required
|
|
182
184
|
error={showErrors && Boolean(errors.path)} />
|
|
183
185
|
|
|
184
186
|
<FieldCaption error={touched.path && Boolean(errors.path)}>
|
|
185
187
|
{touched.path && Boolean(errors.path)
|
|
186
188
|
? errors.path
|
|
187
|
-
: isSubcollection ? "
|
|
189
|
+
: isSubcollection ? t("relative_path_to_parent") : t("path_in_database")}
|
|
188
190
|
</FieldCaption>
|
|
189
191
|
|
|
190
192
|
</div>
|
|
@@ -247,7 +249,7 @@ export function CollectionDetailsForm({
|
|
|
247
249
|
<Select
|
|
248
250
|
key={`order-select-${numberProperties.length}`}
|
|
249
251
|
name="orderProperty"
|
|
250
|
-
label="
|
|
252
|
+
label={t("order_property")}
|
|
251
253
|
size={"large"}
|
|
252
254
|
fullWidth={true}
|
|
253
255
|
position={"item-aligned"}
|
|
@@ -259,10 +261,10 @@ export function CollectionDetailsForm({
|
|
|
259
261
|
}}
|
|
260
262
|
renderValue={(value) => {
|
|
261
263
|
if (orderPropertyMissing) {
|
|
262
|
-
return <span className="text-red-500">{value} (
|
|
264
|
+
return <span className="text-red-500">{value} ({t("not_found_suffix")})</span>;
|
|
263
265
|
}
|
|
264
266
|
const prop = numberProperties.find(p => p.key === value);
|
|
265
|
-
if (!prop) return "
|
|
267
|
+
if (!prop) return t("select_a_property");
|
|
266
268
|
const fieldConfig = getFieldConfig(prop.property, customizationController.propertyConfigs);
|
|
267
269
|
return (
|
|
268
270
|
<div className="flex items-center gap-2">
|
|
@@ -292,7 +294,7 @@ export function CollectionDetailsForm({
|
|
|
292
294
|
<div>
|
|
293
295
|
<div>{prop.label}</div>
|
|
294
296
|
<Typography variant="caption" color="secondary">
|
|
295
|
-
{fieldConfig?.name || "
|
|
297
|
+
{fieldConfig?.name || t("number")}
|
|
296
298
|
</Typography>
|
|
297
299
|
</div>
|
|
298
300
|
</div>
|
|
@@ -302,10 +304,10 @@ export function CollectionDetailsForm({
|
|
|
302
304
|
</Select>
|
|
303
305
|
<FieldCaption error={orderPropertyMissing}>
|
|
304
306
|
{orderPropertyMissing
|
|
305
|
-
?
|
|
307
|
+
? t("order_property_not_found", { property: values.orderProperty ?? "" })
|
|
306
308
|
: numberProperties.length === 0
|
|
307
|
-
? "
|
|
308
|
-
: "
|
|
309
|
+
? t("no_number_properties")
|
|
310
|
+
: t("order_property_description")
|
|
309
311
|
}
|
|
310
312
|
</FieldCaption>
|
|
311
313
|
</>
|
|
@@ -323,7 +325,7 @@ export function CollectionDetailsForm({
|
|
|
323
325
|
: "__order";
|
|
324
326
|
const dialogPropertyName = orderPropertyMissing && values.orderProperty
|
|
325
327
|
? unslugify(values.orderProperty)
|
|
326
|
-
: "
|
|
328
|
+
: t("order_label");
|
|
327
329
|
|
|
328
330
|
if (!showCreateButton) return null;
|
|
329
331
|
|
|
@@ -334,7 +336,7 @@ export function CollectionDetailsForm({
|
|
|
334
336
|
className="ml-3.5 text-sm text-primary hover:text-primary-dark mt-2"
|
|
335
337
|
onClick={() => setOrderPropertyDialogOpen(true)}
|
|
336
338
|
>
|
|
337
|
-
|
|
339
|
+
{t("create_property", { property: dialogPropertyKey })}
|
|
338
340
|
</button>
|
|
339
341
|
<PropertyFormDialog
|
|
340
342
|
open={orderPropertyDialogOpen}
|
|
@@ -376,16 +378,14 @@ export function CollectionDetailsForm({
|
|
|
376
378
|
position={"start"}
|
|
377
379
|
size={"large"}
|
|
378
380
|
allowIndeterminate={true}
|
|
379
|
-
label={<span className="flex items-center gap-2"><HistoryIcon size={"smallest"} />{values.history === null || values.history === undefined ? "
|
|
380
|
-
values.history ? "
|
|
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")
|
|
381
383
|
)}</span>}
|
|
382
384
|
onValueChange={(v) => setFieldValue("history", v)}
|
|
383
385
|
value={values.history === undefined ? null : values.history}
|
|
384
386
|
/>
|
|
385
387
|
<FieldCaption>
|
|
386
|
-
|
|
387
|
-
This is useful for auditing purposes. The data is stored in a subcollection of the document
|
|
388
|
-
in your database, called <b>__history</b>.
|
|
388
|
+
{t("doc_history_description")}
|
|
389
389
|
</FieldCaption>
|
|
390
390
|
</div>
|
|
391
391
|
|
|
@@ -424,7 +424,9 @@ function DefaultDatabaseField({
|
|
|
424
424
|
onDatabaseIdUpdate
|
|
425
425
|
}: { databaseId?: string, onDatabaseIdUpdate: (databaseId: string) => void }) {
|
|
426
426
|
|
|
427
|
-
|
|
427
|
+
const { t } = useTranslation();
|
|
428
|
+
|
|
429
|
+
return <Tooltip title={t("database_id")}
|
|
428
430
|
side={"top"}
|
|
429
431
|
align={"start"}>
|
|
430
432
|
<TextField size={"small"}
|
|
@@ -432,6 +434,6 @@ function DefaultDatabaseField({
|
|
|
432
434
|
inputClassName={"text-end"}
|
|
433
435
|
value={databaseId ?? ""}
|
|
434
436
|
onChange={(e: any) => onDatabaseIdUpdate(e.target.value)}
|
|
435
|
-
placeholder={"
|
|
437
|
+
placeholder={t("default_text")}></TextField>
|
|
436
438
|
</Tooltip>
|
|
437
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,
|
|
@@ -113,6 +114,8 @@ export interface CollectionEditorDialogProps {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
117
|
+
const { t } = useTranslation();
|
|
118
|
+
|
|
116
119
|
|
|
117
120
|
const open = props.open;
|
|
118
121
|
|
|
@@ -143,7 +146,7 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
143
146
|
maxWidth={"7xl"}
|
|
144
147
|
onOpenChange={(open) => !open ? handleCancel() : undefined}
|
|
145
148
|
>
|
|
146
|
-
<DialogTitle hidden>
|
|
149
|
+
<DialogTitle hidden>{t("collection_editor")}</DialogTitle>
|
|
147
150
|
<AIModifiedPathsProvider>
|
|
148
151
|
{open && <CollectionEditor {...props}
|
|
149
152
|
handleCancel={handleCancel}
|
|
@@ -153,7 +156,7 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
153
156
|
open={unsavedChangesDialogOpen}
|
|
154
157
|
handleOk={() => props.handleClose(undefined)}
|
|
155
158
|
handleCancel={() => setUnsavedChangesDialogOpen(false)}
|
|
156
|
-
body={"
|
|
159
|
+
body={t("unsaved_changes_in_collection")} />
|
|
157
160
|
</AIModifiedPathsProvider>
|
|
158
161
|
</Dialog>
|
|
159
162
|
);
|
|
@@ -306,10 +309,11 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
306
309
|
collection: PersistedCollection<M> | undefined,
|
|
307
310
|
setCollection: (collection: PersistedCollection<M>) => void,
|
|
308
311
|
propertyConfigs: Record<string, PropertyConfig<any>>,
|
|
309
|
-
groups: string[],
|
|
312
|
+
groups: (string | null)[],
|
|
310
313
|
}
|
|
311
314
|
) {
|
|
312
315
|
|
|
316
|
+
const { t } = useTranslation();
|
|
313
317
|
const importConfig = useImportConfig();
|
|
314
318
|
const navigation = useNavigationController();
|
|
315
319
|
const snackbarController = useSnackbarController();
|
|
@@ -342,7 +346,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
342
346
|
console.error(e);
|
|
343
347
|
snackbarController.open({
|
|
344
348
|
type: "error",
|
|
345
|
-
message: "
|
|
349
|
+
message: t("error_persisting_collection", { error: e.message ?? t("details_in_console") })
|
|
346
350
|
});
|
|
347
351
|
return false;
|
|
348
352
|
});
|
|
@@ -423,7 +427,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
423
427
|
console.error(e);
|
|
424
428
|
snackbarController.open({
|
|
425
429
|
type: "error",
|
|
426
|
-
message: "
|
|
430
|
+
message: t("error_inferring_collection", { error: e.message ?? t("details_in_console") })
|
|
427
431
|
});
|
|
428
432
|
return newCollection;
|
|
429
433
|
}
|
|
@@ -486,7 +490,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
486
490
|
} catch (e: any) {
|
|
487
491
|
snackbarController.open({
|
|
488
492
|
type: "error",
|
|
489
|
-
message: "
|
|
493
|
+
message: t("error_persisting_collection", { error: e.message ?? t("details_in_console") })
|
|
490
494
|
});
|
|
491
495
|
console.error(e);
|
|
492
496
|
formexController.resetForm({ values: newCollectionState });
|
|
@@ -510,11 +514,11 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
510
514
|
errors = { ...errors, ...propertyErrorsRef.current };
|
|
511
515
|
}
|
|
512
516
|
if (currentView === "general") {
|
|
513
|
-
const pathError = validatePath(col.path, isNewCollection, existingPaths, col.id);
|
|
517
|
+
const pathError = validatePath(t, col.path, isNewCollection, existingPaths, col.id);
|
|
514
518
|
if (pathError) {
|
|
515
519
|
errors.path = pathError;
|
|
516
520
|
}
|
|
517
|
-
const idError = validateId(col.id, isNewCollection, existingPaths, existingIds);
|
|
521
|
+
const idError = validateId(t, col.id, isNewCollection, existingPaths, existingIds);
|
|
518
522
|
if (idError) {
|
|
519
523
|
errors.id = idError;
|
|
520
524
|
}
|
|
@@ -539,7 +543,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
539
543
|
|
|
540
544
|
const path = values.path;
|
|
541
545
|
const updatedFullPath = fullPath?.includes("/") ? fullPath?.split("/").slice(0, -1).join("/") + "/" + path : path; // TODO: this path is wrong
|
|
542
|
-
const pathError = validatePath(path, isNewCollection, existingPaths, values.id);
|
|
546
|
+
const pathError = validatePath(t, path, isNewCollection, existingPaths, values.id);
|
|
543
547
|
|
|
544
548
|
const parentPaths = !pathError && parentCollectionIds ? navigation.convertIdsToPaths(parentCollectionIds) : undefined;
|
|
545
549
|
const resolvedPath = !pathError ? navigation.resolveIdsFrom(updatedFullPath) : undefined;
|
|
@@ -601,7 +605,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
601
605
|
setDeleteRequested(false);
|
|
602
606
|
handleCancel();
|
|
603
607
|
snackbarController.open({
|
|
604
|
-
message: "
|
|
608
|
+
message: t("collection_deleted"),
|
|
605
609
|
type: "success"
|
|
606
610
|
});
|
|
607
611
|
});
|
|
@@ -641,16 +645,16 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
641
645
|
<Tabs value={currentView}
|
|
642
646
|
onValueChange={(v) => setCurrentView(v as EditorView)}>
|
|
643
647
|
<Tab value={"general"}>
|
|
644
|
-
|
|
648
|
+
{t("tab_general")}
|
|
645
649
|
</Tab>
|
|
646
650
|
<Tab value={"display"}>
|
|
647
|
-
|
|
651
|
+
{t("tab_display")}
|
|
648
652
|
</Tab>
|
|
649
653
|
<Tab value={"properties"}>
|
|
650
|
-
|
|
654
|
+
{t("tab_properties")}
|
|
651
655
|
</Tab>
|
|
652
656
|
<Tab value={"extend"}>
|
|
653
|
-
|
|
657
|
+
{t("tab_extend")}
|
|
654
658
|
</Tab>
|
|
655
659
|
</Tabs>
|
|
656
660
|
</div>}
|
|
@@ -695,7 +699,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
695
699
|
onImportSuccess={async (importedCollection) => {
|
|
696
700
|
snackbarController.open({
|
|
697
701
|
type: "info",
|
|
698
|
-
message: "
|
|
702
|
+
message: t("data_imported_successfully_msg")
|
|
699
703
|
});
|
|
700
704
|
await saveCollection(values);
|
|
701
705
|
handleClose(importedCollection);
|
|
@@ -763,7 +767,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
763
767
|
importConfig.setInUse(false);
|
|
764
768
|
return setCurrentView("welcome");
|
|
765
769
|
}}>
|
|
766
|
-
|
|
770
|
+
{t("back")}
|
|
767
771
|
</Button>}
|
|
768
772
|
|
|
769
773
|
{isNewCollection && includeTemplates && currentView === "import_data_preview" &&
|
|
@@ -772,14 +776,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
772
776
|
onClick={() => {
|
|
773
777
|
setCurrentView("import_data_mapping");
|
|
774
778
|
}}>
|
|
775
|
-
|
|
779
|
+
{t("back")}
|
|
776
780
|
</Button>}
|
|
777
781
|
|
|
778
782
|
{isNewCollection && includeTemplates && currentView === "general" &&
|
|
779
783
|
<Button variant={"text"}
|
|
780
784
|
type="button"
|
|
781
785
|
onClick={() => setCurrentView("welcome")}>
|
|
782
|
-
|
|
786
|
+
{t("back")}
|
|
783
787
|
</Button>}
|
|
784
788
|
|
|
785
789
|
{isNewCollection && currentView === "properties" && <Button variant={"text"}
|
|
@@ -787,7 +791,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
787
791
|
color={"neutral"}
|
|
788
792
|
onClick={() => setCurrentView("general")}>
|
|
789
793
|
<ArrowBackIcon />
|
|
790
|
-
|
|
794
|
+
{t("back")}
|
|
791
795
|
</Button>}
|
|
792
796
|
|
|
793
797
|
<Button variant={"text"}
|
|
@@ -795,12 +799,12 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
795
799
|
onClick={() => {
|
|
796
800
|
handleCancel();
|
|
797
801
|
}}>
|
|
798
|
-
|
|
802
|
+
{t("cancel")}
|
|
799
803
|
</Button>
|
|
800
804
|
|
|
801
805
|
{currentView === "welcome" &&
|
|
802
806
|
<Button variant={"text"} onClick={() => onWelcomeScreenContinue()}>
|
|
803
|
-
|
|
807
|
+
{t("continue_from_scratch")}
|
|
804
808
|
</Button>}
|
|
805
809
|
|
|
806
810
|
{isNewCollection && currentView === "import_data_mapping" &&
|
|
@@ -809,7 +813,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
809
813
|
color="primary"
|
|
810
814
|
onClick={onImportMappingComplete}
|
|
811
815
|
>
|
|
812
|
-
|
|
816
|
+
{t("next")}
|
|
813
817
|
</Button>}
|
|
814
818
|
|
|
815
819
|
{isNewCollection && currentView === "import_data_preview" &&
|
|
@@ -820,7 +824,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
820
824
|
setNextMode();
|
|
821
825
|
}}
|
|
822
826
|
>
|
|
823
|
-
|
|
827
|
+
{t("next")}
|
|
824
828
|
</Button>}
|
|
825
829
|
|
|
826
830
|
{isNewCollection && (currentView === "general" || currentView === "properties") &&
|
|
@@ -834,8 +838,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
834
838
|
? <CheckIcon />
|
|
835
839
|
: undefined}
|
|
836
840
|
>
|
|
837
|
-
{currentView === "general" && "
|
|
838
|
-
{currentView === "properties" && "
|
|
841
|
+
{currentView === "general" && t("next")}
|
|
842
|
+
{currentView === "properties" && t("create_collection")}
|
|
839
843
|
</LoadingButton>}
|
|
840
844
|
|
|
841
845
|
{!isNewCollection && <LoadingButton
|
|
@@ -844,7 +848,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
844
848
|
type="submit"
|
|
845
849
|
loading={isSubmitting}
|
|
846
850
|
>
|
|
847
|
-
|
|
851
|
+
{t("update_collection")}
|
|
848
852
|
</LoadingButton>}
|
|
849
853
|
|
|
850
854
|
</DialogActions>
|
|
@@ -857,10 +861,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
857
861
|
open={deleteRequested}
|
|
858
862
|
onAccept={deleteCollection}
|
|
859
863
|
onCancel={() => setDeleteRequested(false)}
|
|
860
|
-
title={<>
|
|
861
|
-
body={<>
|
|
862
|
-
delete any data</b>, only
|
|
863
|
-
the stored config, and reset to the code state.</>} />
|
|
864
|
+
title={<>{t("delete_stored_config")}</>}
|
|
865
|
+
body={<>{t("delete_stored_config_body")}</>} />
|
|
864
866
|
|
|
865
867
|
</DialogContent>
|
|
866
868
|
|
|
@@ -915,30 +917,32 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
|
|
|
915
917
|
|
|
916
918
|
}
|
|
917
919
|
|
|
918
|
-
|
|
920
|
+
type TranslateFn = (key: string, params?: Record<string, string>) => string;
|
|
921
|
+
|
|
922
|
+
const validatePath = (t: TranslateFn, value: string, isNewCollection: boolean, existingPaths: string[], idValue?: string) => {
|
|
919
923
|
let error;
|
|
920
924
|
if (!value) {
|
|
921
|
-
error = "
|
|
925
|
+
error = t("must_specify_path");
|
|
922
926
|
}
|
|
923
927
|
// if (isNewCollection && existingIds?.includes(value.trim().toLowerCase()))
|
|
924
928
|
// error = "There is already a collection which uses this path as an id";
|
|
925
929
|
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()) && !idValue)
|
|
926
|
-
error = "
|
|
930
|
+
error = t("collection_path_already_exists");
|
|
927
931
|
|
|
928
932
|
const subpaths = removeInitialAndTrailingSlashes(value).split("/");
|
|
929
933
|
if (subpaths.length % 2 === 0) {
|
|
930
|
-
error =
|
|
934
|
+
error = t("collection_path_odd_segments", { path: value });
|
|
931
935
|
}
|
|
932
936
|
return error;
|
|
933
937
|
};
|
|
934
938
|
|
|
935
|
-
const validateId = (value: string, isNewCollection: boolean, existingPaths: string[], existingIds: string[]) => {
|
|
939
|
+
const validateId = (t: TranslateFn, value: string, isNewCollection: boolean, existingPaths: string[], existingIds: string[]) => {
|
|
936
940
|
if (!value) return undefined;
|
|
937
941
|
let error;
|
|
938
942
|
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()))
|
|
939
|
-
error = "
|
|
943
|
+
error = t("collection_uses_path_as_id");
|
|
940
944
|
if (isNewCollection && existingIds?.includes(value.trim().toLowerCase()))
|
|
941
|
-
error = "
|
|
945
|
+
error = t("collection_uses_this_id");
|
|
942
946
|
// if (error) {
|
|
943
947
|
// setAdvancedPanelExpanded(true);
|
|
944
948
|
// }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { AIIcon, EntityCollection, prettifyIdentifier, } from "@firecms/core";
|
|
2
|
+
import { AIIcon, EntityCollection, prettifyIdentifier, useTranslation } from "@firecms/core";
|
|
3
3
|
import { Button, Card, Chip, cls, CodeIcon, Container, Icon, Tooltip, Typography, } from "@firecms/ui";
|
|
4
4
|
import { CollectionJsonImportDialog } from "./CollectionJsonImportDialog";
|
|
5
5
|
|
|
@@ -30,6 +30,7 @@ export function CollectionEditorWelcomeView({
|
|
|
30
30
|
}) {
|
|
31
31
|
|
|
32
32
|
const { pathSuggestions } = useCollectionEditorController();
|
|
33
|
+
const { t } = useTranslation();
|
|
33
34
|
|
|
34
35
|
const filteredSuggestions = (pathSuggestions ?? []).filter(s => !(existingCollectionPaths ?? []).find(c => c.trim().toLowerCase() === s.trim().toLowerCase()));
|
|
35
36
|
|
|
@@ -49,13 +50,13 @@ export function CollectionEditorWelcomeView({
|
|
|
49
50
|
<div
|
|
50
51
|
className="flex flex-row py-2 pt-3 items-center">
|
|
51
52
|
<Typography variant={"h4"} className={"flex-grow"}>
|
|
52
|
-
|
|
53
|
+
{t("new_collection")}
|
|
53
54
|
</Typography>
|
|
54
55
|
</div>
|
|
55
56
|
|
|
56
57
|
{parentCollection && <Chip colorScheme={"tealDarker"}>
|
|
57
58
|
<Typography variant={"caption"}>
|
|
58
|
-
|
|
59
|
+
{t("this_is_subcollection_of")} <b>{parentCollection.name}</b>
|
|
59
60
|
</Typography>
|
|
60
61
|
</Chip>}
|
|
61
62
|
|
|
@@ -63,7 +64,7 @@ export function CollectionEditorWelcomeView({
|
|
|
63
64
|
|
|
64
65
|
<Typography variant={"caption"}
|
|
65
66
|
color={"secondary"}>
|
|
66
|
-
●
|
|
67
|
+
● {t("use_existing_paths_database")}
|
|
67
68
|
</Typography>
|
|
68
69
|
<div className={"flex flex-wrap gap-x-2 gap-y-1 items-center my-2 min-h-7"}>
|
|
69
70
|
|
|
@@ -92,7 +93,7 @@ export function CollectionEditorWelcomeView({
|
|
|
92
93
|
<Typography variant={"caption"}
|
|
93
94
|
color={"secondary"}
|
|
94
95
|
className={"mb-2"}>
|
|
95
|
-
●
|
|
96
|
+
● {t("describe_collection_ai")}
|
|
96
97
|
</Typography>
|
|
97
98
|
|
|
98
99
|
<AICollectionGeneratorPopover
|
|
@@ -107,7 +108,7 @@ export function CollectionEditorWelcomeView({
|
|
|
107
108
|
variant="outlined"
|
|
108
109
|
startIcon={<AIIcon size="small" />}
|
|
109
110
|
>
|
|
110
|
-
|
|
111
|
+
{t("generate_with_ai")}
|
|
111
112
|
</Button>
|
|
112
113
|
}
|
|
113
114
|
/>
|
|
@@ -118,7 +119,7 @@ export function CollectionEditorWelcomeView({
|
|
|
118
119
|
<Typography variant={"caption"}
|
|
119
120
|
color={"secondary"}
|
|
120
121
|
className={"mb-2"}>
|
|
121
|
-
●
|
|
122
|
+
● {t("create_from_json_config")}
|
|
122
123
|
</Typography>
|
|
123
124
|
|
|
124
125
|
<Button
|
|
@@ -126,7 +127,7 @@ export function CollectionEditorWelcomeView({
|
|
|
126
127
|
onClick={() => setJsonImportOpen(true)}
|
|
127
128
|
startIcon={<CodeIcon size="small" />}
|
|
128
129
|
>
|
|
129
|
-
|
|
130
|
+
{t("paste_json_config")}
|
|
130
131
|
</Button>
|
|
131
132
|
|
|
132
133
|
<CollectionJsonImportDialog
|
|
@@ -149,7 +150,7 @@ export function CollectionEditorWelcomeView({
|
|
|
149
150
|
<Typography variant={"caption"}
|
|
150
151
|
color={"secondary"}
|
|
151
152
|
className={"mb-2"}>
|
|
152
|
-
●
|
|
153
|
+
● {t("create_collection_from_file_formats")}
|
|
153
154
|
</Typography>
|
|
154
155
|
|
|
155
156
|
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)} />
|
|
@@ -159,34 +160,34 @@ export function CollectionEditorWelcomeView({
|
|
|
159
160
|
<div className={"my-2"}>
|
|
160
161
|
<Typography variant={"caption"}
|
|
161
162
|
color={"secondary"}>
|
|
162
|
-
●
|
|
163
|
+
● {t("select_template")}
|
|
163
164
|
</Typography>
|
|
164
165
|
|
|
165
166
|
<div className={"flex gap-2"}>
|
|
166
|
-
<TemplateButton title={"
|
|
167
|
-
subtitle={"
|
|
167
|
+
<TemplateButton title={t("products")}
|
|
168
|
+
subtitle={t("collection_products_subtitle")}
|
|
168
169
|
icon={<Icon size={"small"}
|
|
169
170
|
iconKey={productsCollectionTemplate.icon! as string} />}
|
|
170
171
|
onClick={() => {
|
|
171
172
|
setValues(productsCollectionTemplate);
|
|
172
173
|
onContinue();
|
|
173
174
|
}} />
|
|
174
|
-
<TemplateButton title={"
|
|
175
|
-
subtitle={"
|
|
175
|
+
<TemplateButton title={t("users")}
|
|
176
|
+
subtitle={t("collection_users_subtitle")}
|
|
176
177
|
icon={<Icon size={"small"} iconKey={usersCollectionTemplate.icon! as string} />}
|
|
177
178
|
onClick={() => {
|
|
178
179
|
setValues(usersCollectionTemplate);
|
|
179
180
|
onContinue();
|
|
180
181
|
}} />
|
|
181
|
-
<TemplateButton title={"
|
|
182
|
-
subtitle={"
|
|
182
|
+
<TemplateButton title={t("blog_posts")}
|
|
183
|
+
subtitle={t("collection_blog_posts_subtitle")}
|
|
183
184
|
icon={<Icon size={"small"} iconKey={blogCollectionTemplate.icon! as string} />}
|
|
184
185
|
onClick={() => {
|
|
185
186
|
setValues(blogCollectionTemplate);
|
|
186
187
|
onContinue();
|
|
187
188
|
}} />
|
|
188
|
-
<TemplateButton title={"
|
|
189
|
-
subtitle={"
|
|
189
|
+
<TemplateButton title={t("pages")}
|
|
190
|
+
subtitle={t("collection_pages_subtitle")}
|
|
190
191
|
icon={<Icon size={"small"} iconKey={pagesCollectionTemplate.icon! as string} />}
|
|
191
192
|
onClick={() => {
|
|
192
193
|
setValues(pagesCollectionTemplate);
|