feat(ui): add navigation header and wire up real page routes
- Add sticky header with book management nav links and logout button - Replace placeholder page components with real feature imports - Add login page route to router - Fix date-time formatting for consistent YYYY-MM-DD HH:mm output
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { Loader2Icon } from "lucide-react"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
import { useMyBorrows, useReturnBook } from "./hooks"
|
||||
import { formatDateTime } from "@/lib/formatters"
|
||||
|
||||
export default function MyBorrowsPage() {
|
||||
const { data: records, isLoading, error } = useMyBorrows()
|
||||
const returnBook = useReturnBook()
|
||||
|
||||
if (isLoading) return <MyBorrowsSkeleton />
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="mx-auto max-w-[1200px] px-4 py-8">
|
||||
<div className="rounded-lg border border-destructive/50 bg-destructive/10 px-4 py-3 text-sm text-destructive">
|
||||
加载失败:{error instanceof Error ? error.message : "未知错误"}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const list = records ?? []
|
||||
|
||||
if (list.length === 0) {
|
||||
return (
|
||||
<div className="mx-auto max-w-[1200px] px-4 py-16 text-center text-muted-foreground">
|
||||
<p className="text-lg">暂无借阅记录</p>
|
||||
<p className="mt-1 text-sm">去书目页面借阅你感兴趣的图书吧</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-[1200px] px-4 py-8">
|
||||
<h1 className="mb-4 text-xl font-semibold">我的借阅</h1>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>书名</TableHead>
|
||||
<TableHead>作者</TableHead>
|
||||
<TableHead>借阅时间</TableHead>
|
||||
<TableHead>归还时间</TableHead>
|
||||
<TableHead>状态</TableHead>
|
||||
<TableHead className="w-[100px]">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{list.map((r) => (
|
||||
<TableRow key={r.id}>
|
||||
<TableCell className="font-medium">{r.bookBorrowVo.name}</TableCell>
|
||||
<TableCell>{r.bookBorrowVo.author}</TableCell>
|
||||
<TableCell>{formatDateTime(r.borrowTime)}</TableCell>
|
||||
<TableCell>
|
||||
{r.returnTime ? formatDateTime(r.returnTime) : "-"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={r.status === "BORROWED" ? "default" : "secondary"}>
|
||||
{r.status === "BORROWED" ? "借阅中" : "已归还"}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{r.status === "BORROWED" && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={returnBook.isPending}
|
||||
onClick={() => returnBook.mutate(r.id)}
|
||||
>
|
||||
{returnBook.isPending && <Loader2Icon className="animate-spin" />}
|
||||
归还
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function MyBorrowsSkeleton() {
|
||||
return (
|
||||
<div className="mx-auto max-w-[1200px] px-4 py-8">
|
||||
<Skeleton className="mb-4 h-7 w-24" />
|
||||
<div className="space-y-2">
|
||||
{Array.from({ length: 5 }).map((_, i) => (
|
||||
<Skeleton key={i} className="h-10 w-full" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user