summaryrefslogtreecommitdiff
path: root/lib/tbe-last/table
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-18 00:23:40 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-18 00:23:40 +0000
commitcf8dac0c6490469dab88a560004b0c07dbd48612 (patch)
treeb9e76061e80d868331e6b4277deecb9086f845f3 /lib/tbe-last/table
parente5745fc0268bbb5770bc14a55fd58a0ec30b466e (diff)
(대표님) rfq, 계약, 서명 등
Diffstat (limited to 'lib/tbe-last/table')
-rw-r--r--lib/tbe-last/table/tbe-last-table-columns.tsx49
-rw-r--r--lib/tbe-last/table/tbe-last-table.tsx118
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}