Skip to content

Commit d1b6362

Browse files
Merge pull request #9 from aviarytech/schema-editor
Schema editor
2 parents 9ee1bbf + a487865 commit d1b6362

File tree

10 files changed

+480
-136
lines changed

10 files changed

+480
-136
lines changed

src/components/editor/AddFieldModal.tsx

Lines changed: 123 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Checkbox } from '../checkbox/Checkbox';
66
import { Dialog, DialogHeader, DialogContent } from '../modal/Dialog';
77
import { FormProperty, FormPropertyPath } from '../../types/schema';
88
import { Textarea } from '../textarea/Textarea';
9+
import { Button } from '../button/Button';
910

1011
interface AddFieldModalProps {
1112
isOpen: boolean;
@@ -135,110 +136,142 @@ export const AddFieldModal: React.FC<AddFieldModalProps> = ({
135136
};
136137

137138
return (
138-
<Dialog open={isOpen} onClose={onClose}>
139-
<DialogHeader>
140-
<h2 className="text-xl font-semibold text-gray-200">
141-
{editMode ? (
142-
<>Edit Field {parentPathString && <>in <span className="text-blue-300">{parentPathString}</span></>}</>
143-
) : parentPath.length > 0 ? (
144-
<>Add Field to <span className="text-blue-300">{parentPathString}</span></>
145-
) : (
146-
'Add Root Field'
147-
)}
148-
</h2>
139+
<Dialog
140+
open={isOpen}
141+
onClose={onClose}
142+
size="lg"
143+
>
144+
<DialogHeader className="px-6 py-4 border-b bg-gray-50 dark:bg-gray-800">
145+
<h2 className="text-xl font-semibold">Add Root Field</h2>
146+
{parentPathString && (
147+
<p className="text-sm text-gray-500 mt-1">
148+
Adding to: {parentPathString}
149+
</p>
150+
)}
149151
</DialogHeader>
150-
<DialogContent>
151-
<form onSubmit={handleSubmit}>
152-
<div className="space-y-4">
153-
<div>
154-
<label className="block text-gray-300 mb-2">Title</label>
155-
<Input
156-
type="text"
157-
value={title}
158-
onChange={(e) => setTitle(e.target.value)}
159-
placeholder="Enter field title (e.g. First Name)"
160-
required
161-
/>
162-
{!editMode && title && (
163-
<p className="text-gray-400 text-sm mt-1">
164-
Field name: <code className="bg-gray-700 px-1 rounded">{title.toLowerCase().replace(/\s+/g, '_')}</code>
165-
</p>
166-
)}
167-
</div>
168-
<div>
169-
<label className="block text-gray-300 mb-2">Type</label>
170-
<Select
171-
value={fieldType}
172-
onChange={(e) => setFieldType(e.target.value as FormProperty['type'])}
173-
>
174-
<option value="string">String</option>
175-
<option value="number">Number</option>
176-
<option value="boolean">Boolean</option>
177-
<option value="object">Object</option>
178-
<option value="array">Array</option>
179-
</Select>
180-
</div>
152+
<DialogContent className="p-6">
153+
<form onSubmit={handleSubmit} className="space-y-6">
154+
{/* Title Input */}
155+
<div className="space-y-2">
156+
<label className="flex items-center gap-1.5 text-sm font-medium text-gray-700 dark:text-gray-200">
157+
Title
158+
<span className="text-red-500">*</span>
159+
</label>
160+
<Input
161+
type="text"
162+
value={title}
163+
onChange={(e) => setTitle(e.target.value)}
164+
placeholder="Enter field title (e.g. First Name)"
165+
required
166+
className="w-full bg-white dark:bg-gray-900 shadow-sm"
167+
/>
168+
{!editMode && title && (
169+
<p className="text-sm text-gray-500 mt-1">
170+
Field name: <code className="font-mono bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">{title.toLowerCase().replace(/\s+/g, '_')}</code>
171+
</p>
172+
)}
173+
</div>
174+
175+
{/* Type Select */}
176+
<div className="space-y-2">
177+
<label className="flex items-center gap-1.5 text-sm font-medium text-gray-700 dark:text-gray-200">
178+
Type
179+
<span className="text-red-500">*</span>
180+
</label>
181+
<Select
182+
value={fieldType}
183+
onChange={(e) => setFieldType(e.target.value as FormProperty['type'])}
184+
className="w-full bg-white dark:bg-gray-900 shadow-sm"
185+
>
186+
<option value="string">String</option>
187+
<option value="number">Number</option>
188+
<option value="boolean">Boolean</option>
189+
<option value="object">Object</option>
190+
<option value="array">Array</option>
191+
</Select>
192+
</div>
193+
194+
{/* Required Checkbox */}
195+
<div className="pt-2">
181196
<Checkbox
182197
id={checkboxId.current}
183198
checked={isRequired}
184199
onChange={(e) => setIsRequired(e.target.checked)}
185-
label="Required"
200+
label="Required field"
201+
className="text-sm text-gray-700 dark:text-gray-200"
186202
/>
187-
<div>
188-
<button
189-
type="button"
190-
onClick={() => setShowAdvanced(!showAdvanced)}
191-
className="text-sm text-gray-400 hover:text-gray-300 flex items-center gap-2"
192-
>
193-
<ChevronRight className={`transition-transform ${showAdvanced ? 'rotate-90' : ''}`} size={16} />
194-
Advanced Options
195-
</button>
196-
197-
{showAdvanced && (
198-
<div className="mt-2 space-y-4">
199-
<div>
200-
<label className="block text-gray-300 mb-2">Description</label>
201-
<Textarea
202-
value={comment}
203-
onChange={(e) => setComment(e.target.value)}
204-
placeholder="Add a description for this field..."
205-
rows={3}
206-
/>
207-
</div>
208-
209-
<div>
210-
<label className="block text-gray-300 mb-2">Example</label>
211-
<Input
212-
type="text"
213-
value={example}
214-
onChange={(e) => setExample(e.target.value)}
215-
placeholder={`Enter example ${fieldType} value...`}
216-
/>
217-
<p className="text-xs text-gray-400">
218-
{fieldType === 'object' || fieldType === 'array'
219-
? 'Enter valid JSON for this type'
220-
: `Enter a valid ${fieldType} value`}
221-
</p>
222-
</div>
223-
</div>
224-
)}
225-
</div>
226203
</div>
227-
<div className="flex justify-end gap-2 mt-6">
204+
205+
{/* Advanced Options Section */}
206+
<div className="pt-2">
228207
<button
229208
type="button"
209+
onClick={() => setShowAdvanced(!showAdvanced)}
210+
className="flex items-center gap-2 text-sm font-medium text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100 transition-colors"
211+
>
212+
<ChevronRight
213+
className={`transition-transform duration-150 ${showAdvanced ? 'rotate-90' : ''}`}
214+
size={16}
215+
/>
216+
Advanced Options
217+
</button>
218+
219+
{showAdvanced && (
220+
<div className="mt-6 space-y-6 pl-6">
221+
{/* Description Field */}
222+
<div className="space-y-2">
223+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-200">
224+
Description
225+
</label>
226+
<Textarea
227+
value={comment}
228+
onChange={(e) => setComment(e.target.value)}
229+
placeholder="Add a description for this field..."
230+
rows={3}
231+
className="w-full bg-white dark:bg-gray-900 shadow-sm resize-none"
232+
/>
233+
</div>
234+
235+
{/* Example Field */}
236+
<div className="space-y-2">
237+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-200">
238+
Example Value
239+
</label>
240+
<Input
241+
type="text"
242+
value={example}
243+
onChange={(e) => setExample(e.target.value)}
244+
placeholder={`Enter example ${fieldType} value...`}
245+
className="w-full bg-white dark:bg-gray-900 shadow-sm"
246+
/>
247+
<p className="text-xs text-gray-500 dark:text-gray-400">
248+
Enter a valid {fieldType} value
249+
</p>
250+
</div>
251+
</div>
252+
)}
253+
</div>
254+
255+
{/* Action Buttons */}
256+
<div className="flex justify-end gap-3 pt-6 mt-6">
257+
<Button
258+
variant="outline"
230259
onClick={onClose}
231-
className="px-4 py-2 text-gray-300 hover:bg-gray-700 rounded transition-colors"
260+
type="button"
261+
size="sm"
262+
className="px-4"
232263
>
233264
Cancel
234-
</button>
235-
<button
265+
</Button>
266+
<Button
236267
type="submit"
237-
className="px-4 py-2 bg-blue-500/20 text-blue-300 border border-blue-500/30
238-
hover:bg-blue-500/30 rounded transition-colors"
268+
variant="default"
269+
size="sm"
270+
className="px-4"
271+
onClick={handleSubmit}
239272
>
240-
{editMode ? 'Update Field' : 'Add Field'}
241-
</button>
273+
Add Field
274+
</Button>
242275
</div>
243276
</form>
244277
</DialogContent>

src/components/editor/FieldComponent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const FieldComponent: React.FC<FieldComponentProps> = ({
2929
<div className="flex items-center gap-2">
3030
<div className="flex-grow">
3131
<div className="flex items-center gap-2">
32-
<span className="font-medium text-gray-200">{field.title}</span>
32+
<span className="font-medium">{field.title}</span>
3333
{field.required && (
3434
<span className="text-red-400 text-sm" title="Required field">*</span>
3535
)}
@@ -41,7 +41,7 @@ const FieldComponent: React.FC<FieldComponentProps> = ({
4141
className="text-gray-400 hover:text-gray-300 cursor-help"
4242
/>
4343
<div className="absolute left-1/2 -translate-x-1/2 bottom-full mb-2 hidden group-hover:block">
44-
<div className="bg-gray-900 text-gray-200 text-sm rounded px-2 py-1 whitespace-nowrap">
44+
<div className="bg-gray-900 text-sm rounded px-2 py-1 whitespace-nowrap">
4545
{field.$comment}
4646
</div>
4747
</div>

0 commit comments

Comments
 (0)