@firecms/collection_editor 3.0.0 → 3.1.0-canary.1df3b2c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ConfigControllerProvider.d.ts +6 -0
- package/dist/api/generateCollectionApi.d.ts +71 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +9677 -5837
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +9653 -5813
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +14 -0
- package/dist/types/collection_inference.d.ts +8 -2
- package/dist/types/config_controller.d.ts +31 -1
- package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
- package/dist/ui/KanbanSetupAction.d.ts +10 -0
- package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +33 -0
- package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
- package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
- package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
- package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
- package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
- package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
- package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
- package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
- package/dist/useCollectionEditorPlugin.d.ts +7 -1
- package/dist/utils/validateCollectionJson.d.ts +22 -0
- package/package.json +11 -11
- package/src/ConfigControllerProvider.tsx +81 -47
- package/src/api/generateCollectionApi.ts +119 -0
- package/src/api/index.ts +1 -0
- package/src/index.ts +28 -1
- package/src/types/collection_editor_controller.tsx +16 -3
- package/src/types/collection_inference.ts +15 -2
- package/src/types/config_controller.tsx +37 -1
- package/src/ui/AddKanbanColumnAction.tsx +203 -0
- package/src/ui/EditorCollectionActionStart.tsx +1 -2
- package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
- package/src/ui/KanbanSetupAction.tsx +38 -0
- package/src/ui/MissingReferenceWidget.tsx +1 -1
- package/src/ui/NewCollectionButton.tsx +1 -1
- package/src/ui/PropertyAddColumnComponent.tsx +1 -1
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +225 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +209 -258
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +226 -173
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +130 -67
- package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +190 -91
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +333 -0
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -96
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +6 -7
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +1 -3
- package/src/ui/collection_editor/EnumForm.tsx +147 -100
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +93 -0
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +57 -36
- package/src/ui/collection_editor/KanbanConfigSection.tsx +207 -0
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +22 -41
- package/src/ui/collection_editor/PropertyEditView.tsx +205 -141
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
- package/src/ui/collection_editor/PropertyTree.tsx +130 -58
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +171 -162
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
- package/src/ui/collection_editor/ViewModeSwitch.tsx +41 -0
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +1 -0
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +117 -35
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +28 -21
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +115 -39
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +1 -5
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +23 -2
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +861 -0
- package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
- package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
- package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
- package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +1 -1
- package/src/useCollectionEditorPlugin.tsx +32 -17
- package/src/utils/validateCollectionJson.ts +380 -0
|
@@ -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, } 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,17 +10,21 @@ 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
|
+
}: {
|
|
19
23
|
path: string;
|
|
20
24
|
parentCollection?: EntityCollection;
|
|
21
25
|
onContinue: (importData?: object[], propertiesOrder?: string[]) => void;
|
|
22
26
|
existingCollectionPaths?: string[];
|
|
27
|
+
generateCollection?: CollectionGenerationCallback;
|
|
23
28
|
}) {
|
|
24
29
|
|
|
25
30
|
const { pathSuggestions } = useCollectionEditorController();
|
|
@@ -33,6 +38,8 @@ export function CollectionEditorWelcomeView({
|
|
|
33
38
|
submitCount
|
|
34
39
|
} = useFormex<EntityCollection>();
|
|
35
40
|
|
|
41
|
+
const [jsonImportOpen, setJsonImportOpen] = useState(false);
|
|
42
|
+
|
|
36
43
|
return (
|
|
37
44
|
<div className={"overflow-auto my-auto"}>
|
|
38
45
|
<Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
@@ -53,22 +60,22 @@ export function CollectionEditorWelcomeView({
|
|
|
53
60
|
{(filteredSuggestions ?? []).length > 0 && <div className={"my-2"}>
|
|
54
61
|
|
|
55
62
|
<Typography variant={"caption"}
|
|
56
|
-
|
|
63
|
+
color={"secondary"}>
|
|
57
64
|
● Use one of the existing paths in your database:
|
|
58
65
|
</Typography>
|
|
59
66
|
<div className={"flex flex-wrap gap-x-2 gap-y-1 items-center my-2 min-h-7"}>
|
|
60
67
|
|
|
61
68
|
{filteredSuggestions?.map((suggestion, index) => (
|
|
62
69
|
<Chip key={suggestion}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
colorScheme={"cyanLighter"}
|
|
71
|
+
onClick={() => {
|
|
72
|
+
setFieldValue("name", prettifyIdentifier(suggestion));
|
|
73
|
+
setFieldValue("id", suggestion);
|
|
74
|
+
setFieldValue("path", suggestion);
|
|
75
|
+
setFieldValue("properties", undefined);
|
|
76
|
+
onContinue();
|
|
77
|
+
}}
|
|
78
|
+
size="small">
|
|
72
79
|
{suggestion}
|
|
73
80
|
</Chip>
|
|
74
81
|
))}
|
|
@@ -76,61 +83,117 @@ export function CollectionEditorWelcomeView({
|
|
|
76
83
|
</div>
|
|
77
84
|
|
|
78
85
|
</div>}
|
|
86
|
+
<div className="flex flex-row gap-8">
|
|
79
87
|
|
|
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
|
-
|
|
88
|
+
{generateCollection && (
|
|
89
|
+
<div className={"my-2"}>
|
|
90
|
+
<Typography variant={"caption"}
|
|
91
|
+
color={"secondary"}
|
|
92
|
+
className={"mb-2"}>
|
|
93
|
+
● Describe your collection to AI:
|
|
94
|
+
</Typography>
|
|
95
|
+
|
|
96
|
+
<AICollectionGeneratorPopover
|
|
97
|
+
onGenerated={(generatedCollection) => {
|
|
98
|
+
setValues(generatedCollection);
|
|
99
|
+
onContinue();
|
|
100
|
+
}}
|
|
101
|
+
generateCollection={generateCollection}
|
|
102
|
+
trigger={
|
|
103
|
+
<Button
|
|
104
|
+
variant="outlined"
|
|
105
|
+
startIcon={<AIIcon size="small" />}
|
|
106
|
+
>
|
|
107
|
+
Generate with AI
|
|
108
|
+
</Button>
|
|
109
|
+
}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
|
|
114
|
+
<div className={"my-2"}>
|
|
115
|
+
<Typography variant={"caption"}
|
|
116
|
+
color={"secondary"}
|
|
117
|
+
className={"mb-2"}>
|
|
118
|
+
● Create from JSON configuration:
|
|
119
|
+
</Typography>
|
|
120
|
+
|
|
121
|
+
<Button
|
|
122
|
+
variant={"outlined"}
|
|
123
|
+
onClick={() => setJsonImportOpen(true)}
|
|
124
|
+
startIcon={<CodeIcon size="small" />}
|
|
125
|
+
>
|
|
126
|
+
Paste JSON Configuration
|
|
127
|
+
</Button>
|
|
128
|
+
|
|
129
|
+
<CollectionJsonImportDialog
|
|
130
|
+
open={jsonImportOpen}
|
|
131
|
+
onClose={() => setJsonImportOpen(false)}
|
|
132
|
+
onImport={(collection) => {
|
|
133
|
+
setValues(collection);
|
|
134
|
+
onContinue();
|
|
135
|
+
}}
|
|
136
|
+
/>
|
|
116
137
|
</div>
|
|
117
138
|
|
|
139
|
+
|
|
140
|
+
|
|
118
141
|
</div>
|
|
119
142
|
|
|
143
|
+
|
|
120
144
|
{!parentCollection && <div>
|
|
121
145
|
|
|
122
146
|
<Typography variant={"caption"}
|
|
123
|
-
|
|
124
|
-
|
|
147
|
+
color={"secondary"}
|
|
148
|
+
className={"mb-2"}>
|
|
125
149
|
● Create a collection from a file (csv, json, xls, xslx...)
|
|
126
150
|
</Typography>
|
|
127
151
|
|
|
128
|
-
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)}/>
|
|
152
|
+
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)} />
|
|
129
153
|
|
|
130
154
|
</div>}
|
|
131
155
|
|
|
156
|
+
<div className={"my-2"}>
|
|
157
|
+
<Typography variant={"caption"}
|
|
158
|
+
color={"secondary"}>
|
|
159
|
+
● Select a template:
|
|
160
|
+
</Typography>
|
|
161
|
+
|
|
162
|
+
<div className={"flex gap-2"}>
|
|
163
|
+
<TemplateButton title={"Products"}
|
|
164
|
+
subtitle={"A collection of products with images, prices and stock"}
|
|
165
|
+
icon={<Icon size={"small"}
|
|
166
|
+
iconKey={productsCollectionTemplate.icon! as string} />}
|
|
167
|
+
onClick={() => {
|
|
168
|
+
setValues(productsCollectionTemplate);
|
|
169
|
+
onContinue();
|
|
170
|
+
}} />
|
|
171
|
+
<TemplateButton title={"Users"}
|
|
172
|
+
subtitle={"A collection of users with emails, names and roles"}
|
|
173
|
+
icon={<Icon size={"small"} iconKey={usersCollectionTemplate.icon! as string} />}
|
|
174
|
+
onClick={() => {
|
|
175
|
+
setValues(usersCollectionTemplate);
|
|
176
|
+
onContinue();
|
|
177
|
+
}} />
|
|
178
|
+
<TemplateButton title={"Blog posts"}
|
|
179
|
+
subtitle={"A collection of blog posts with images, authors and complex content"}
|
|
180
|
+
icon={<Icon size={"small"} iconKey={blogCollectionTemplate.icon! as string} />}
|
|
181
|
+
onClick={() => {
|
|
182
|
+
setValues(blogCollectionTemplate);
|
|
183
|
+
onContinue();
|
|
184
|
+
}} />
|
|
185
|
+
<TemplateButton title={"Pages"}
|
|
186
|
+
subtitle={"A collection of pages with images, authors and complex content"}
|
|
187
|
+
icon={<Icon size={"small"} iconKey={pagesCollectionTemplate.icon! as string} />}
|
|
188
|
+
onClick={() => {
|
|
189
|
+
setValues(pagesCollectionTemplate);
|
|
190
|
+
onContinue();
|
|
191
|
+
}} />
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
</div>
|
|
195
|
+
|
|
132
196
|
|
|
133
|
-
{/*<div style={{ height: "52px" }}/>*/}
|
|
134
197
|
|
|
135
198
|
</Container>
|
|
136
199
|
</div>
|
|
@@ -138,11 +201,11 @@ export function CollectionEditorWelcomeView({
|
|
|
138
201
|
}
|
|
139
202
|
|
|
140
203
|
export function TemplateButton({
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
204
|
+
title,
|
|
205
|
+
subtitle,
|
|
206
|
+
icon,
|
|
207
|
+
onClick
|
|
208
|
+
}: {
|
|
146
209
|
title: string,
|
|
147
210
|
icon: React.ReactNode,
|
|
148
211
|
subtitle: string,
|
|
@@ -151,12 +214,12 @@ export function TemplateButton({
|
|
|
151
214
|
|
|
152
215
|
return (
|
|
153
216
|
<Tooltip title={subtitle}
|
|
154
|
-
|
|
217
|
+
asChild={true}>
|
|
155
218
|
<Card
|
|
156
219
|
onClick={onClick}
|
|
157
220
|
className={cls(
|
|
158
|
-
"my-2 rounded-md border
|
|
159
|
-
"text-
|
|
221
|
+
"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",
|
|
222
|
+
"text-text-secondary dark:text-text-secondary-dark",
|
|
160
223
|
"hover:border-primary-dark hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
|
|
161
224
|
"border-surface-400 dark:border-surface-600 "
|
|
162
225
|
)}
|
|
@@ -164,7 +227,7 @@ export function TemplateButton({
|
|
|
164
227
|
{icon}
|
|
165
228
|
<div className={"flex flex-col items-start"}>
|
|
166
229
|
|
|
167
|
-
<Typography variant={"
|
|
230
|
+
<Typography variant={"subtitle2"}>
|
|
168
231
|
{title}
|
|
169
232
|
</Typography>
|
|
170
233
|
|
|
@@ -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
|
+
}
|