import type { JSX } from "react" import { Suspense } from "react" import { BookX, Clock, GraduationCap, Repeat, Sparkles } from "lucide-react" import { getTranslations } from "next-intl/server" import { requirePermission } from "@/shared/lib/auth-guard" import { Permissions } from "@/shared/types/permissions" import { getParam, type SearchParams } from "@/shared/lib/search-params" import { Skeleton } from "@/shared/components/ui/skeleton" import { StatsGrid } from "@/shared/components/ui/stats-grid" import { WidgetBoundary } from "@/shared/components/widget-boundary" import { getErrorBookItems, getErrorBookStats } from "@/modules/error-book/data-access" import type { ErrorBookStatusValue, ErrorBookSourceTypeValue } from "@/modules/error-book/types" import { AiClientProvider, } from "@/modules/ai/context/ai-client-provider" import { createCoreAiClientService } from "@/modules/ai/context/create-ai-client-service" import { StudentErrorBookListClient } from "./student-error-book-list-client" import { AddErrorBookDialogWithQuestions } from "./add-error-book-dialog-with-questions" import { ErrorBookFilters } from "./error-book-filters" export const dynamic = "force-dynamic" const VALID_STATUS = new Set(["new", "learning", "mastered", "archived"]) const VALID_SOURCE = new Set(["exam", "homework", "manual"]) function isErrorBookStatus(v: string): v is ErrorBookStatusValue { return VALID_STATUS.has(v) } function isErrorBookSource(v: string): v is ErrorBookSourceTypeValue { return VALID_SOURCE.has(v) } function parseStatus(v?: string): ErrorBookStatusValue | undefined { if (!v || !isErrorBookStatus(v)) return undefined return v } function parseSource(v?: string): ErrorBookSourceTypeValue | undefined { if (!v || !isErrorBookSource(v)) return undefined return v } async function ErrorBookResults({ searchParams }: { searchParams: Promise }): Promise { const params = await searchParams const ctx = await requirePermission(Permissions.ERROR_BOOK_READ) const q = getParam(params, "q") const status = parseStatus(getParam(params, "status")) const sourceType = parseSource(getParam(params, "source")) const dueOnly = getParam(params, "due") === "due" const { data: items } = await getErrorBookItems({ studentId: ctx.userId, q: q || undefined, status, sourceType, dueOnly, pageSize: 50, }) return } function ErrorBookResultsFallback() { return (
{Array.from({ length: 4 }).map((_, idx) => ( ))}
) } export default async function StudentErrorBookPage({ searchParams, }: { searchParams: Promise }): Promise { const ctx = await requirePermission(Permissions.ERROR_BOOK_READ) const t = await getTranslations("student") const stats = await getErrorBookStats(ctx.userId) const tStats = await getTranslations("errorBook") const masteredPercent = stats.totalCount > 0 ? Math.round(stats.masteredRate * 100) : 0 const aiClientService = createCoreAiClientService() return (

{t("errorBook.title")}

{t("errorBook.description")}

0 }, ]} />
}> }>
) }