import { redirect } from "next/navigation"; import { cookies } from "next/headers"; import { apiFetch, ApiError } from "@/lib/api"; import { BlogPost, User, UserRole, CurrentUser, PostStatus } from "@/lib/types"; import { PostsTable } from "./components/posts-table"; import { UsersTable } from "./components/users-table"; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { FileText, Users, Eye, TrendingUp, CheckCircle2, Clock, Archive, } from "lucide-react"; // ── JWT decode (no verification — display only) ──────────────────────────── function decodeJwtPayload(token: string): CurrentUser | null { try { const base64Payload = token.split(".")[1]; const decoded = Buffer.from(base64Payload, "base64url").toString("utf-8"); return JSON.parse(decoded) as CurrentUser; } catch { return null; } } const rolePill: Record = { [UserRole.ADMIN]: "bg-red-100 text-red-700 border-red-200", [UserRole.MANAGER]: "bg-amber-100 text-amber-700 border-amber-200", [UserRole.MEMBER]: "bg-blue-100 text-blue-700 border-blue-200", }; // ── Stat card ────────────────────────────────────────────────────────────── function StatCard({ icon: Icon, label, value, sub, accent, }: { icon: React.ElementType; label: string; value: number | string; sub?: string; accent?: string; }) { return (

{label}

{value}

{sub && (

{sub}

)}
); } // ── Page ─────────────────────────────────────────────────────────────────── export default async function DashboardPage() { /* Auth */ const cookieStore = await cookies(); const accessToken = cookieStore.get("accessToken")?.value; if (!accessToken) redirect("/auth"); const currentUser = decodeJwtPayload(accessToken); if (!currentUser) redirect("/auth"); /* Posts */ let posts: BlogPost[] = []; try { const data = await apiFetch<{ posts: BlogPost[]; total: number }>( "/blog-posts?pageSize=100" ); posts = data.posts; } catch (err) { if (err instanceof ApiError && err.status === 401) redirect("/auth"); } /* Users (ADMIN only) */ let users: User[] = []; if (currentUser.role === UserRole.ADMIN) { try { const data = await apiFetch<{ users: User[]; total: number }>( "/users?pageSize=100" ); users = data.users; } catch { /* non-fatal */ } } /* Stats */ const published = posts.filter((p) => p.status === PostStatus.PUBLISHED).length; const drafts = posts.filter((p) => p.status === PostStatus.DRAFT).length; const archived = posts.filter((p) => p.status === PostStatus.ARCHIVED).length; const totalViews = posts.reduce((s, p) => s + (p.views ?? 0), 0); const displayName = currentUser.name || currentUser.email; const isAdmin = currentUser.role === UserRole.ADMIN; return (
{/* ── Top header bar ──────────────────────────────────────────────────── */}

Dashboard

Welcome back, {displayName}

{currentUser.role}
{/* ── Stats grid ────────────────────────────────────────────────────── */}
{isAdmin && ( <> )}
{/* Second row for admin — archived + users */} {isAdmin && (
u.isActive).length} active`} />
)} {/* ── Tabs ──────────────────────────────────────────────────────────── */} Posts {posts.length} {isAdmin && ( Users {users.length} )} {/* Posts tab */} {/* Users tab (admin only) */} {isAdmin && ( )} {/* Member read-only notice */} {currentUser.role === UserRole.MEMBER && (

Read-only access.{" "} You can view posts but cannot create, edit, or delete them. Contact an admin to upgrade your role.

)}
); }