diff options
Diffstat (limited to 'lib/tbe-last/table')
| -rw-r--r-- | lib/tbe-last/table/tbe-last-table-columns.tsx | 49 | ||||
| -rw-r--r-- | lib/tbe-last/table/tbe-last-table.tsx | 118 |
2 files changed, 149 insertions, 18 deletions
diff --git a/lib/tbe-last/table/tbe-last-table-columns.tsx b/lib/tbe-last/table/tbe-last-table-columns.tsx index 726d8925..b18e51c0 100644 --- a/lib/tbe-last/table/tbe-last-table-columns.tsx +++ b/lib/tbe-last/table/tbe-last-table-columns.tsx @@ -86,6 +86,55 @@ export function getColumns({ cell: ({ row }) => row.original.rfqCode, size: 120, }, + + { + id: "tbeRequired", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="TBE 필요" /> + ), + cell: ({ row, table }) => { + const rfqCode = row.original.rfqCode; + const sessionStatus = row.original.sessionStatus; + + // 같은 RFQ의 첫 번째 행에만 체크박스 표시 + const allRows = table.getRowModel().rows; + const isFirstInGroup = allRows.findIndex( + r => r.original.rfqCode === rfqCode + ) === allRows.indexOf(row); + + if (!isFirstInGroup) return null; + + // 같은 RFQ의 모든 row + const rfqRows = allRows.filter( + r => r.original.rfqCode === rfqCode + ); + + const vendorCount = rfqRows.length; + + // 같은 RFQ의 row들이 선택되었는지 확인 + const isChecked = rfqRows.every(r => r.getIsSelected()); + const isIndeterminate = rfqRows.some(r => r.getIsSelected()) && !isChecked; + + return ( + <div className="flex items-center gap-2"> + <Checkbox + checked={isChecked} + indeterminate={isIndeterminate} + onCheckedChange={(checked) => { + // RFQ의 모든 벤더 선택/해제 + rfqRows.forEach(r => { + r.toggleSelected(!!checked); + }); + }} + /> + <span className="text-xs text-muted-foreground"> + ({vendorCount} vendors) + </span> + </div> + ); + }, + size: 120, + }, // RFQ Title { diff --git a/lib/tbe-last/table/tbe-last-table.tsx b/lib/tbe-last/table/tbe-last-table.tsx index a9328bdf..c18768cd 100644 --- a/lib/tbe-last/table/tbe-last-table.tsx +++ b/lib/tbe-last/table/tbe-last-table.tsx @@ -10,7 +10,7 @@ import { DataTable } from "@/components/data-table/data-table" import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar" import { getColumns } from "./tbe-last-table-columns" import { TbeLastView } from "@/db/schema" -import { getAllTBELast, getTBESessionDetail } from "@/lib/tbe-last/service" +import { getAllTBELast, getTBESessionDetail, requestTBEForRFQ } from "@/lib/tbe-last/service" import { Button } from "@/components/ui/button" import { Download, RefreshCw } from "lucide-react" import { exportTableToExcel } from "@/lib/export" @@ -20,6 +20,7 @@ import { SessionDetailDialog } from "./session-detail-dialog" import { DocumentsSheet } from "./documents-sheet" import { PrItemsDialog } from "./pr-items-dialog" import { EvaluationDialog } from "./evaluation-dialog" +import { toast } from "sonner" interface TbeLastTableProps { promises: Promise<[ @@ -30,21 +31,21 @@ interface TbeLastTableProps { export function TbeLastTable({ promises }: TbeLastTableProps) { const router = useRouter() const [{ data, pageCount }] = React.use(promises) - - console.log(data,"data") + + console.log(data, "data") // Dialog states const [sessionDetailOpen, setSessionDetailOpen] = React.useState(false) const [documentsOpen, setDocumentsOpen] = React.useState(false) const [prItemsOpen, setPrItemsOpen] = React.useState(false) const [evaluationOpen, setEvaluationOpen] = React.useState(false) - + const [selectedSessionId, setSelectedSessionId] = React.useState<number | null>(null) const [selectedRfqId, setSelectedRfqId] = React.useState<number | null>(null) const [selectedSession, setSelectedSession] = React.useState<TbeLastView | null>(null) const [sessionDetail, setSessionDetail] = React.useState<any>(null) const [isLoadingDetail, setIsLoadingDetail] = React.useState(false) - + // Load session detail when needed const loadSessionDetail = React.useCallback(async (sessionId: number) => { setIsLoadingDetail(true) @@ -57,37 +58,37 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { setIsLoadingDetail(false) } }, []) - + // Handlers const handleOpenSessionDetail = React.useCallback((sessionId: number) => { setSelectedSessionId(sessionId) setSessionDetailOpen(true) loadSessionDetail(sessionId) }, [loadSessionDetail]) - + const handleOpenDocuments = React.useCallback((sessionId: number) => { setSelectedSessionId(sessionId) setDocumentsOpen(true) loadSessionDetail(sessionId) }, [loadSessionDetail]) - + const handleOpenPrItems = React.useCallback((rfqId: number) => { setSelectedRfqId(rfqId) setPrItemsOpen(true) loadSessionDetail(rfqId) }, [loadSessionDetail]) - + const handleOpenEvaluation = React.useCallback((session: TbeLastView) => { setSelectedSession(session) setEvaluationOpen(true) loadSessionDetail(session.rfqId) }, []) - + const handleRefresh = React.useCallback(() => { router.refresh() }, [router]) - + // Table columns const columns = React.useMemo( () => @@ -99,7 +100,7 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { }), [handleOpenSessionDetail, handleOpenDocuments, handleOpenPrItems, handleOpenEvaluation] ) - + // Filter fields const filterFields: DataTableAdvancedFilterField<TbeLastView>[] = [ { @@ -125,7 +126,7 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { ], }, ] - + // Data table const { table } = useDataTable({ data, @@ -142,7 +143,64 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { shallow: false, clearOnDefault: true, }) - + + const handleBulkTBERequest = React.useCallback(async (rfqGroups: Map<string, TbeLastView[]>) => { + try { + const promises = Array.from(rfqGroups.entries()).map(async ([rfqCode, sessions]) => { + // 준비중 상태인 세션만 필터링 + const pendingSessions = sessions.filter(s => s.sessionStatus === "준비중"); + + if (pendingSessions.length === 0) { + toast.info(`RFQ ${rfqCode}: 이미 TBE가 요청되었습니다.`); + return null; + } + + const vendors = pendingSessions.map(session => ({ + sessionId: session.tbeSessionId, + vendorId: session.vendorId, // vendor ID 추가 + vendorCode: session.vendorCode, + vendorName: session.vendorName, + })); + + const rfqInfo = { + rfqId: sessions[0].rfqId, // rfqLastId 추가 + rfqCode: sessions[0].rfqCode, + rfqTitle: sessions[0].rfqTitle || "", + rfqDueDate: sessions[0].rfqDueDate, + projectCode: sessions[0].projectCode || "", + projectName: sessions[0].projectName || "", + packageNo: sessions[0].packageNo || "", + packageName: sessions[0].packageName || "", + picName: sessions[0].picName || "", + }; + + return requestTBEForRFQ(rfqInfo, vendors); + }); + + const results = await Promise.allSettled(promises); + + const successCount = results.filter(r => r.status === "fulfilled" && r.value?.success).length; + const failCount = results.filter(r => r.status === "rejected" || (r.status === "fulfilled" && !r.value?.success)).length; + + if (successCount > 0) { + toast.success(`${successCount}개 RFQ에 대한 TBE 요청이 완료되었습니다.`); + } + + if (failCount > 0) { + toast.error(`${failCount}개 RFQ에 대한 TBE 요청이 실패했습니다.`); + } + + // 테이블 새로고침 + router.refresh(); + table.resetRowSelection(); + + } catch (error) { + console.error("TBE 요청 처리 중 오류:", error); + toast.error("TBE 요청 처리 중 오류가 발생했습니다."); + } + }, [router, table]); + + return ( <> <DataTable table={table}> @@ -152,6 +210,30 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { shallow={false} > <div className="flex items-center gap-2"> + + {table.getFilteredSelectedRowModel().rows.length > 0 && ( + <Button + variant="default" + size="sm" + onClick={() => { + const selectedRows = table.getFilteredSelectedRowModel().rows; + const rfqGroups = new Map(); + + // RFQ별로 그룹핑 + selectedRows.forEach(row => { + const rfqCode = row.original.rfqCode; + if (!rfqGroups.has(rfqCode)) { + rfqGroups.set(rfqCode, []); + } + rfqGroups.get(rfqCode).push(row.original); + }); + + handleBulkTBERequest(rfqGroups); + }} + > + 선택된 항목 TBE 요청 ({table.getFilteredSelectedRowModel().rows.length}) + </Button> + )} <Button variant="outline" size="sm" @@ -178,7 +260,7 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { </div> </DataTableAdvancedToolbar> </DataTable> - + {/* Session Detail Dialog */} <SessionDetailDialog open={sessionDetailOpen} @@ -186,7 +268,7 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { sessionDetail={sessionDetail} isLoading={isLoadingDetail} /> - + {/* Documents Sheet */} <DocumentsSheet open={documentsOpen} @@ -194,7 +276,7 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { sessionDetail={sessionDetail} isLoading={isLoadingDetail} /> - + {/* PR Items Dialog */} <PrItemsDialog open={prItemsOpen} @@ -202,7 +284,7 @@ export function TbeLastTable({ promises }: TbeLastTableProps) { sessionDetail={sessionDetail} isLoading={isLoadingDetail} /> - + {/* Evaluation Dialog */} <EvaluationDialog open={evaluationOpen} |
