From 7084806aac58b1ce168eadda32553bb955279756 Mon Sep 17 00:00:00 2001 From: dmitriylaukhin Date: Tue, 19 May 2026 14:29:47 +0500 Subject: [PATCH] Add search, filters, pagination to homework list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace client HomeworkFilters component with server-side
- Switch search param from `search` to `q`, add lesson title to search scope - Change status filter from DB status field to feedback-count logic (pending=no feedbacks, reviewed=has feedbacks) - Update pagination label to "Страница X из Y · Всего: N" - Preserve all existing submission links and layout Co-Authored-By: Claude Sonnet 4.6 --- src/app/curator/homework/page.tsx | 113 ++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 31 deletions(-) diff --git a/src/app/curator/homework/page.tsx b/src/app/curator/homework/page.tsx index 87752df..af512ee 100644 --- a/src/app/curator/homework/page.tsx +++ b/src/app/curator/homework/page.tsx @@ -1,13 +1,12 @@ import { prisma } from "@/lib/prisma"; import Link from "next/link"; -import { HomeworkFilters } from "@/components/admin/homework-filters"; import { Suspense } from "react"; const PAGE_SIZE = 20; interface Props { searchParams: Promise<{ - search?: string; + q?: string; status?: string; courseId?: string; page?: string; @@ -15,20 +14,22 @@ interface Props { } export default async function HomeworkListPage({ searchParams }: Props) { - const { search = "", status = "", courseId = "", page = "1" } = await searchParams; - const currentPage = Math.max(1, parseInt(page) || 1); + const sp = await searchParams; + const q = sp.q ?? ""; + const status = sp.status ?? ""; + const courseId = sp.courseId ?? ""; + const currentPage = Math.max(1, parseInt(sp.page ?? "1") || 1); const skip = (currentPage - 1) * PAGE_SIZE; // Build where clause const where = { - ...(search + ...(q ? { - user: { - OR: [ - { name: { contains: search, mode: "insensitive" as const } }, - { email: { contains: search, mode: "insensitive" as const } }, - ], - }, + OR: [ + { user: { name: { contains: q, mode: "insensitive" as const } } }, + { user: { email: { contains: q, mode: "insensitive" as const } } }, + { homework: { lesson: { title: { contains: q, mode: "insensitive" as const } } } }, + ], } : {}), ...(courseId @@ -38,10 +39,8 @@ export default async function HomeworkListPage({ searchParams }: Props) { }, } : {}), - ...(status === "pending" ? { status: "PENDING" } : {}), - ...(status === "reviewing" ? { status: "REVIEWING" } : {}), - ...(status === "approved" ? { status: "APPROVED" } : {}), - ...(status === "rejected" ? { status: "REJECTED" } : {}), + ...(status === "pending" ? { feedbacks: { none: {} } } : {}), + ...(status === "reviewed" ? { feedbacks: { some: {} } } : {}), }; const [submissions, total, courses] = await Promise.all([ @@ -71,17 +70,25 @@ export default async function HomeworkListPage({ searchParams }: Props) { const totalPages = Math.ceil(total / PAGE_SIZE); - const pendingCount = submissions.filter((s) => s.status === "PENDING").length; - function pageUrl(p: number) { const params = new URLSearchParams(); - if (search) params.set("search", search); + if (q) params.set("q", q); if (status) params.set("status", status); if (courseId) params.set("courseId", courseId); params.set("page", String(p)); return `/curator/homework?${params.toString()}`; } + const inputStyle: React.CSSProperties = { + border: "2px solid var(--border)", + background: "var(--background)", + outline: "none", + padding: "0.4rem 0.75rem", + fontSize: "0.8rem", + fontFamily: "inherit", + color: "var(--foreground)", + }; + return (
@@ -92,12 +99,59 @@ export default async function HomeworkListPage({ searchParams }: Props) { Домашние задания

- {total} {total === 1 ? "работа" : "работ"} · {pendingCount} ожидают проверки + {total} {total === 1 ? "работа" : "работ"}

+ {/* Filters */} - + + + + + + + + + + {(q || status || courseId) && ( + + Сбросить + + )} + {submissions.length === 0 ? ( @@ -111,19 +165,16 @@ export default async function HomeworkListPage({ searchParams }: Props) { ) : (
{submissions.map((s) => { - const statusMap: Record = { - PENDING: { label: "Новое", bg: "var(--foreground)", color: "var(--background)", border: "var(--foreground)" }, - REVIEWING: { label: "На рассмотрении", bg: "oklch(0.9 0.08 80)", color: "oklch(0.4 0.1 80)", border: "oklch(0.75 0.1 80)" }, - APPROVED: { label: "Одобрено", bg: "oklch(0.88 0.1 145)", color: "oklch(0.35 0.15 145)", border: "oklch(0.7 0.15 145)" }, - REJECTED: { label: "Отклонено", bg: "oklch(0.9 0.06 27)", color: "oklch(0.45 0.2 27)", border: "oklch(0.75 0.1 27)" }, - }; - const st = statusMap[s.status] ?? statusMap.PENDING; + const hasReview = s.feedbacks.length > 0; + const reviewBadge = hasReview + ? { label: "С отзывом", bg: "oklch(0.88 0.1 145)", color: "oklch(0.35 0.15 145)" } + : { label: "Без ответа", bg: "var(--foreground)", color: "var(--background)" }; return (

{s.user.name}

@@ -137,9 +188,9 @@ export default async function HomeworkListPage({ searchParams }: Props) {
- {st.label} + {reviewBadge.label}

{new Date(s.submittedAt).toLocaleDateString("ru-RU")} @@ -186,7 +237,7 @@ export default async function HomeworkListPage({ searchParams }: Props) { → )} - стр. {currentPage} из {totalPages} + Страница {currentPage} из {totalPages} · Всего: {total}

)}