'use client'; import { useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { clsx } from 'clsx'; import { Calendar, ChevronRight, Rows3, SlidersHorizontal } from 'lucide-react'; import EmptyState from '@/components/ui/EmptyState'; import StatusBadge from '@/components/ui/StatusBadge'; import Surface from '@/components/ui/Surface'; import { Post } from '@/types'; type ArchiveExplorerProps = { posts: Post[]; }; type Density = 'comfortable' | 'compact'; const ALL = 'all'; const getPostDate = (post: Post) => { const date = new Date(post.createdAt); return Number.isNaN(date.getTime()) ? null : date; }; const getMonthKey = (date: Date) => { return (date.getMonth() + 1).toString().padStart(2, '0'); }; const formatDate = (value: string) => { const date = new Date(value); if (Number.isNaN(date.getTime())) return ''; return new Intl.DateTimeFormat('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit', }).format(date); }; const sortNewestFirst = (posts: Post[]) => { return [...posts].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); }; export default function ArchiveExplorer({ posts }: ArchiveExplorerProps) { const [selectedYear, setSelectedYear] = useState(ALL); const [selectedMonth, setSelectedMonth] = useState(ALL); const [selectedCategory, setSelectedCategory] = useState(ALL); const [density, setDensity] = useState('compact'); const years = useMemo(() => { return Array.from(new Set(posts.map(getPostDate).filter(Boolean).map((date) => date!.getFullYear().toString()))) .sort((a, b) => Number(b) - Number(a)); }, [posts]); const monthOptions = useMemo(() => { return Array.from(new Set(posts .map((post) => { const date = getPostDate(post); if (!date) return null; if (selectedYear !== ALL && date.getFullYear().toString() !== selectedYear) return null; return getMonthKey(date); }) .filter(Boolean) as string[])) .sort((a, b) => Number(b) - Number(a)); }, [posts, selectedYear]); const categories = useMemo(() => { return Array.from(new Set(posts.map((post) => post.categoryName || '미분류'))).sort((a, b) => a.localeCompare(b, 'ko-KR')); }, [posts]); useEffect(() => { if (selectedMonth !== ALL && !monthOptions.includes(selectedMonth)) { setSelectedMonth(ALL); } }, [monthOptions, selectedMonth]); const filteredPosts = useMemo(() => { return sortNewestFirst(posts.filter((post) => { const date = getPostDate(post); if (!date) return false; const year = date.getFullYear().toString(); const month = getMonthKey(date); const category = post.categoryName || '미분류'; return ( (selectedYear === ALL || year === selectedYear) && (selectedMonth === ALL || month === selectedMonth) && (selectedCategory === ALL || category === selectedCategory) ); })); }, [posts, selectedCategory, selectedMonth, selectedYear]); const groupedPosts = useMemo(() => { const groups: Record> = {}; filteredPosts.forEach((post) => { const date = getPostDate(post); if (!date) return; const year = date.getFullYear().toString(); const month = getMonthKey(date); groups[year] ??= {}; groups[year][month] ??= []; groups[year][month].push(post); }); return groups; }, [filteredPosts]); const sortedYears = Object.keys(groupedPosts).sort((a, b) => Number(b) - Number(a)); const isCompact = density === 'compact'; return (

조건에 맞는 글 {filteredPosts.length.toLocaleString()}

{sortedYears.length > 0 ? (
{sortedYears.map((year) => { const months = groupedPosts[year]; const sortedMonths = Object.keys(months).sort((a, b) => Number(b) - Number(a)); return (

{year}

{sortedMonths.map((month) => { const monthPosts = months[month]; return (

{Number(month)}월 {monthPosts.length}

{monthPosts.map((post) => (

{post.title}

{post.categoryName || '미분류'} {formatDate(post.createdAt)}
))}
); })}
); })}
) : ( )}
); }