@firecms/collection_editor 3.0.1 → 3.1.0-canary.768c91f
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 +9466 -5588
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +9461 -5583
- 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 +23 -2
- 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/types/collection_editor_controller.tsx +16 -3
- package/src/types/collection_inference.ts +15 -2
- package/src/types/config_controller.tsx +27 -2
- 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 +242 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +212 -259
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +237 -169
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +133 -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 +337 -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 +206 -142
- 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/StoragePropertyField.tsx +1 -1
- 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,23 @@ 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,6 +40,8 @@ export function CollectionEditorWelcomeView({
|
|
|
33
40
|
submitCount
|
|
34
41
|
} = useFormex<EntityCollection>();
|
|
35
42
|
|
|
43
|
+
const [jsonImportOpen, setJsonImportOpen] = useState(false);
|
|
44
|
+
|
|
36
45
|
return (
|
|
37
46
|
<div className={"overflow-auto my-auto"}>
|
|
38
47
|
<Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
@@ -53,22 +62,22 @@ export function CollectionEditorWelcomeView({
|
|
|
53
62
|
{(filteredSuggestions ?? []).length > 0 && <div className={"my-2"}>
|
|
54
63
|
|
|
55
64
|
<Typography variant={"caption"}
|
|
56
|
-
|
|
65
|
+
color={"secondary"}>
|
|
57
66
|
● Use one of the existing paths in your database:
|
|
58
67
|
</Typography>
|
|
59
68
|
<div className={"flex flex-wrap gap-x-2 gap-y-1 items-center my-2 min-h-7"}>
|
|
60
69
|
|
|
61
70
|
{filteredSuggestions?.map((suggestion, index) => (
|
|
62
71
|
<Chip key={suggestion}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
colorScheme={"cyanLighter"}
|
|
73
|
+
onClick={() => {
|
|
74
|
+
setFieldValue("name", prettifyIdentifier(suggestion));
|
|
75
|
+
setFieldValue("id", suggestion);
|
|
76
|
+
setFieldValue("path", suggestion);
|
|
77
|
+
setFieldValue("properties", undefined);
|
|
78
|
+
onContinue();
|
|
79
|
+
}}
|
|
80
|
+
size="small">
|
|
72
81
|
{suggestion}
|
|
73
82
|
</Chip>
|
|
74
83
|
))}
|
|
@@ -76,61 +85,118 @@ export function CollectionEditorWelcomeView({
|
|
|
76
85
|
</div>
|
|
77
86
|
|
|
78
87
|
</div>}
|
|
88
|
+
<div className="flex flex-row gap-8">
|
|
79
89
|
|
|
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
|
-
|
|
90
|
+
{generateCollection && (
|
|
91
|
+
<div className={"my-2"}>
|
|
92
|
+
<Typography variant={"caption"}
|
|
93
|
+
color={"secondary"}
|
|
94
|
+
className={"mb-2"}>
|
|
95
|
+
● Describe your collection to AI:
|
|
96
|
+
</Typography>
|
|
97
|
+
|
|
98
|
+
<AICollectionGeneratorPopover
|
|
99
|
+
onGenerated={(generatedCollection) => {
|
|
100
|
+
setValues(generatedCollection);
|
|
101
|
+
onContinue();
|
|
102
|
+
}}
|
|
103
|
+
generateCollection={generateCollection}
|
|
104
|
+
onAnalyticsEvent={onAnalyticsEvent}
|
|
105
|
+
trigger={
|
|
106
|
+
<Button
|
|
107
|
+
variant="outlined"
|
|
108
|
+
startIcon={<AIIcon size="small" />}
|
|
109
|
+
>
|
|
110
|
+
Generate with AI
|
|
111
|
+
</Button>
|
|
112
|
+
}
|
|
113
|
+
/>
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
|
|
117
|
+
<div className={"my-2"}>
|
|
118
|
+
<Typography variant={"caption"}
|
|
119
|
+
color={"secondary"}
|
|
120
|
+
className={"mb-2"}>
|
|
121
|
+
● Create from JSON configuration:
|
|
122
|
+
</Typography>
|
|
123
|
+
|
|
124
|
+
<Button
|
|
125
|
+
variant={"outlined"}
|
|
126
|
+
onClick={() => setJsonImportOpen(true)}
|
|
127
|
+
startIcon={<CodeIcon size="small" />}
|
|
128
|
+
>
|
|
129
|
+
Paste JSON Configuration
|
|
130
|
+
</Button>
|
|
131
|
+
|
|
132
|
+
<CollectionJsonImportDialog
|
|
133
|
+
open={jsonImportOpen}
|
|
134
|
+
onClose={() => setJsonImportOpen(false)}
|
|
135
|
+
onImport={(collection) => {
|
|
136
|
+
setValues(collection);
|
|
137
|
+
onContinue();
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
116
140
|
</div>
|
|
117
141
|
|
|
142
|
+
|
|
143
|
+
|
|
118
144
|
</div>
|
|
119
145
|
|
|
146
|
+
|
|
120
147
|
{!parentCollection && <div>
|
|
121
148
|
|
|
122
149
|
<Typography variant={"caption"}
|
|
123
|
-
|
|
124
|
-
|
|
150
|
+
color={"secondary"}
|
|
151
|
+
className={"mb-2"}>
|
|
125
152
|
● Create a collection from a file (csv, json, xls, xslx...)
|
|
126
153
|
</Typography>
|
|
127
154
|
|
|
128
|
-
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)}/>
|
|
155
|
+
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)} />
|
|
129
156
|
|
|
130
157
|
</div>}
|
|
131
158
|
|
|
159
|
+
<div className={"my-2"}>
|
|
160
|
+
<Typography variant={"caption"}
|
|
161
|
+
color={"secondary"}>
|
|
162
|
+
● Select a template:
|
|
163
|
+
</Typography>
|
|
164
|
+
|
|
165
|
+
<div className={"flex gap-2"}>
|
|
166
|
+
<TemplateButton title={"Products"}
|
|
167
|
+
subtitle={"A collection of products with images, prices and stock"}
|
|
168
|
+
icon={<Icon size={"small"}
|
|
169
|
+
iconKey={productsCollectionTemplate.icon! as string} />}
|
|
170
|
+
onClick={() => {
|
|
171
|
+
setValues(productsCollectionTemplate);
|
|
172
|
+
onContinue();
|
|
173
|
+
}} />
|
|
174
|
+
<TemplateButton title={"Users"}
|
|
175
|
+
subtitle={"A collection of users with emails, names and roles"}
|
|
176
|
+
icon={<Icon size={"small"} iconKey={usersCollectionTemplate.icon! as string} />}
|
|
177
|
+
onClick={() => {
|
|
178
|
+
setValues(usersCollectionTemplate);
|
|
179
|
+
onContinue();
|
|
180
|
+
}} />
|
|
181
|
+
<TemplateButton title={"Blog posts"}
|
|
182
|
+
subtitle={"A collection of blog posts with images, authors and complex content"}
|
|
183
|
+
icon={<Icon size={"small"} iconKey={blogCollectionTemplate.icon! as string} />}
|
|
184
|
+
onClick={() => {
|
|
185
|
+
setValues(blogCollectionTemplate);
|
|
186
|
+
onContinue();
|
|
187
|
+
}} />
|
|
188
|
+
<TemplateButton title={"Pages"}
|
|
189
|
+
subtitle={"A collection of pages with images, authors and complex content"}
|
|
190
|
+
icon={<Icon size={"small"} iconKey={pagesCollectionTemplate.icon! as string} />}
|
|
191
|
+
onClick={() => {
|
|
192
|
+
setValues(pagesCollectionTemplate);
|
|
193
|
+
onContinue();
|
|
194
|
+
}} />
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
</div>
|
|
198
|
+
|
|
132
199
|
|
|
133
|
-
{/*<div style={{ height: "52px" }}/>*/}
|
|
134
200
|
|
|
135
201
|
</Container>
|
|
136
202
|
</div>
|
|
@@ -138,11 +204,11 @@ export function CollectionEditorWelcomeView({
|
|
|
138
204
|
}
|
|
139
205
|
|
|
140
206
|
export function TemplateButton({
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
207
|
+
title,
|
|
208
|
+
subtitle,
|
|
209
|
+
icon,
|
|
210
|
+
onClick
|
|
211
|
+
}: {
|
|
146
212
|
title: string,
|
|
147
213
|
icon: React.ReactNode,
|
|
148
214
|
subtitle: string,
|
|
@@ -151,12 +217,12 @@ export function TemplateButton({
|
|
|
151
217
|
|
|
152
218
|
return (
|
|
153
219
|
<Tooltip title={subtitle}
|
|
154
|
-
|
|
220
|
+
asChild={true}>
|
|
155
221
|
<Card
|
|
156
222
|
onClick={onClick}
|
|
157
223
|
className={cls(
|
|
158
|
-
"my-2 rounded-md border
|
|
159
|
-
"text-
|
|
224
|
+
"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",
|
|
225
|
+
"text-text-secondary dark:text-text-secondary-dark",
|
|
160
226
|
"hover:border-primary-dark hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
|
|
161
227
|
"border-surface-400 dark:border-surface-600 "
|
|
162
228
|
)}
|
|
@@ -164,7 +230,7 @@ export function TemplateButton({
|
|
|
164
230
|
{icon}
|
|
165
231
|
<div className={"flex flex-col items-start"}>
|
|
166
232
|
|
|
167
|
-
<Typography variant={"
|
|
233
|
+
<Typography variant={"subtitle2"}>
|
|
168
234
|
{title}
|
|
169
235
|
</Typography>
|
|
170
236
|
|
|
@@ -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
|
+
}
|