import { useState, useEffect } from "react" import { Loader2Icon } from "lucide-react" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Card, CardContent } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Badge } from "@/components/ui/badge" import { Skeleton } from "@/components/ui/skeleton" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" import { useAdminBorrows, useSearchBorrows, useAdminReturnBook } from "./hooks" import { usePlatform } from "@/hooks/usePlatform" import { useIsMobile } from "@/hooks/useIsMobile" import { formatDateTime } from "@/lib/formatters" import { getErrorMessage } from "@/lib/errors" import type { BorrowInfoVo } from "@/types/api" export default function AdminBorrowsPage() { const platform = usePlatform() const isMobile = useIsMobile() const sel = platform === "web" ? ({ "data-selectable": true } as const) : ({} as const) const [searchText, setSearchText] = useState("") const [debounced, setDebounced] = useState("") useEffect(() => { const t = setTimeout(() => setDebounced(searchText), 300) return () => clearTimeout(t) }, [searchText]) const isSearching = debounced.length > 0 const allQuery = useAdminBorrows() const searchQuery = useSearchBorrows(debounced) const { data, isLoading, error } = isSearching ? searchQuery : allQuery const returnMutation = useAdminReturnBook() const [returnTarget, setReturnTarget] = useState(null) if (isLoading) return if (error) { return (
加载失败:{getErrorMessage(error)}
) } const list = data ?? [] return (

借阅管理

setSearchText(e.target.value)} className="max-w-sm" />
{list.length === 0 ? (

{isSearching ? "无匹配记录" : "暂无借阅记录"}

) : !isMobile ? ( ID 书名 借阅用户 借阅时间 归还时间 状态 操作 {list.map((r) => ( setReturnTarget(r)} sel={sel} /> ))}
) : (
{list.map((r) => ( setReturnTarget(r)} sel={sel} /> ))}
)} !o && setReturnTarget(null)}> 确认归还 确定要代 {returnTarget?.userBorrowVo.username} 归还《{returnTarget?.bookBorrowVo.name}》吗? 取消 returnMutation.mutateAsync(returnTarget!.id).then(() => setReturnTarget(null))} > {returnMutation.isPending && } 确认归还
) } function BorrowRow({ record, isMobile, returning, onReturn, sel, }: { record: BorrowInfoVo isMobile: boolean returning: boolean onReturn: () => void sel: Record }) { const statusBadge = ( {record.status === "BORROWED" ? "借阅中" : "已归还"} ) const returnButton = record.status === "BORROWED" && ( ) if (!isMobile) { return ( {record.id} {record.bookBorrowVo?.name ?? "-"} {record.userBorrowVo.username} {formatDateTime(record.borrowTime)} {record.returnTime ? formatDateTime(record.returnTime) : "-"} {statusBadge} {returnButton} ) } return ( <>
#{record.id} {statusBadge}
{record.bookBorrowVo?.name ?? "-"}
借阅人:{record.userBorrowVo.username}
借阅:{formatDateTime(record.borrowTime)} {record.returnTime && ` | 归还:${formatDateTime(record.returnTime)}`}
{returnButton &&
{returnButton}
} ) } function AdminBorrowsSkeleton({ isMobile }: { isMobile: boolean }) { if (isMobile) { return (
{Array.from({ length: 4 }).map((_, i) => ( ))}
) } return (
{Array.from({ length: 5 }).map((_, i) => ( ))}
) }