'use client'; import Link from 'next/link'; import { useMemo, useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { ArrowRight, CalendarClock, FileText, Loader2, PenLine, Settings, } from 'lucide-react'; import { getAdminDashboard } from '@/api/dashboard'; import { getCategories } from '@/api/category'; import { getAdminComments } from '@/api/comments'; import { getPosts } from '@/api/posts'; import AdminDashboardActionCenter from '@/components/admin/dashboard/AdminDashboardActionCenter'; import AdminDashboardCategoryHealth from '@/components/admin/dashboard/AdminDashboardCategoryHealth'; import AdminDashboardOverview from '@/components/admin/dashboard/AdminDashboardOverview'; import AdminDashboardPostPerformance from '@/components/admin/dashboard/AdminDashboardPostPerformance'; import AdminDashboardTrafficChart from '@/components/admin/dashboard/AdminDashboardTrafficChart'; import EmptyState from '@/components/ui/EmptyState'; import StatusBadge from '@/components/ui/StatusBadge'; import Surface from '@/components/ui/Surface'; import { AdminComment, AdminCommentListResponse, Category, DashboardPostStat, DashboardRange, PageMeta, Post, } from '@/types'; const countCategories = (categories: Category[] = []): number => { return categories.reduce((count, category) => { return count + 1 + countCategories(category.children || []); }, 0); }; const emptyPageMeta: PageMeta = { totalPages: 0, totalElements: 0, number: 0, last: true, }; const formatDate = (value?: string | Date, withTime = false) => { if (!value) return '-'; const date = value instanceof Date ? value : new Date(value); if (Number.isNaN(date.getTime())) return '-'; return new Intl.DateTimeFormat('ko-KR', { year: 'numeric', month: 'short', day: 'numeric', ...(withTime ? { hour: '2-digit', minute: '2-digit' } : {}), }).format(date); }; const getCommentListMeta = (data?: AdminCommentListResponse): PageMeta => { if (!data) return emptyPageMeta; return { totalElements: data.page?.totalElements ?? data.totalElements ?? 0, totalPages: data.page?.totalPages ?? data.totalPages ?? 0, number: data.page?.number ?? data.number ?? 0, last: data.page?.last ?? data.last, }; }; const getPostTotal = (data?: { page?: PageMeta; totalElements?: number }) => { return data?.page?.totalElements ?? data?.totalElements ?? 0; }; const getAuthorName = (comment: AdminComment) => { return comment.author || comment.memberNickname || comment.guestNickname || '익명'; }; const toPostStat = (post: Post): DashboardPostStat => ({ id: post.id, title: post.title, slug: post.slug, categoryName: post.categoryName, viewCount: post.viewCount, rangeViewCount: post.viewCount, createdAt: post.createdAt, }); function RecentPostRow({ post }: { post: Post }) { return (
{post.title}
{post.categoryName || '미분류'} · {formatDate(post.createdAt)}
{comment.content}
{getAuthorName(comment)} · {comment.postTitle || '게시글 정보 없음'} · {formatDate(comment.createdAt, true)}
발행 흐름을 빠르게 확인합니다.
새 반응을 대시보드에서 바로 봅니다.
공개 화면과 분리된 운영 공간입니다. 통계 API가 준비되기 전에도 기존 데이터로 운영 흐름을 확인합니다.