feat:update form

This commit is contained in:
2026-07-28 00:02:34 +07:00 Unverified
parent 0e8c816276
commit fa35f00f38
25 changed files with 919 additions and 149 deletions
+72 -12
View File
@@ -61,6 +61,12 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
interface Question {
id: string;
@@ -72,6 +78,7 @@ interface Question {
required: boolean;
options: string[] | null;
allowOther: boolean;
imageAnswerMode: "none" | "optional" | "required";
}
interface FormEditorClientProps {
@@ -80,6 +87,7 @@ interface FormEditorClientProps {
title: string;
description: string | null;
isOpen: boolean;
allowMultipleSubmissions: boolean;
};
initialQuestions: Question[];
}
@@ -89,6 +97,9 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
const [formTitle, setFormTitle] = useState(form.title);
const [formDescription, setFormDescription] = useState(form.description || "");
const [formIsOpen, setFormIsOpen] = useState(form.isOpen);
const [allowMultipleSubmissions, setAllowMultipleSubmissions] = useState(
form.allowMultipleSubmissions
);
const [questions, setQuestions] = useState<Question[]>(initialQuestions);
const [uploadingId, setUploadingId] = useState<string | null>(null);
const [addingQuestion, setAddingQuestion] = useState(false);
@@ -97,14 +108,16 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
const formTitleRef = useRef(formTitle);
const formDescriptionRef = useRef(formDescription);
const formIsOpenRef = useRef(formIsOpen);
const allowMultipleSubmissionsRef = useRef(allowMultipleSubmissions);
const questionsRef = useRef(questions);
useEffect(() => {
formTitleRef.current = formTitle;
formDescriptionRef.current = formDescription;
formIsOpenRef.current = formIsOpen;
allowMultipleSubmissionsRef.current = allowMultipleSubmissions;
questionsRef.current = questions;
}, [formDescription, formIsOpen, formTitle, questions]);
}, [allowMultipleSubmissions, formDescription, formIsOpen, formTitle, questions]);
const saveForm = useCallback(async () => {
const title = formTitleRef.current;
@@ -115,6 +128,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
title: title.trim(),
description: formDescriptionRef.current,
isOpen: formIsOpenRef.current,
allowMultipleSubmissions: allowMultipleSubmissionsRef.current,
});
router.refresh();
}, [form.id, router]);
@@ -125,6 +139,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
...q,
options: q.options === null ? [] : q.options,
allowOther: q.allowOther,
imageAnswerMode: q.imageAnswerMode,
}));
await bulkUpdateQuestions(form.id, formattedUpdates);
router.refresh();
@@ -154,6 +169,12 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
formAutoSave.schedule();
};
const handleAllowMultipleSubmissionsChange = (val: boolean) => {
allowMultipleSubmissionsRef.current = val;
setAllowMultipleSubmissions(val);
formAutoSave.schedule();
};
const handleAddQuestion = async () => {
setAddingQuestion(true);
try {
@@ -334,9 +355,10 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
/>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="form-title">Form Title</Label>
<CardContent>
<FieldGroup>
<Field>
<FieldLabel htmlFor="form-title">Form Title</FieldLabel>
<Input
id="form-title"
value={formTitle}
@@ -347,26 +369,42 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
{!formTitle.trim() && (
<p className="text-sm text-destructive">Form title is required.</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="form-description">Description</Label>
</Field>
<Field>
<FieldLabel htmlFor="form-description">Description</FieldLabel>
<RichTextEditor
value={formDescription}
onChange={(html) => handleFormDescriptionChange(html)}
placeholder="Provide a description..."
className="min-h-20 w-full"
/>
</div>
<div className="flex items-center space-x-2 pt-2">
</Field>
<Field orientation="horizontal">
<Switch
id="form-is-open"
checked={formIsOpen}
onCheckedChange={handleFormIsOpenChange}
/>
<Label htmlFor="form-is-open" className="text-sm font-normal text-muted-foreground">
<FieldLabel htmlFor="form-is-open" className="font-normal">
Accepting Submissions
</Label>
</div>
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Switch
id="allow-multiple-submissions"
checked={allowMultipleSubmissions}
onCheckedChange={handleAllowMultipleSubmissionsChange}
/>
<div className="flex flex-col gap-1">
<FieldLabel htmlFor="allow-multiple-submissions" className="font-normal">
Allow multiple submissions
</FieldLabel>
<FieldDescription>
Users can create separate responses instead of only editing their current response.
</FieldDescription>
</div>
</Field>
</FieldGroup>
</CardContent>
</Card>
@@ -566,6 +604,28 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
Required
</Label>
</div>
<Field className="w-full max-w-xs">
<FieldLabel>Answer image</FieldLabel>
<Select
value={q.imageAnswerMode}
onValueChange={(val) =>
handleUpdateQuestion(q.id, {
imageAnswerMode: val as Question["imageAnswerMode"],
})
}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="none">Off</SelectItem>
<SelectItem value="optional">Optional</SelectItem>
<SelectItem value="required">Required</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</Field>
</div>
{(q.type === "radio" || q.type === "checkbox") && (