import { useState, useEffect, useCallback } from 'react'; import { useAuthStore } from '@common/store/authStore'; import { fetchBoardPosts, type BoardPostItem } from '../services/boardApi'; // 카테고리 코드 ↔ 표시명 매핑 const CATEGORY_MAP: Record = { NOTICE: '공지사항', DATA: '자료실', QNA: 'Q&A', MANUAL: '해경매뉴얼', }; const CATEGORY_FILTER: { label: string; code: string | null }[] = [ { label: '전체', code: null }, { label: '공지사항', code: 'NOTICE' }, { label: '자료실', code: 'DATA' }, { label: 'Q&A', code: 'QNA' }, ]; const CATEGORY_STYLE: Record = { NOTICE: 'bg-red-500/20 text-red-400', DATA: 'bg-blue-500/20 text-blue-400', QNA: 'bg-green-500/20 text-green-400', MANUAL: 'bg-yellow-500/20 text-yellow-400', }; const PAGE_SIZE = 20; interface BoardListTableProps { onPostClick: (id: number) => void; onWriteClick: () => void; } export function BoardListTable({ onPostClick, onWriteClick }: BoardListTableProps) { const hasPermission = useAuthStore((s) => s.hasPermission); const [posts, setPosts] = useState([]); const [totalCount, setTotalCount] = useState(0); const [page, setPage] = useState(1); const [selectedCategory, setSelectedCategory] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [searchInput, setSearchInput] = useState(''); const [loading, setLoading] = useState(false); const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); // 카테고리별 서브리소스 권한 확인 (전체 선택 시 board CREATE) const canWrite = selectedCategory ? hasPermission(`board:${selectedCategory.toLowerCase()}`, 'CREATE') : hasPermission('board', 'CREATE'); const loadPosts = useCallback(async () => { setLoading(true); try { const result = await fetchBoardPosts({ categoryCd: selectedCategory || undefined, search: searchTerm || undefined, page, size: PAGE_SIZE, }); setPosts(result.items); setTotalCount(result.totalCount); } catch { setPosts([]); setTotalCount(0); } finally { setLoading(false); } }, [selectedCategory, searchTerm, page]); useEffect(() => { loadPosts(); }, [loadPosts]); const handleCategoryChange = (code: string | null) => { setSelectedCategory(code); setPage(1); }; const handleSearch = () => { setSearchTerm(searchInput); setPage(1); }; const handleSearchKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') handleSearch(); }; const formatDate = (dtm: string) => { return new Date(dtm).toLocaleDateString('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit', }); }; return (
{/* Header with Search and Write Button */}
{CATEGORY_FILTER.map((cat) => ( ))}
setSearchInput(e.target.value)} onKeyDown={handleSearchKeyDown} className="px-4 py-2 text-sm bg-bg-2 border border-border rounded text-text-1 placeholder-text-3 focus:border-primary-cyan focus:outline-none w-64" /> {canWrite && ( )}
{/* Board List Table */}
{loading ? (

불러오는 중...

) : ( <> {posts.map((post) => ( onPostClick(post.sn)} className="border-b border-border hover:bg-bg-2 cursor-pointer transition-colors" > ))}
번호 분류 제목 작성자 작성일 조회수
{post.pinnedYn === 'Y' ? ( 공지 ) : ( post.sn )} {CATEGORY_MAP[post.categoryCd] || post.categoryCd} {post.title} {post.authorName} {formatDate(post.regDtm)} {post.viewCnt}
{posts.length === 0 && (

검색 결과가 없습니다.

)} )}
{/* Pagination */} {totalPages > 1 && (
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => ( ))}
)}
); }