'use client'; import { Suspense, useMemo, useState } from 'react'; import Link from 'next/link'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; import Image from 'next/image'; import { useQuery } from '@tanstack/react-query'; import { clsx } from 'clsx'; import { Archive, ChevronRight, Crown, FileQuestion, Folder, FolderOpen, Github, Mail, Menu, X, } from 'lucide-react'; import { getCategories } from '@/api/category'; import { getProfile } from '@/api/profile'; import PostSearch from '@/components/post/PostSearch'; import { Category, Profile } from '@/types'; interface CategoryItemProps { category: Category; depth: number; onNavigate: () => void; pathname: string; } const defaultProfile: Profile = { name: 'Dev Park', bio: '개발자', imageUrl: 'https://api.dicebear.com/7.x/notionists/svg?seed=Felix', githubUrl: 'https://github.com', email: 'user@example.com', }; const sortCategories = (categories: Category[] = []) => { return [...categories].sort((a, b) => a.id - b.id); }; function CategoryItem({ category, depth, onNavigate, pathname }: CategoryItemProps) { const sortedChildren = useMemo(() => sortCategories(category.children), [category.children]); const hasChildren = sortedChildren.length > 0; const isActive = decodeURIComponent(pathname) === `/category/${category.name}`; const hasActiveChild = useMemo(() => { const check = (categories: Category[] | undefined): boolean => { if (!categories) return false; return categories.some((child) => { return decodeURIComponent(pathname) === `/category/${child.name}` || check(child.children); }); }; return check(category.children); }, [category.children, pathname]); const [isExpanded, setIsExpanded] = useState(isActive || hasActiveChild); const isChildrenVisible = isExpanded || isActive || hasActiveChild; return (
{isActive ? : } {category.name} {hasChildren && ( )}
{isChildrenVisible && hasChildren && (
{sortedChildren.map((child) => ( ))}
)}
); } function SidebarContent() { const [isOpen, setIsOpen] = useState(false); const pathname = usePathname(); const router = useRouter(); const searchParams = useSearchParams(); const keyword = searchParams.get('keyword') || ''; const { data: categories } = useQuery({ queryKey: ['categories'], queryFn: getCategories, }); const sortedCategories = useMemo(() => sortCategories(categories), [categories]); const { data: profile, isLoading: isProfileLoading } = useQuery({ queryKey: ['profile'], queryFn: getProfile, retry: 0, }); const displayProfile = profile ? { ...defaultProfile, ...profile } : defaultProfile; const closeSidebar = () => setIsOpen(false); const handleSearch = (newKeyword: string) => { const trimmedKeyword = newKeyword.trim(); if (trimmedKeyword) { router.push(`/?keyword=${encodeURIComponent(trimmedKeyword)}`); closeSidebar(); return; } router.push('/'); closeSidebar(); }; return ( <>