@firecms/collection_editor 3.0.0 → 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.
- package/dist/ConfigControllerProvider.d.ts +6 -0
- package/dist/api/generateCollectionApi.d.ts +71 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +15234 -8138
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +15199 -8103
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +120 -0
- package/dist/locales/en.d.ts +120 -0
- package/dist/locales/es.d.ts +120 -0
- package/dist/locales/fr.d.ts +120 -0
- package/dist/locales/hi.d.ts +120 -0
- package/dist/locales/it.d.ts +120 -0
- package/dist/locales/pt.d.ts +120 -0
- package/dist/types/collection_editor_controller.d.ts +14 -0
- package/dist/types/collection_inference.d.ts +8 -2
- package/dist/types/config_controller.d.ts +31 -1
- package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
- package/dist/ui/KanbanSetupAction.d.ts +10 -0
- package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +37 -0
- package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +24 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +4 -1
- package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
- package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
- package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
- package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
- package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
- package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
- package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
- package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
- package/dist/useCollectionEditorPlugin.d.ts +7 -1
- package/dist/utils/validateCollectionJson.d.ts +22 -0
- package/package.json +15 -15
- package/src/ConfigControllerProvider.tsx +82 -47
- package/src/api/generateCollectionApi.ts +119 -0
- package/src/api/index.ts +1 -0
- package/src/index.ts +28 -1
- package/src/locales/de.ts +125 -0
- package/src/locales/en.ts +145 -0
- package/src/locales/es.ts +125 -0
- package/src/locales/fr.ts +125 -0
- package/src/locales/hi.ts +125 -0
- package/src/locales/it.ts +125 -0
- package/src/locales/pt.ts +125 -0
- package/src/types/collection_editor_controller.tsx +16 -3
- package/src/types/collection_inference.ts +15 -2
- package/src/types/config_controller.tsx +37 -1
- package/src/ui/AddKanbanColumnAction.tsx +203 -0
- package/src/ui/EditorCollectionAction.tsx +3 -3
- package/src/ui/EditorCollectionActionStart.tsx +1 -2
- package/src/ui/EditorEntityAction.tsx +3 -2
- package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
- package/src/ui/KanbanSetupAction.tsx +38 -0
- package/src/ui/MissingReferenceWidget.tsx +1 -1
- package/src/ui/NewCollectionButton.tsx +4 -2
- package/src/ui/NewCollectionCard.tsx +7 -4
- package/src/ui/PropertyAddColumnComponent.tsx +4 -3
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +243 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +222 -268
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +270 -204
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +138 -71
- package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +202 -101
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +335 -0
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -97
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +8 -10
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +5 -7
- package/src/ui/collection_editor/EnumForm.tsx +153 -102
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +94 -0
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +63 -41
- package/src/ui/collection_editor/KanbanConfigSection.tsx +209 -0
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +27 -43
- package/src/ui/collection_editor/PropertyEditView.tsx +272 -199
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
- package/src/ui/collection_editor/PropertyTree.tsx +130 -58
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +169 -163
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
- package/src/ui/collection_editor/ViewModeSwitch.tsx +43 -0
- 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/BlockPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +4 -1
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +6 -4
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +126 -42
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +32 -24
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -9
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +128 -53
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +6 -9
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +65 -49
- 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 +23 -4
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +866 -0
- package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
- package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
- package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
- package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
- package/src/ui/collection_editor/properties/validation/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 +5 -2
- package/src/useCollectionEditorPlugin.tsx +53 -22
- package/src/utils/validateCollectionJson.ts +380 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { EntityCollection, prettifyIdentifier, } from "@firecms/core";
|
|
3
|
-
import { Card, Chip, cls, Container, Icon, Tooltip, Typography, } from "@firecms/ui";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { AIIcon, EntityCollection, prettifyIdentifier, useTranslation } from "@firecms/core";
|
|
3
|
+
import { Button, Card, Chip, cls, CodeIcon, Container, Icon, Tooltip, Typography, } from "@firecms/ui";
|
|
4
|
+
import { CollectionJsonImportDialog } from "./CollectionJsonImportDialog";
|
|
4
5
|
|
|
5
6
|
import { productsCollectionTemplate } from "./templates/products_template";
|
|
6
7
|
import { blogCollectionTemplate } from "./templates/blog_template";
|
|
@@ -9,20 +10,27 @@ import { ImportFileUpload } from "@firecms/data_import";
|
|
|
9
10
|
import { pagesCollectionTemplate } from "./templates/pages_template";
|
|
10
11
|
import { useFormex } from "@firecms/formex";
|
|
11
12
|
import { useCollectionEditorController } from "../../useCollectionEditorController";
|
|
13
|
+
import { AICollectionGeneratorPopover } from "./AICollectionGeneratorPopover";
|
|
14
|
+
import { CollectionGenerationCallback } from "../../api/generateCollectionApi";
|
|
12
15
|
|
|
13
16
|
export function CollectionEditorWelcomeView({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
path,
|
|
18
|
+
parentCollection,
|
|
19
|
+
onContinue,
|
|
20
|
+
existingCollectionPaths,
|
|
21
|
+
generateCollection,
|
|
22
|
+
onAnalyticsEvent
|
|
23
|
+
}: {
|
|
19
24
|
path: string;
|
|
20
25
|
parentCollection?: EntityCollection;
|
|
21
26
|
onContinue: (importData?: object[], propertiesOrder?: string[]) => void;
|
|
22
27
|
existingCollectionPaths?: string[];
|
|
28
|
+
generateCollection?: CollectionGenerationCallback;
|
|
29
|
+
onAnalyticsEvent?: (event: string, params?: object) => void;
|
|
23
30
|
}) {
|
|
24
31
|
|
|
25
32
|
const { pathSuggestions } = useCollectionEditorController();
|
|
33
|
+
const { t } = useTranslation();
|
|
26
34
|
|
|
27
35
|
const filteredSuggestions = (pathSuggestions ?? []).filter(s => !(existingCollectionPaths ?? []).find(c => c.trim().toLowerCase() === s.trim().toLowerCase()));
|
|
28
36
|
|
|
@@ -33,6 +41,8 @@ export function CollectionEditorWelcomeView({
|
|
|
33
41
|
submitCount
|
|
34
42
|
} = useFormex<EntityCollection>();
|
|
35
43
|
|
|
44
|
+
const [jsonImportOpen, setJsonImportOpen] = useState(false);
|
|
45
|
+
|
|
36
46
|
return (
|
|
37
47
|
<div className={"overflow-auto my-auto"}>
|
|
38
48
|
<Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
@@ -40,35 +50,35 @@ export function CollectionEditorWelcomeView({
|
|
|
40
50
|
<div
|
|
41
51
|
className="flex flex-row py-2 pt-3 items-center">
|
|
42
52
|
<Typography variant={"h4"} className={"flex-grow"}>
|
|
43
|
-
|
|
53
|
+
{t("new_collection")}
|
|
44
54
|
</Typography>
|
|
45
55
|
</div>
|
|
46
56
|
|
|
47
57
|
{parentCollection && <Chip colorScheme={"tealDarker"}>
|
|
48
58
|
<Typography variant={"caption"}>
|
|
49
|
-
|
|
59
|
+
{t("this_is_subcollection_of")} <b>{parentCollection.name}</b>
|
|
50
60
|
</Typography>
|
|
51
61
|
</Chip>}
|
|
52
62
|
|
|
53
63
|
{(filteredSuggestions ?? []).length > 0 && <div className={"my-2"}>
|
|
54
64
|
|
|
55
65
|
<Typography variant={"caption"}
|
|
56
|
-
|
|
57
|
-
●
|
|
66
|
+
color={"secondary"}>
|
|
67
|
+
● {t("use_existing_paths_database")}
|
|
58
68
|
</Typography>
|
|
59
69
|
<div className={"flex flex-wrap gap-x-2 gap-y-1 items-center my-2 min-h-7"}>
|
|
60
70
|
|
|
61
71
|
{filteredSuggestions?.map((suggestion, index) => (
|
|
62
72
|
<Chip key={suggestion}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
colorScheme={"cyanLighter"}
|
|
74
|
+
onClick={() => {
|
|
75
|
+
setFieldValue("name", prettifyIdentifier(suggestion));
|
|
76
|
+
setFieldValue("id", suggestion);
|
|
77
|
+
setFieldValue("path", suggestion);
|
|
78
|
+
setFieldValue("properties", undefined);
|
|
79
|
+
onContinue();
|
|
80
|
+
}}
|
|
81
|
+
size="small">
|
|
72
82
|
{suggestion}
|
|
73
83
|
</Chip>
|
|
74
84
|
))}
|
|
@@ -76,61 +86,118 @@ export function CollectionEditorWelcomeView({
|
|
|
76
86
|
</div>
|
|
77
87
|
|
|
78
88
|
</div>}
|
|
89
|
+
<div className="flex flex-row gap-8">
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
91
|
+
{generateCollection && (
|
|
92
|
+
<div className={"my-2"}>
|
|
93
|
+
<Typography variant={"caption"}
|
|
94
|
+
color={"secondary"}
|
|
95
|
+
className={"mb-2"}>
|
|
96
|
+
● {t("describe_collection_ai")}
|
|
97
|
+
</Typography>
|
|
98
|
+
|
|
99
|
+
<AICollectionGeneratorPopover
|
|
100
|
+
onGenerated={(generatedCollection) => {
|
|
101
|
+
setValues(generatedCollection);
|
|
102
|
+
onContinue();
|
|
103
|
+
}}
|
|
104
|
+
generateCollection={generateCollection}
|
|
105
|
+
onAnalyticsEvent={onAnalyticsEvent}
|
|
106
|
+
trigger={
|
|
107
|
+
<Button
|
|
108
|
+
variant="outlined"
|
|
109
|
+
startIcon={<AIIcon size="small" />}
|
|
110
|
+
>
|
|
111
|
+
{t("generate_with_ai")}
|
|
112
|
+
</Button>
|
|
113
|
+
}
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
)}
|
|
117
|
+
|
|
118
|
+
<div className={"my-2"}>
|
|
119
|
+
<Typography variant={"caption"}
|
|
120
|
+
color={"secondary"}
|
|
121
|
+
className={"mb-2"}>
|
|
122
|
+
● {t("create_from_json_config")}
|
|
123
|
+
</Typography>
|
|
124
|
+
|
|
125
|
+
<Button
|
|
126
|
+
variant={"outlined"}
|
|
127
|
+
onClick={() => setJsonImportOpen(true)}
|
|
128
|
+
startIcon={<CodeIcon size="small" />}
|
|
129
|
+
>
|
|
130
|
+
{t("paste_json_config")}
|
|
131
|
+
</Button>
|
|
132
|
+
|
|
133
|
+
<CollectionJsonImportDialog
|
|
134
|
+
open={jsonImportOpen}
|
|
135
|
+
onClose={() => setJsonImportOpen(false)}
|
|
136
|
+
onImport={(collection) => {
|
|
137
|
+
setValues(collection);
|
|
138
|
+
onContinue();
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
116
141
|
</div>
|
|
117
142
|
|
|
143
|
+
|
|
144
|
+
|
|
118
145
|
</div>
|
|
119
146
|
|
|
147
|
+
|
|
120
148
|
{!parentCollection && <div>
|
|
121
149
|
|
|
122
150
|
<Typography variant={"caption"}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
●
|
|
151
|
+
color={"secondary"}
|
|
152
|
+
className={"mb-2"}>
|
|
153
|
+
● {t("create_collection_from_file_formats")}
|
|
126
154
|
</Typography>
|
|
127
155
|
|
|
128
|
-
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)}/>
|
|
156
|
+
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)} />
|
|
129
157
|
|
|
130
158
|
</div>}
|
|
131
159
|
|
|
160
|
+
<div className={"my-2"}>
|
|
161
|
+
<Typography variant={"caption"}
|
|
162
|
+
color={"secondary"}>
|
|
163
|
+
● {t("select_template")}
|
|
164
|
+
</Typography>
|
|
165
|
+
|
|
166
|
+
<div className={"flex gap-2"}>
|
|
167
|
+
<TemplateButton title={t("products")}
|
|
168
|
+
subtitle={t("collection_products_subtitle")}
|
|
169
|
+
icon={<Icon size={"small"}
|
|
170
|
+
iconKey={productsCollectionTemplate.icon! as string} />}
|
|
171
|
+
onClick={() => {
|
|
172
|
+
setValues(productsCollectionTemplate);
|
|
173
|
+
onContinue();
|
|
174
|
+
}} />
|
|
175
|
+
<TemplateButton title={t("users")}
|
|
176
|
+
subtitle={t("collection_users_subtitle")}
|
|
177
|
+
icon={<Icon size={"small"} iconKey={usersCollectionTemplate.icon! as string} />}
|
|
178
|
+
onClick={() => {
|
|
179
|
+
setValues(usersCollectionTemplate);
|
|
180
|
+
onContinue();
|
|
181
|
+
}} />
|
|
182
|
+
<TemplateButton title={t("blog_posts")}
|
|
183
|
+
subtitle={t("collection_blog_posts_subtitle")}
|
|
184
|
+
icon={<Icon size={"small"} iconKey={blogCollectionTemplate.icon! as string} />}
|
|
185
|
+
onClick={() => {
|
|
186
|
+
setValues(blogCollectionTemplate);
|
|
187
|
+
onContinue();
|
|
188
|
+
}} />
|
|
189
|
+
<TemplateButton title={t("pages")}
|
|
190
|
+
subtitle={t("collection_pages_subtitle")}
|
|
191
|
+
icon={<Icon size={"small"} iconKey={pagesCollectionTemplate.icon! as string} />}
|
|
192
|
+
onClick={() => {
|
|
193
|
+
setValues(pagesCollectionTemplate);
|
|
194
|
+
onContinue();
|
|
195
|
+
}} />
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
</div>
|
|
199
|
+
|
|
132
200
|
|
|
133
|
-
{/*<div style={{ height: "52px" }}/>*/}
|
|
134
201
|
|
|
135
202
|
</Container>
|
|
136
203
|
</div>
|
|
@@ -138,11 +205,11 @@ export function CollectionEditorWelcomeView({
|
|
|
138
205
|
}
|
|
139
206
|
|
|
140
207
|
export function TemplateButton({
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
208
|
+
title,
|
|
209
|
+
subtitle,
|
|
210
|
+
icon,
|
|
211
|
+
onClick
|
|
212
|
+
}: {
|
|
146
213
|
title: string,
|
|
147
214
|
icon: React.ReactNode,
|
|
148
215
|
subtitle: string,
|
|
@@ -151,12 +218,12 @@ export function TemplateButton({
|
|
|
151
218
|
|
|
152
219
|
return (
|
|
153
220
|
<Tooltip title={subtitle}
|
|
154
|
-
|
|
221
|
+
asChild={true}>
|
|
155
222
|
<Card
|
|
156
223
|
onClick={onClick}
|
|
157
224
|
className={cls(
|
|
158
|
-
"my-2 rounded-md border
|
|
159
|
-
"text-
|
|
225
|
+
"my-2 rounded-md border px-4 py-3 focus:outline-none transition ease-in-out duration-150 flex flex-row gap-4 items-center",
|
|
226
|
+
"text-text-secondary dark:text-text-secondary-dark",
|
|
160
227
|
"hover:border-primary-dark hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
|
|
161
228
|
"border-surface-400 dark:border-surface-600 "
|
|
162
229
|
)}
|
|
@@ -164,7 +231,7 @@ export function TemplateButton({
|
|
|
164
231
|
{icon}
|
|
165
232
|
<div className={"flex flex-col items-start"}>
|
|
166
233
|
|
|
167
|
-
<Typography variant={"
|
|
234
|
+
<Typography variant={"subtitle2"}>
|
|
168
235
|
{title}
|
|
169
236
|
</Typography>
|
|
170
237
|
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
cls,
|
|
5
|
+
CodeIcon,
|
|
6
|
+
Dialog,
|
|
7
|
+
DialogActions,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
Typography
|
|
11
|
+
} from "@firecms/ui";
|
|
12
|
+
import { EntityCollection } from "@firecms/core";
|
|
13
|
+
import { validateCollectionJson, CollectionValidationError } from "../../utils/validateCollectionJson";
|
|
14
|
+
|
|
15
|
+
const EXAMPLE_JSON = `{
|
|
16
|
+
"id": "products",
|
|
17
|
+
"name": "Products",
|
|
18
|
+
"path": "products",
|
|
19
|
+
"icon": "shopping_cart",
|
|
20
|
+
"properties": {
|
|
21
|
+
"name": {
|
|
22
|
+
"dataType": "string",
|
|
23
|
+
"name": "Name",
|
|
24
|
+
"validation": { "required": true }
|
|
25
|
+
},
|
|
26
|
+
"price": {
|
|
27
|
+
"dataType": "number",
|
|
28
|
+
"name": "Price"
|
|
29
|
+
},
|
|
30
|
+
"available": {
|
|
31
|
+
"dataType": "boolean",
|
|
32
|
+
"name": "Available"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}`;
|
|
36
|
+
|
|
37
|
+
export interface CollectionJsonImportDialogProps {
|
|
38
|
+
open: boolean;
|
|
39
|
+
onClose: () => void;
|
|
40
|
+
onImport: (collection: EntityCollection) => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function CollectionJsonImportDialog({
|
|
44
|
+
open,
|
|
45
|
+
onClose,
|
|
46
|
+
onImport
|
|
47
|
+
}: CollectionJsonImportDialogProps) {
|
|
48
|
+
const [jsonValue, setJsonValue] = useState<string>("");
|
|
49
|
+
const [errors, setErrors] = useState<CollectionValidationError[]>([]);
|
|
50
|
+
const [touched, setTouched] = useState(false);
|
|
51
|
+
|
|
52
|
+
const handleJsonChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
53
|
+
const value = e.target.value;
|
|
54
|
+
setJsonValue(value);
|
|
55
|
+
setTouched(true);
|
|
56
|
+
|
|
57
|
+
if (!value.trim()) {
|
|
58
|
+
setErrors([]);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const result = validateCollectionJson(value);
|
|
63
|
+
setErrors(result.errors);
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
const handleImport = useCallback(() => {
|
|
67
|
+
const result = validateCollectionJson(jsonValue);
|
|
68
|
+
if (result.valid && result.collection) {
|
|
69
|
+
onImport(result.collection);
|
|
70
|
+
setJsonValue("");
|
|
71
|
+
setErrors([]);
|
|
72
|
+
setTouched(false);
|
|
73
|
+
onClose();
|
|
74
|
+
}
|
|
75
|
+
}, [jsonValue, onImport, onClose]);
|
|
76
|
+
|
|
77
|
+
const handleClose = useCallback(() => {
|
|
78
|
+
setJsonValue("");
|
|
79
|
+
setErrors([]);
|
|
80
|
+
setTouched(false);
|
|
81
|
+
onClose();
|
|
82
|
+
}, [onClose]);
|
|
83
|
+
|
|
84
|
+
const isValid = touched && jsonValue.trim() && errors.length === 0;
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Dialog
|
|
88
|
+
open={open}
|
|
89
|
+
onOpenChange={(open) => !open && handleClose()}
|
|
90
|
+
maxWidth="2xl"
|
|
91
|
+
>
|
|
92
|
+
<DialogTitle className="flex items-center gap-2">
|
|
93
|
+
<CodeIcon size="small" />
|
|
94
|
+
Import Collection from JSON
|
|
95
|
+
</DialogTitle>
|
|
96
|
+
<DialogContent className="flex flex-col gap-4">
|
|
97
|
+
<Typography variant="body2" color="secondary">
|
|
98
|
+
Paste a JSON object representing your collection configuration.
|
|
99
|
+
The JSON must include <code className="bg-surface-200 dark:bg-surface-700 px-1 rounded">id</code>,
|
|
100
|
+
<code className="bg-surface-200 dark:bg-surface-700 px-1 rounded">name</code>,
|
|
101
|
+
<code className="bg-surface-200 dark:bg-surface-700 px-1 rounded">path</code>, and
|
|
102
|
+
<code className="bg-surface-200 dark:bg-surface-700 px-1 rounded">properties</code>.
|
|
103
|
+
</Typography>
|
|
104
|
+
|
|
105
|
+
<textarea
|
|
106
|
+
value={jsonValue}
|
|
107
|
+
onChange={handleJsonChange}
|
|
108
|
+
placeholder={EXAMPLE_JSON}
|
|
109
|
+
rows={12}
|
|
110
|
+
className={cls(
|
|
111
|
+
"w-full p-3 font-mono text-sm rounded-md border resize-none overflow-y-auto",
|
|
112
|
+
"bg-surface-50 dark:bg-surface-900",
|
|
113
|
+
"focus:outline-none focus:ring-2 focus:ring-primary",
|
|
114
|
+
"h-[300px]",
|
|
115
|
+
errors.length > 0 && touched
|
|
116
|
+
? "border-red-500 dark:border-red-400"
|
|
117
|
+
: "border-surface-300 dark:border-surface-600"
|
|
118
|
+
)}
|
|
119
|
+
/>
|
|
120
|
+
|
|
121
|
+
{errors.length > 0 && touched && (
|
|
122
|
+
<div className="p-3 bg-red-50 dark:bg-red-900/20 rounded-md border border-red-200 dark:border-red-800">
|
|
123
|
+
<Typography variant="body2" className="font-medium text-red-700 dark:text-red-400 mb-2">
|
|
124
|
+
Validation errors:
|
|
125
|
+
</Typography>
|
|
126
|
+
<ul className="list-disc list-inside space-y-1">
|
|
127
|
+
{errors.map((error, index) => (
|
|
128
|
+
<li key={index} className="text-sm text-red-600 dark:text-red-400">
|
|
129
|
+
{error.path ? (
|
|
130
|
+
<>
|
|
131
|
+
<code className="bg-red-100 dark:bg-red-900/40 px-1 rounded">
|
|
132
|
+
{error.path}
|
|
133
|
+
</code>
|
|
134
|
+
: {error.message}
|
|
135
|
+
</>
|
|
136
|
+
) : (
|
|
137
|
+
error.message
|
|
138
|
+
)}
|
|
139
|
+
</li>
|
|
140
|
+
))}
|
|
141
|
+
</ul>
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
|
|
145
|
+
{isValid && (
|
|
146
|
+
<div className="p-3 bg-green-50 dark:bg-green-900/20 rounded-md border border-green-200 dark:border-green-800">
|
|
147
|
+
<Typography variant="body2" className="text-green-700 dark:text-green-400">
|
|
148
|
+
✓ JSON is valid and ready to import
|
|
149
|
+
</Typography>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
</DialogContent>
|
|
153
|
+
<DialogActions>
|
|
154
|
+
<Button
|
|
155
|
+
variant="text"
|
|
156
|
+
onClick={handleClose}
|
|
157
|
+
>
|
|
158
|
+
Cancel
|
|
159
|
+
</Button>
|
|
160
|
+
<Button
|
|
161
|
+
variant="filled"
|
|
162
|
+
color="primary"
|
|
163
|
+
onClick={handleImport}
|
|
164
|
+
disabled={!isValid}
|
|
165
|
+
>
|
|
166
|
+
Import Collection
|
|
167
|
+
</Button>
|
|
168
|
+
</DialogActions>
|
|
169
|
+
</Dialog>
|
|
170
|
+
);
|
|
171
|
+
}
|