"use client"; import { useState, useEffect } from "react"; import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Search, } from "lucide-react"; import { Button, buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import type { MaestroStudentsResult } from "@/lib/maestros"; type StudentsSectionProps = { maestroID: number; }; export function StudentsSection({ maestroID }: StudentsSectionProps) { const [q, setQ] = useState(""); const [submittedQ, setSubmittedQ] = useState(""); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [errorMessage, setErrorMessage] = useState(""); useEffect(() => { let cancelled = false; const run = async () => { setIsLoading(true); setErrorMessage(""); try { const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize), q: submittedQ, }); const response = await fetch( `/api/maestros/${maestroID}/students?${params.toString()}` ); if (!response.ok) { throw new Error("학생 목록을 불러오지 못했습니다."); } const result = (await response.json()) as MaestroStudentsResult; if (!cancelled) { setData(result); } } catch (err) { if (!cancelled) { setErrorMessage( err instanceof Error ? err.message : "학생 목록을 불러오지 못했습니다." ); } } finally { if (!cancelled) { setIsLoading(false); } } }; void run(); return () => { cancelled = true; }; }, [maestroID, submittedQ, page, pageSize]); function handleSearch(e: React.FormEvent) { e.preventDefault(); setPage(1); setSubmittedQ(q); } return (

학생 목록

{data ? `전체 ${data.totalCount.toLocaleString()}명${submittedQ ? ` — "${submittedQ}" 검색 결과` : ""}` : "로딩 중..."}

{errorMessage ? (

{errorMessage}

) : ( <> {data ? ( { setPageSize(size); setPage(1); }} page={data.page} pageSize={pageSize} totalPages={data.totalPages} /> ) : null} )}
); } function StudentTable({ result, isLoading, }: { result: MaestroStudentsResult | null; isLoading: boolean; }) { if (isLoading || !result) { return (

로딩 중...

); } return (
{result.items.length > 0 ? ( result.items.map((player) => ( )) ) : ( )}
PlayerID 이름 입장코드
{player.playerID} {player.name} {player.enterCode}
{result.q ? "검색 결과가 없습니다." : "등록된 학생이 없습니다."}
); } function StudentPagination({ page, pageSize, totalPages, onPageChange, onPageSizeChange, }: { page: number; pageSize: number; totalPages: number; onPageChange: (page: number) => void; onPageSizeChange: (pageSize: number) => void; }) { const showNav = totalPages > 1; const paginationItems = buildPaginationItems(page, totalPages); const firstVisiblePage = paginationItems[0] ?? 1; const lastVisiblePage = paginationItems.at(-1) ?? totalPages; const hasFirstPage = paginationItems.includes(1); const hasLastPage = paginationItems.includes(totalPages); const hasPreviousPageGroup = firstVisiblePage > 1; const hasNextPageGroup = lastVisiblePage < totalPages; return (
{showNav ? : null}
); } function buildPaginationItems(currentPage: number, totalPages: number) { const startPage = Math.max(1, currentPage - 2); const endPage = Math.min(totalPages, currentPage + 2); return Array.from( { length: endPage - startPage + 1 }, (_, index) => startPage + index ); }