refactor(problemset): move components to separate files and improve structure

- Move ProblemsetHeader component from 'problemset-header' to 'header'
- Extract problemset table logic into dedicated ProblemsetTable component
- Add Suspense and skeleton loading for better UX
- Update layout and page structure
This commit is contained in:
cfngc4594 2025-05-13 16:03:46 +08:00
parent 0e3daefe48
commit 148ae677d7
2 changed files with 11 additions and 69 deletions

View File

@ -1,4 +1,4 @@
import { ProblemsetHeader } from "@/features/problemset/components/problemset-header"; import { ProblemsetHeader } from "@/features/problemset/components/header";
interface ProblemsetLayoutProps { interface ProblemsetLayoutProps {
children: React.ReactNode; children: React.ReactNode;

View File

@ -1,73 +1,15 @@
import Link from "next/link"; import { Suspense } from "react";
import { import {
Table, ProblemsetTable,
TableBody, ProblemsetTableSkeleton,
TableCell, } from "@/features/problemset/components/table";
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import prisma from "@/lib/prisma";
import { auth } from "@/lib/auth";
import { getTranslations } from "next-intl/server";
import { getDifficultyColorClass } from "@/lib/utils";
import { CircleCheckBigIcon, CircleDotIcon } from "lucide-react";
export default async function ProblemsetPage() {
const problems = await prisma.problem.findMany({
where: { published: true },
orderBy: { id: "asc" },
select: { id: true, title: true, difficulty: true },
});
const session = await auth();
const userId = session?.user?.id;
const submissions = userId
? await prisma.submission.findMany({
where: { userId },
select: { problemId: true, status: true },
})
: [];
const completedProblems = new Set(submissions.filter(s => s.status === "AC").map(s => s.problemId));
const attemptedProblems = new Set(submissions.filter(s => s.status !== "AC").map(s => s.problemId));
const t = await getTranslations();
export default function ProblemsetPage() {
return ( return (
<Table> <div className="h-full container mx-auto p-4">
<TableHeader className="bg-transparent"> <Suspense fallback={<ProblemsetTableSkeleton />}>
<TableRow className="hover:bg-transparent"> <ProblemsetTable />
<TableHead className="w-1/3">{t("ProblemsetPage.Status")}</TableHead> </Suspense>
<TableHead className="w-1/3">{t("ProblemsetPage.Title")}</TableHead> </div>
<TableHead className="w-1/3">{t("ProblemsetPage.Difficulty")}</TableHead>
</TableRow>
</TableHeader>
<TableBody className="[&_td:first-child]:rounded-l-lg [&_td:last-child]:rounded-r-lg">
{problems.map((problem, index) => (
<TableRow
key={problem.id}
className="h-10 border-b-0 odd:bg-muted/50 hover:text-blue-500 hover:bg-muted"
>
<TableCell className="py-2.5">
{userId && (completedProblems.has(problem.id) ? (
<CircleCheckBigIcon className="text-green-500" size={18} aria-hidden="true" />
) : attemptedProblems.has(problem.id) ? (
<CircleDotIcon className="text-yellow-500" size={18} aria-hidden="true" />
) : null)}
</TableCell>
<TableCell className="py-2.5">
<Link href={`/problems/${problem.id}`} className="hover:text-blue-500" prefetch>
{index + 1}. {problem.title}
</Link>
</TableCell>
<TableCell className={`py-2.5 ${getDifficultyColorClass(problem.difficulty)}`}>
{t(`Difficulty.${problem.difficulty}`)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
); );
} }