Files
evanpage/frontend/app/(main)/dashboard/page.tsx
root 694b02e848 frontend: rebuild bookmark page with drag-and-drop, search, and theme system
- bookmark management with dnd-kit reordering, bulk edit, search,
  category filter/rename, and meta auto-fetch
- migrate /bookmarks → /dashboard/bookmarks under (main) layout
- homepage redesign with category grid, /-key search, dock tooltips
- theme toggle + use-theme, sonner toasts, alert-dialog/skeleton,
  visual refresh of auth pages

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 22:53:17 +00:00

121 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
import { auth } from "@/auth";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Globe, ExternalLink } from "lucide-react";
import Link from "next/link";
export const metadata: Metadata = {
title: "仪表盘",
};
const SERVER_API_URL = process.env.SERVER_API_URL || "http://backend:8080";
interface Bookmark {
id: number;
title: string;
url: string;
description: string;
icon: string;
category: string;
}
async function fetchPublicBookmarks(): Promise<Bookmark[]> {
try {
const res = await fetch(`${SERVER_API_URL}/api/bookmarks/public`, {
next: { revalidate: 0 },
});
if (!res.ok) return [];
const data = await res.json();
return data.bookmarks?.slice(0, 6) || [];
} catch {
return [];
}
}
export default async function DashboardPage() {
const session = await auth();
const user = session?.user as any;
const isAdmin = user?.role === "admin";
const bookmarks = await fetchPublicBookmarks();
return (
<div className="space-y-4">
<h1 className="text-2xl font-bold">
{user?.name || user?.email}
</h1>
<div className="grid gap-4 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-sm">
<p>
<span className="font-medium"></span>
{user?.name}
</p>
<p>
<span className="font-medium"></span>
{user?.email}
</p>
<p>
<span className="font-medium"></span>
{user?.role}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle></CardTitle>
{isAdmin && (
<Link
href="/dashboard/bookmarks"
className="text-sm text-primary hover:underline"
>
</Link>
)}
</CardHeader>
<CardContent>
{bookmarks.length === 0 ? (
<p className="text-sm text-muted-foreground">
</p>
) : (
<div className="space-y-2">
{bookmarks.map((bm) => (
<a
key={bm.id}
href={bm.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 rounded-lg p-2 text-sm transition-colors hover:bg-muted"
>
<div className="flex size-6 shrink-0 items-center justify-center">
{bm.icon ? (
<img
src={bm.icon}
alt=""
width={16}
height={16}
loading="lazy"
className="size-4 object-contain"
/>
) : (
<Globe className="size-4 text-muted-foreground" />
)}
</div>
<span className="truncate font-medium">{bm.title}</span>
<ExternalLink className="ml-auto size-3 shrink-0 text-muted-foreground" />
</a>
))}
</div>
)}
</CardContent>
</Card>
</div>
</div>
);
}