Files
evanpage/frontend/app/(main)/dashboard/page.tsx
evan 37cecaa1ce frontend: add bookmark management and homepage navigation
Admin-only /bookmarks page for managing entries; homepage now renders
public bookmarks as a category-grouped navigation grid (empty state
links admin to the manager). Dashboard gains a recent-bookmarks card,
dock and main layout get a bookmark entry for admins, and the
middleware protects /bookmarks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 01:51:55 +08:00

113 lines
3.4 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 { auth } from "@/auth";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Globe, ExternalLink } from "lucide-react";
import Link from "next/link";
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="/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=""
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>
);
}