feat: add form schemas, modularize delete components, and migrate authentication to a dedicated database schema

This commit is contained in:
2026-05-28 19:20:07 +07:00 Unverified
parent bcae6ece38
commit b13e3c6abc
11 changed files with 787 additions and 198 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ import Link from "next/link";
import { Button } from "@/components/ui/button";
import { ArrowLeft } from "lucide-react";
import SubmissionDetailClient from "./client";
import { DeleteSubmissionButton } from "../client";
import { DeleteSubmissionButton } from "@/components/delete-buttons";
interface SubmissionDetailPageProps {
params: Promise<{
-106
View File
@@ -1,106 +0,0 @@
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Trash2, Loader2 } from "lucide-react";
import { deleteSubmission, deleteAllSubmissions } from "@/app/actions/submissions";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Spinner } from "@/components/ui/spinner";
export function DeleteSubmissionButton({ id, redirectUrl }: { id: string, redirectUrl?: string }) {
const [loading, setLoading] = useState(false);
const router = useRouter();
const handleDelete = async () => {
setLoading(true);
try {
await deleteSubmission(id);
toast.success("Submission deleted");
if (redirectUrl) {
router.push(redirectUrl);
}
} catch (err: any) {
toast.error(err.message || "Failed to delete submission");
} finally {
setLoading(false);
}
};
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button size="sm" variant="destructive" disabled={loading}>
{loading ? <Spinner /> : <Trash2 className="w-3.5 h-3.5" />}
Delete
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete this submission and remove its data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={handleDelete} className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
export function DeleteAllSubmissionsButton() {
const [loading, setLoading] = useState(false);
const handleDeleteAll = async () => {
setLoading(true);
try {
await deleteAllSubmissions();
toast.success("All submissions deleted");
} catch (err: any) {
toast.error(err.message || "Failed to delete submissions");
} finally {
setLoading(false);
}
};
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" disabled={loading}>
{loading ? <Spinner /> : <Trash2 className="w-4 h-4" />}
Delete All Submissions
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete <strong>ALL</strong> submissions and remove their data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={handleDeleteAll}>
Delete All
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
+1 -2
View File
@@ -4,7 +4,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
import { Button } from "@/components/ui/button";
import Link from "next/link";
import { Eye, Users } from "lucide-react";
import { DeleteSubmissionButton, DeleteAllSubmissionsButton } from "./client";
import { DeleteSubmissionButton, DeleteAllSubmissionsButton } from "@/components/delete-buttons";
export const dynamic = "force-dynamic";
@@ -73,7 +73,6 @@ export default async function ResultsListPage() {
<Link href={`/form/admin/result/${sub.id}`}>
<Button size="sm" variant="outline" className="gap-1">
<Eye className="w-3.5 h-3.5" />
View Answers
</Button>
</Link>
<DeleteSubmissionButton id={sub.id} />
+1 -1
View File
@@ -37,7 +37,7 @@ export function FormLayoutClient({
<Button
variant="ghost"
size="sm"
onClick={() => signOut({ callbackUrl: "/auth/signin" })}
onClick={() => signOut()}
>
<LogOut className="w-4 h-4" />
</Button>