Files
spsays-admin/app/(dashboard)/quizzes/page.tsx
T
2026-10-16 15:45:29 +05:30

113 lines
3.6 KiB
TypeScript

"use client";
/**
* /quizzes — every quiz, with how many people have started and finished it.
*/
import { useEffect, useState } from "react";
import Link from "next/link";
import { toast } from "sonner";
import { quizzes, AdminError, type Quiz } from "@/lib/api";
import { Button } from "@/components/ui/button";
import { Table, THead, TH, TR, TD } from "@/components/ui/table";
import { Can } from "@/components/staff-context";
import { fmtDate } from "@/lib/utils";
import { KIND_LABEL } from "./labels";
import { Clamp } from "@/components/ui/clamp";
export default function QuizzesPage() {
const [list, setList] = useState<Quiz[] | null>(null);
useEffect(() => {
quizzes
.list()
.then(setList)
.catch((err) => {
toast.error(
err instanceof AdminError ? err.message : "Could not load quizzes",
);
setList([]);
});
}, []);
return (
<div className="space-y-6">
<header className="flex items-start justify-between gap-4">
<div>
<h1 className="text-xl font-semibold text-foreground">Quizzes</h1>
<p className="mt-1 text-sm text-muted-foreground">
Quizzes on book chapters, lectures and conversations — or on their
own. A published quiz shows a button on its page and appears in the
app&apos;s quiz list.
</p>
</div>
<Can perm="quizzes:write">
<Link href="/quizzes/new" className="shrink-0">
<Button className="whitespace-nowrap">New quiz</Button>
</Link>
</Can>
</header>
{list === null ? (
<p className="text-sm text-muted-foreground">Loading…</p>
) : list.length === 0 ? (
<p className="text-sm text-muted-foreground">No quizzes yet.</p>
) : (
<Table>
<THead>
<TR>
<TH>Quiz</TH>
<TH>Attached to</TH>
<TH>Questions</TH>
<TH>Taken</TH>
<TH>Status</TH>
</TR>
</THead>
<tbody>
{list.map((q) => (
<TR key={q.id}>
<TD>
<Link
href={`/quizzes/${q.id}`}
className="block font-medium text-foreground hover:underline"
>
<Clamp lines={2}>{q.title}</Clamp>
</Link>
<p className="text-xs text-muted-foreground">
Updated {fmtDate(q.updated_at)}
</p>
</TD>
<TD>
<p className="text-sm">{KIND_LABEL[q.target_kind]}</p>
{q.target_title && (
<Clamp className="text-xs text-muted-foreground">
{q.target_title}
</Clamp>
)}
</TD>
<TD>{q.question_count}</TD>
<TD className="[overflow-wrap:normal]">
{q.completed_count ?? 0} finished
<span className="text-muted-foreground">
{" "}
/ {q.attempt_count ?? 0} started
</span>
</TD>
<TD>
{q.published_at ? (
<span className="rounded bg-primary/10 px-2 py-0.5 text-xs text-primary whitespace-nowrap">
Published
</span>
) : (
<span className="text-xs text-muted-foreground">Draft</span>
)}
</TD>
</TR>
))}
</tbody>
</Table>
)}
</div>
);
}