diff --git a/components/FormEditor.tsx b/components/FormEditor.tsx index 7962eb4..5f5b67b 100644 --- a/components/FormEditor.tsx +++ b/components/FormEditor.tsx @@ -1,13 +1,27 @@ "use client"; + import React, { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Trash2, Plus, Save, ArrowUp, ArrowDown } from "lucide-react"; import toast from "react-hot-toast"; +import { useAutoSaveDraft } from "@/hooks/useAutoSaveDraft"; + type FieldType = { id: string; label: string; @@ -44,21 +58,41 @@ const fieldTypes = [ const FormEditor: React.FC = ({ form, onSave }) => { const [formData, setFormData] = useState({ formTitle: "", - formFields: [] + formFields: [], }); + const draftKey = `genform-draft-${form?.id || "new"}`; + + const { status, loadDraft, clearDraft } = useAutoSaveDraft( + draftKey, + formData + ); + useEffect(() => { + const savedDraft = loadDraft(); + + if (savedDraft) { + setFormData(savedDraft); + toast.success("Draft restored"); + return; + } + if (form) { - const parsedContent = typeof form.content === 'string' ? JSON.parse(form.content) : form.content; + const parsedContent = + typeof form.content === "string" + ? JSON.parse(form.content) + : form.content; + setFormData({ formTitle: parsedContent.formTitle || "", - formFields: parsedContent.formFields?.map((field: any, index: number) => ({ - id: `field-${index}`, - ...field - })) || [] + formFields: + parsedContent.formFields?.map((field: any, index: number) => ({ + id: `field-${index}`, + ...field, + })) || [], }); } - }, [form]); + }, [form, loadDraft]); const addField = () => { const newField: FieldType = { @@ -67,45 +101,45 @@ const FormEditor: React.FC = ({ form, onSave }) => { name: `field_${formData.formFields.length + 1}`, type: "text", placeholder: "Enter value", - required: false + required: false, }; - setFormData(prev => ({ + setFormData((prev) => ({ ...prev, - formFields: [...prev.formFields, newField] + formFields: [...prev.formFields, newField], })); }; const updateField = (fieldId: string, updates: Partial) => { - setFormData(prev => ({ + setFormData((prev) => ({ ...prev, - formFields: prev.formFields.map(field => + formFields: prev.formFields.map((field) => field.id === fieldId ? { ...field, ...updates } : field - ) + ), })); }; const deleteField = (fieldId: string) => { - setFormData(prev => ({ + setFormData((prev) => ({ ...prev, - formFields: prev.formFields.filter(field => field.id !== fieldId) + formFields: prev.formFields.filter((field) => field.id !== fieldId), })); }; - const moveField = (fieldId: string, direction: 'up' | 'down') => { - const currentIndex = formData.formFields.findIndex(field => field.id === fieldId); - if (currentIndex === -1) return; + const moveField = (fieldId: string, direction: "up" | "down") => { + const index = formData.formFields.findIndex((f) => f.id === fieldId); + if (index === -1) return; - const newIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1; + const newIndex = direction === "up" ? index - 1 : index + 1; if (newIndex < 0 || newIndex >= formData.formFields.length) return; - const items = Array.from(formData.formFields); - const [movedItem] = items.splice(currentIndex, 1); - items.splice(newIndex, 0, movedItem); + const items = [...formData.formFields]; + const [moved] = items.splice(index, 1); + items.splice(newIndex, 0, moved); - setFormData(prev => ({ + setFormData((prev) => ({ ...prev, - formFields: items + formFields: items, })); }; @@ -120,272 +154,228 @@ const FormEditor: React.FC = ({ form, onSave }) => { return; } - // Validate fields - for (const field of formData.formFields) { - if (!field.label.trim() || !field.name.trim()) { - toast.error("All fields must have a label and name"); - return; - } - } - onSave(formData); + clearDraft(); + toast.success("Form saved successfully"); }; - const addOption = (fieldId: string) => { - updateField(fieldId, { - options: [ - ...(formData.formFields.find(f => f.id === fieldId)?.options || []), - "New Option" - ] - }); - }; - - const updateOption = (fieldId: string, optionIndex: number, value: string) => { - const field = formData.formFields.find(f => f.id === fieldId); - if (field?.options) { - const newOptions = [...field.options]; - newOptions[optionIndex] = value; - updateField(fieldId, { options: newOptions }); - } - }; - - const deleteOption = (fieldId: string, optionIndex: number) => { - const field = formData.formFields.find(f => f.id === fieldId); - if (field?.options) { - const newOptions = field.options.filter((_, index) => index !== optionIndex); - updateField(fieldId, { options: newOptions }); - } - }; - - const needsOptions = (type: string) => ["select", "radio", "checkbox"].includes(type); - const needsPlaceholder = (type: string) => !["select", "radio", "checkbox", "date", "time", "datetime-local"].includes(type); - return (
- {/* Form Title */} Form Settings + -
-
- - setFormData(prev => ({ ...prev, formTitle: e.target.value }))} - placeholder="Enter form title" - className="mt-1" - /> -
-
+ + + setFormData((prev) => ({ + ...prev, + formTitle: e.target.value, + })) + } + placeholder="Enter form title" + />
- {/* Fields Editor */} - - Form Fields ({formData.formFields.length}) + + Form Fields - -
- {formData.formFields.map((field, index) => ( - - -
-
- - -
- -
-
-
- - updateField(field.id, { label: e.target.value })} - placeholder="Field label" - /> -
-
- - updateField(field.id, { name: e.target.value })} - placeholder="field_name" - /> -
-
- - -
-
- -
- {needsPlaceholder(field.type) && ( -
- - updateField(field.id, { placeholder: e.target.value })} - placeholder="Enter placeholder text" - /> -
- )} -
- updateField(field.id, { required: e.target.checked })} - className="rounded" - /> - -
-
- - {/* Options for select, radio, checkbox */} - {needsOptions(field.type) && ( -
-
- - -
-
- {field.options?.map((option, optionIndex) => ( -
- updateOption(field.id, optionIndex, e.target.value)} - placeholder={`Option ${optionIndex + 1}`} - /> - -
- )) || []} -
-
- )} -
- - -
-
-
- ))} - {formData.formFields.length === 0 && ( -
- No fields added yet. Click "Add Field" to get started. -
- )} -
+ + {formData.formFields.map((field, index) => ( + + + {/* label + controls row */} +
+ + updateField(field.id, { label: e.target.value }) + } + placeholder="Field Label" + /> + + + + + + +
+ + {/* type selector */} +
+ + +
+ + {/* placeholder input */} +
+ + + updateField(field.id, { placeholder: e.target.value }) + } + placeholder="Placeholder" + /> +
+ + {/* required toggle */} +
+ + updateField(field.id, { required: e.target.checked }) + } + /> + +
+ + {/* options for select/radio/checkbox */} + {["select", "radio", "checkbox"].includes(field.type) && ( +
+ + {(field.options || []).map((option, idx) => ( + { + const newOptions = [...(field.options || [])]; + newOptions[idx] = e.target.value; + updateField(field.id, { options: newOptions }); + }} + /> + ))} + +
+ )} +
+
+ ))}
- {/* Preview Section */} + {/* live preview card */} - Form Preview + Live Preview -
-

- {formData.formTitle || "Untitled Form"} -

- {formData.formFields.length > 0 ? ( -
- {formData.formFields.map((field) => ( -
- - {field.type === "textarea" ? ( -
- {field.placeholder || "Textarea field"} -
- ) : field.type === "select" ? ( -
- Select {field.options?.[0] || "option"}... -
- ) : ( -
- {field.placeholder || `${field.type} field`} -
- )} -
- ))} -
- ) : ( -

No fields added yet

- )} -
+ {formData.formFields.map((field) => ( +
+ + + {field.type === "textarea" && ( +