// lib/vendor-document-list/plant/upload/table.tsx "use client" import * as React from "react" import type { DataTableAdvancedFilterField, DataTableFilterField, DataTableRowAction, } from "@/types/table" import { useDataTable } from "@/hooks/use-data-table" import { DataTable } from "@/components/data-table/data-table" import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar" import { getColumns } from "./columns" import { getStageSubmissions } from "./service" import { StageSubmissionView } from "@/db/schema" import { StageSubmissionToolbarActions } from "./toolbar-actions" import { useRouter, useSearchParams, usePathname } from "next/navigation" import { ProjectFilter } from "./components/project-filter" import { SingleUploadDialog } from "./components/single-upload-dialog" import { HistoryDialog } from "./components/history-dialog" import { ViewSubmissionDialog } from "./components/view-submission-dialog" import { toast } from "sonner" import { quickDownload } from "@/lib/file-download" interface StageSubmissionsTableProps { promises: Promise<[ Awaited>, { projects: Array<{ id: number; code: string }> } ]> selectedProjectId?: number | null } export function StageSubmissionsTable({ promises, selectedProjectId }: StageSubmissionsTableProps) { const [{ data, pageCount }, { projects }] = React.use(promises) const router = useRouter() const pathname = usePathname() const searchParams = useSearchParams() const [rowAction, setRowAction] = React.useState | null>(null) const columns = React.useMemo( () => getColumns({ setRowAction }), [setRowAction] ) // 프로젝트 필터 핸들러 const handleProjectChange = (projectId: number | null) => { const current = new URLSearchParams(Array.from(searchParams.entries())) if (projectId) { current.set("projectId", projectId.toString()) } else { current.delete("projectId") } // 페이지를 1로 리셋 current.set("page", "1") const search = current.toString() const query = search ? `?${search}` : "" router.push(`${pathname}${query}`) } // Filter fields - 프로젝트 필터 제거 const filterFields: DataTableFilterField[] = [ { id: "stageStatus", label: "Stage Status", options: [ { label: "Planned", value: "PLANNED" }, { label: "In Progress", value: "IN_PROGRESS" }, { label: "Submitted", value: "SUBMITTED" }, { label: "Approved", value: "APPROVED" }, { label: "Rejected", value: "REJECTED" }, { label: "Completed", value: "COMPLETED" }, ] }, { id: "latestSubmissionStatus", label: "Submission Status", options: [ { label: "Submitted", value: "SUBMITTED" }, { label: "Under Review", value: "UNDER_REVIEW" }, { label: "Draft", value: "DRAFT" }, { label: "Withdrawn", value: "WITHDRAWN" }, ] }, { id: "requiresSubmission", label: "Requires Submission", options: [ { label: "Yes", value: "true" }, { label: "No", value: "false" }, ] }, { id: "requiresSync", label: "Requires Sync", options: [ { label: "Yes", value: "true" }, { label: "No", value: "false" }, ] }, { id: "isOverdue", label: "Overdue", options: [ { label: "Yes", value: "true" }, { label: "No", value: "false" }, ] } ] const advancedFilterFields: DataTableAdvancedFilterField[] = [ { id: "docNumber", label: "Doc Number", type: "text", }, { id: "documentTitle", label: "Document Title", type: "text", }, { id: "stageName", label: "Stage Name", type: "text", }, { id: "stagePlanDate", label: "Due Date", type: "date", }, { id: "daysUntilDue", label: "Days Until Due", type: "number", }, ] const { table } = useDataTable({ data, columns, pageCount, filterFields, enablePinning: true, enableAdvancedFilter: true, initialState: { sorting: [ { id: "isOverdue", desc: true }, { id: "daysUntilDue", desc: false } ], columnPinning: { right: ["actions"] }, }, getRowId: (originalRow) => `${originalRow.documentId}-${originalRow.stageId}`, shallow: false, clearOnDefault: true, columnResizeMode: "onEnd", }) React.useEffect(() => { if (!rowAction) return; const { type, row } = rowAction; if (type === "downloadCover") { const projectCode = row.original.projectCode; const project = projects.find(p => p.code === projectCode); if (!project) { toast.error("프로젝트 정보를 찾을 수 없습니다."); setRowAction(null); return; } (async () => { try { const res = await fetch(`/api/projects/${project.id}/cover`, { method: "GET" }); if (!res.ok) { const error = await res.json(); throw new Error(error.message || "커버 페이지를 가져올 수 없습니다"); } const { fileUrl, fileName } = await res.json(); // quickDownload 사용 quickDownload(fileUrl, fileName || `${projectCode}_cover.pdf`); toast.success("커버 페이지 다운로드를 시작했습니다."); } catch (e) { toast.error(e instanceof Error ? e.message : "커버 페이지 다운로드에 실패했습니다."); console.error(e); } finally { setRowAction(null); } })(); } else if (type === "sync") { // 개별 행 sync 처리 const submissionId = row.original.latestSubmissionId; if (!submissionId) { toast.error("제출물 ID를 찾을 수 없습니다."); setRowAction(null); return; } (async () => { try { toast.info("동기화를 시작합니다..."); const response = await fetch('/api/stage-submissions/sync', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ submissionIds: [submissionId] }), }); const result = await response.json(); if (result.success) { toast.success("동기화가 완료되었습니다."); // 상세 결과 표시 if (result.results?.details?.[0]) { const detail = result.results.details[0]; if (!detail.success) { toast.warning(`동기화 실패: ${detail.error || '알 수 없는 오류'}`); } } // 테이블 새로고침 window.location.reload(); } else { toast.error(result.error || "동기화에 실패했습니다."); } } catch (error) { console.error("Sync error:", error); toast.error("동기화 중 오류가 발생했습니다."); } finally { setRowAction(null); } })(); } }, [rowAction, setRowAction, projects]); return ( <> {/* 프로젝트 필터를 툴바 위에 배치 */}
{data.length} record(s) found
{/* Upload Dialog */} {rowAction?.type === "upload" && ( !open && setRowAction(null)} submission={rowAction.row.original} onUploadComplete={() => { setRowAction(null) // 테이블 새로고침 window.location.reload() }} /> )} {/* View Submission Dialog */} {rowAction?.type === "view" && ( !open && setRowAction(null)} submission={rowAction.row.original} /> )} {/* History Dialog */} {rowAction?.type === "history" && ( !open && setRowAction(null)} submission={rowAction.row.original} /> )} ) }