Harden form auth and submission handling

This commit is contained in:
2026-07-09 23:53:40 +07:00 Unverified
parent 2c82d55839
commit 8c0cc4492b
43 changed files with 747 additions and 534 deletions
+16 -12
View File
@@ -39,6 +39,7 @@ import { RichTextEditor } from "@/components/rich-text-editor";
import { FormattedText } from "@/components/formatted-text";
import { ThemeToggle } from "@/components/theme-toggle";
import { DropdownMenuAvatar, DiscordMemberProfile } from "@/components/dropdown-menu-avatar";
import { getErrorMessage } from "@/lib/errors";
interface Question {
id: string;
@@ -84,10 +85,12 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
const formIsOpenRef = useRef(formIsOpen);
const questionsRef = useRef(questions);
formTitleRef.current = formTitle;
formDescriptionRef.current = formDescription;
formIsOpenRef.current = formIsOpen;
questionsRef.current = questions;
useEffect(() => {
formTitleRef.current = formTitle;
formDescriptionRef.current = formDescription;
formIsOpenRef.current = formIsOpen;
questionsRef.current = questions;
}, [formDescription, formIsOpen, formTitle, questions]);
const autoSaveForm = useCallback(() => {
if (formTimerRef.current) clearTimeout(formTimerRef.current);
@@ -170,7 +173,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
options: [],
});
router.refresh();
} catch (err: any) {
} catch (err: unknown) {
console.error("Failed to add question:", err);
}
};
@@ -186,7 +189,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
try {
await deleteQuestion(id, form.id);
router.refresh();
} catch (err: any) {
} catch (err: unknown) {
console.error("Failed to delete question:", err);
}
};
@@ -243,7 +246,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
{
loading: "Uploading image...",
success: "Image uploaded!",
error: (err: any) => err.message || "Failed to upload image.",
error: (err: unknown) => getErrorMessage(err, "Failed to upload image."),
}
);
};
@@ -259,7 +262,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
{
loading: "Removing image...",
success: "Image removed.",
error: (err: any) => err.message || "Failed to remove image.",
error: (err: unknown) => getErrorMessage(err, "Failed to remove image."),
}
);
};
@@ -283,8 +286,9 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
handleUpdateQuestion(question.id, { options });
};
React.useEffect(() => {
setQuestions(initialQuestions);
useEffect(() => {
const timeout = setTimeout(() => setQuestions(initialQuestions), 0);
return () => clearTimeout(timeout);
}, [initialQuestions]);
return (
@@ -360,7 +364,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
<div className="flex items-center gap-2 self-end sm:self-auto">
<Select
value={q.type}
onValueChange={(val: any) => handleUpdateQuestion(q.id, { type: val })}
onValueChange={(val) => handleUpdateQuestion(q.id, { type: val as Question["type"] })}
>
<SelectTrigger className="w-36">
<SelectValue />
@@ -499,7 +503,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC
onCheckedChange={(checked) => handleUpdateQuestion(q.id, { allowOther: checked })}
/>
<Label htmlFor={`allowOther-${q.id}`} className="text-sm font-normal text-muted-foreground">
Add 'Other' option
Add &apos;Other&apos; option
</Label>
</div>
)}