import { Plus, BookOpen, Upload, Download, FileSpreadsheet, ChevronDown } from 'lucide-react'; import { useOverlayRouter } from '@/lib/useOverlayRouter'; import { showToast } from '@/lib/notify'; import { confirm } from '@/lib/confirm'; import { PageContainer } from '@/components/layout/PageContainer'; import { SearchInput } from '@/components/layout/PageToolbar'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@/components/ui/dropdown-menu'; import { TablePagination } from '@/components/ui/table-pagination'; import { Typography } from '@/components/ui/typography'; import { useServerList } from '@/lib/useServerList'; import { useCards } from '@/features/cards/hooks/useCards'; import { useGetCard } from '@/api/generated/card/card'; import { CardTable } from '@/features/cards/components/CardTable'; import { CardFormSheet } from '@/features/cards/components/CardFormSheet'; import { CardExcelUploadModal, downloadCardTemplate } from '@/features/cards/components/CardExcelUploadModal'; import { mapCardData, type CardTab, type NegotiationCard } from '@/features/cards/types'; import type { ListCardsParams } from '@/api/generated/model/listCardsParams'; export default function CardsPage() { // 검색/탭/페이지 상태(재사용 훅) → 서버 쿼리 파라미터로 변환. const list = useServerList({ pageSize: 10, initialFilters: { tab: 'ALL' } }); const activeTab = list.filters.tab as CardTab; const params: ListCardsParams = { search: list.debouncedSearch || undefined, is_wildcard: activeTab === 'ALL' ? undefined : activeTab === 'WILD', page: list.page, size: list.pageSize, }; const { cards, total, totalNego, totalWild, createCard, updateCard, deleteCard, bulkCreate } = useCards(params); const totalPages = list.totalPages(total); // 오버레이(폼/엑셀)를 쿼리스트링으로 → 딥링크·뒤로가기·새로고침 지원. // ?detail= 직접 접근 시 단건 API 로 받아 수정 폼을 연다(현재 페이지에 없어도 동작). const overlay = useOverlayRouter(['new', 'detail', 'modal']); const editId = overlay.get('detail'); const modal = overlay.get('modal'); // 'excel' | null const editQuery = useGetCard(editId ?? '', { query: { enabled: !!editId } }); const editing: NegotiationCard | null = editQuery.data?.card ? mapCardData(editQuery.data.card) : null; const formMode: 'create' | 'edit' = editId ? 'edit' : 'create'; const isFormOpen = overlay.has('new') || !!editing; const openCreate = () => overlay.open('new'); const openEdit = (card: NegotiationCard) => overlay.open('detail', card.id); const handleDeleteCard = async (id: string, cardName: string) => { if (await confirm({ title: '카드 삭제', description: `[${cardName}]을 삭제하시겠습니까?`, confirmText: '삭제', destructive: true })) { try { await deleteCard(id); showToast('삭제되었습니다.', 'info'); } catch (err) { showToast(err instanceof Error ? err.message : '카드 삭제 실패', 'error'); } } }; const tabs: { id: CardTab; label: string; count: number }[] = [ { id: 'ALL', label: '전체', count: totalNego + totalWild }, { id: 'CARD', label: '협상카드', count: totalNego }, { id: 'WILD', label: '와일드카드', count: totalWild }, ]; return ( {/* Upper Information Banner */}
협상카드 및 와일드카드 관리 우리 회사 카드와 기본 제공(공용) 카드를 함께 조회합니다. 공용 카드는 수정·삭제할 수 없습니다.
}> 엑셀업로드 overlay.open('modal', 'excel')}> 일괄 업로드 양식 다운로드
{/* Primary Navigation Tab */}
{tabs.map((tab) => ( ))}
{/* Filtering Search Bar */} list.setSearch(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && list.submitSearch()} onClear={list.clearSearch} placeholder="전체 카드이름, 카드번호, 코드 검색..." /> } /> {isFormOpen && ( )} {modal === 'excel' && ( )}
); }