import { useState } from 'react'; import { Plus, Upload, Download, FileSpreadsheet, ChevronDown, Trash2 } 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 { PageToolbar, SearchInput } from '@/components/layout/PageToolbar'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { useAuthStore, canManage } from '@/stores/auth'; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@/components/ui/dropdown-menu'; import { useServerList } from '@/lib/useServerList'; import { usePartners } from '@/features/partners/hooks/usePartners'; import type { ListSuppliersParams } from '@/api/generated/model/listSuppliersParams'; import { PartnerTable } from '@/features/partners/components/PartnerTable'; import { PartnerFormSheet } from '@/features/partners/components/PartnerFormSheet'; import { ExcelUploadModal, downloadPartnerTemplate } from '@/features/partners/components/ExcelUploadModal'; import { type Partner } from '@/features/partners/types'; export default function PartnersPage() { // 검색/페이지 상태(재사용 훅) → 서버 쿼리 파라미터로 변환. const list = useServerList({ pageSize: 10 }); const params: ListSuppliersParams = { search: list.debouncedSearch || undefined, page: list.page, size: list.pageSize, }; const { partners, total, allPartners, createPartner, updatePartner, deletePartner, bulkCreate } = usePartners(params); const totalPages = list.totalPages(total); // 오버레이(폼/엑셀)를 쿼리스트링으로 → 딥링크·뒤로가기·새로고침 지원. // ?detail= 직접 접근 시 데이터 로드 후 수정 폼이 자동으로 열린다. const overlay = useOverlayRouter(['new', 'detail', 'modal']); const editId = overlay.get('detail'); const modal = overlay.get('modal'); // 'excel' | null const editing = editId ? allPartners.find((p) => p.supplier_id === editId) ?? null : null; const formMode: 'create' | 'edit' = editId ? 'edit' : 'create'; const isFormOpen = overlay.has('new') || !!editing; // 협력사 삭제는 최고관리자 전용(단건 삭제와 동일 규칙) — 일괄삭제 버튼도 최고관리자에게만 노출. const isSuperAdmin = useAuthStore((st) => canManage(st.user?.role)); const [selectedIds, setSelectedIds] = useState([]); const handleBulkDelete = async () => { if (selectedIds.length === 0) return; if (!(await confirm({ title: '선택 협력사 일괄 삭제', description: `선택한 ${selectedIds.length}개 협력사를 명부에서 삭제하시겠습니까?`, confirmText: '삭제', destructive: true }))) return; let ok = 0; let fail = 0; for (const id of selectedIds) { try { await deletePartner(id); ok += 1; } catch { fail += 1; } } setSelectedIds([]); showToast(fail === 0 ? `${ok}개 협력사가 삭제되었습니다.` : `${ok}개 삭제 · ${fail}개 실패`, fail === 0 ? 'info' : 'error'); }; const openCreate = () => overlay.open('new'); const openEdit = (part: Partner) => overlay.open('detail', part.supplier_id); const handleDeletePartner = async (id: string, partnerName: string) => { if (await confirm({ title: '협력사 삭제', description: `[${partnerName}] 파트너사를 협력사 목록에서 삭제하시겠습니까?`, confirmText: '삭제', destructive: true })) { try { await deletePartner(id); showToast('협력사 정보가 명부에서 제거되었습니다.', 'info'); } catch (err) { showToast(err instanceof Error ? err.message : '협력사 삭제 실패', 'error'); } } }; return ( {/* 검색/액션 바 + 테이블을 한 카드로 붙인다(포털형). */}
{isSuperAdmin && ( )} }> 엑셀업로드 overlay.open('modal', 'excel')}> 일괄 업로드 양식 다운로드 } > list.setSearch(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && list.submitSearch()} onClear={list.clearSearch} placeholder="협력사명, 코드 또는 담당자명으로 추적 검색..." />
{isFormOpen && ( )} {modal === 'excel' && ( )}
); }