diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-15 12:52:11 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-15 12:52:11 +0000 |
| commit | b54f6f03150dd78d86db62201b6386bf14b72394 (patch) | |
| tree | b3092bb34805fdc65eee5282e86a9fb90ba20d6e /lib/rfq-last | |
| parent | c1bd1a2f499ee2f0742170021b37dab410983ab7 (diff) | |
(대표님) 커버, 데이터룸, 파일매니저, 담당자할당 등
Diffstat (limited to 'lib/rfq-last')
| -rw-r--r-- | lib/rfq-last/service.ts | 1 | ||||
| -rw-r--r-- | lib/rfq-last/table/rfq-assign-pic-dialog.tsx | 36 | ||||
| -rw-r--r-- | lib/rfq-last/table/rfq-table-toolbar-actions.tsx | 28 | ||||
| -rw-r--r-- | lib/rfq-last/vendor/rfq-vendor-table.tsx | 32 |
4 files changed, 58 insertions, 39 deletions
diff --git a/lib/rfq-last/service.ts b/lib/rfq-last/service.ts index d4efb81d..8475aac0 100644 --- a/lib/rfq-last/service.ts +++ b/lib/rfq-last/service.ts @@ -100,6 +100,7 @@ export async function getRfqs(input: GetRfqsSchema) { ilike(rfqsLastView.packageNo, s), ilike(rfqsLastView.packageName, s), ilike(rfqsLastView.picName, s), + ilike(rfqsLastView.picCode, s), ilike(rfqsLastView.engPicName, s), ilike(rfqsLastView.projectCode, s), ilike(rfqsLastView.projectName, s), diff --git a/lib/rfq-last/table/rfq-assign-pic-dialog.tsx b/lib/rfq-last/table/rfq-assign-pic-dialog.tsx index 9ca34ccd..70d2ed7e 100644 --- a/lib/rfq-last/table/rfq-assign-pic-dialog.tsx +++ b/lib/rfq-last/table/rfq-assign-pic-dialog.tsx @@ -39,15 +39,6 @@ export function RfqAssignPicDialog({ const [selectedCode, setSelectedCode] = React.useState<PurchaseGroupCodeWithUser | undefined>(undefined); const [selectorOpen, setSelectorOpen] = React.useState(false); - // ITB만 필터링 (rfqCode가 "I"로 시작하는 것) - const itbCodes = React.useMemo(() => { - return selectedRfqCodes.filter(code => code.startsWith("I")); - }, [selectedRfqCodes]); - - const itbIds = React.useMemo(() => { - return selectedRfqIds.filter((id, index) => selectedRfqCodes[index]?.startsWith("I")); - }, [selectedRfqIds, selectedRfqCodes]); - // 다이얼로그 열릴 때 초기화 React.useEffect(() => { if (open) { @@ -81,15 +72,15 @@ export function RfqAssignPicDialog({ return; } - if (itbIds.length === 0) { - toast.error("선택한 항목 중 ITB가 없습니다"); + if (selectedRfqIds.length === 0) { + toast.error("담당자 지정이 가능한 ITB가 없습니다"); return; } setIsAssigning(true); try { const result = await assignPicToRfqs({ - rfqIds: itbIds, + rfqIds: selectedRfqIds, picUserId: selectedCode.user.id, }); @@ -127,32 +118,27 @@ export function RfqAssignPicDialog({ <label className="text-sm font-medium">선택된 ITB</label> <div className="p-3 bg-muted rounded-md"> <div className="flex items-center gap-2 mb-2"> - <Badge variant="secondary">{itbCodes.length}건</Badge> - {itbCodes.length !== selectedRfqCodes.length && ( - <span className="text-xs text-muted-foreground"> - (전체 {selectedRfqCodes.length}건 중) - </span> - )} + <Badge variant="secondary">{selectedRfqCodes.length}건</Badge> </div> <div className="max-h-[100px] overflow-y-auto"> <div className="flex flex-wrap gap-1"> - {itbCodes.slice(0, 10).map((code, index) => ( + {selectedRfqCodes.slice(0, 10).map((code, index) => ( <Badge key={index} variant="outline" className="text-xs"> {code} </Badge> ))} - {itbCodes.length > 10 && ( + {selectedRfqCodes.length > 10 && ( <Badge variant="outline" className="text-xs"> - +{itbCodes.length - 10}개 + +{selectedRfqCodes.length - 10}개 </Badge> )} </div> </div> </div> - {itbCodes.length === 0 && ( + {selectedRfqCodes.length === 0 && ( <Alert className="border-orange-200 bg-orange-50"> <AlertDescription className="text-orange-800"> - 선택한 항목 중 ITB (I로 시작하는 코드)가 없습니다. + 담당자 지정이 가능한 ITB가 없습니다. (상태가 "RFQ 생성" 또는 "구매담당지정"인 ITB만 가능) </AlertDescription> </Alert> )} @@ -165,7 +151,7 @@ export function RfqAssignPicDialog({ type="button" variant="outline" className="w-full justify-start h-auto min-h-[40px]" - disabled={itbCodes.length === 0} + disabled={selectedRfqCodes.length === 0} onClick={() => setSelectorOpen(true)} > {selectedCode ? ( @@ -227,7 +213,7 @@ export function RfqAssignPicDialog({ <Button type="submit" onClick={handleAssign} - disabled={!selectedCode || !selectedCode.user || itbCodes.length === 0 || isAssigning} + disabled={!selectedCode || !selectedCode.user || selectedRfqCodes.length === 0 || isAssigning} > {isAssigning ? ( <> diff --git a/lib/rfq-last/table/rfq-table-toolbar-actions.tsx b/lib/rfq-last/table/rfq-table-toolbar-actions.tsx index 7d48f5a4..00c41402 100644 --- a/lib/rfq-last/table/rfq-table-toolbar-actions.tsx +++ b/lib/rfq-last/table/rfq-table-toolbar-actions.tsx @@ -36,17 +36,27 @@ export function RfqTableToolbarActions<TData>({ // 선택된 RFQ의 ID와 코드 추출 const selectedRfqData = React.useMemo(() => { const rows = selectedRows.map(row => row.original as RfqsLastView); + const assignableRows = rows.filter(row => + row.rfqCode?.startsWith("I") && + (row.status === "RFQ 생성" || row.status === "구매담당지정") + ); + return { ids: rows.map(row => row.id), codes: rows.map(row => row.rfqCode || ""), + statuses: rows.map(row => row.status || ""), // "I"로 시작하는 ITB만 필터링 itbCount: rows.filter(row => row.rfqCode?.startsWith("I")).length, - totalCount: rows.length + totalCount: rows.length, + // 담당자 지정 가능한 ITB (상태가 "RFQ 생성" 또는 "구매담당지정"인 ITB) + assignableItbCount: assignableRows.length, + assignableIds: assignableRows.map(row => row.id), + assignableCodes: assignableRows.map(row => row.rfqCode || "") }; }, [selectedRows]); - // 담당자 지정 가능 여부 체크 ("I"로 시작하는 항목이 있는지) - const canAssignPic = selectedRfqData.itbCount > 0; + // 담당자 지정 가능 여부 체크 (상태가 "RFQ 생성" 또는 "구매담당지정"인 ITB가 있는지) + const canAssignPic = selectedRfqData.assignableItbCount > 0; const handleAssignSuccess = () => { // 테이블 선택 초기화 @@ -76,15 +86,15 @@ export function RfqTableToolbarActions<TData>({ <Users className="h-4 w-4" /> 담당자 지정 <Badge variant="secondary" className="ml-1"> - {selectedRfqData.itbCount}건 + {selectedRfqData.assignableItbCount}건 </Badge> </Button> </TooltipTrigger> <TooltipContent> <p>선택한 ITB에 구매 담당자를 지정합니다</p> - {selectedRfqData.itbCount !== selectedRfqData.totalCount && ( + {selectedRfqData.assignableItbCount !== selectedRfqData.itbCount && ( <p className="text-xs text-muted-foreground mt-1"> - 전체 {selectedRfqData.totalCount}건 중 ITB {selectedRfqData.itbCount}건만 지정됩니다 + 전체 ITB {selectedRfqData.itbCount}건 중 {selectedRfqData.assignableItbCount}건만 지정 가능합니다 </p> )} </TooltipContent> @@ -103,7 +113,7 @@ export function RfqTableToolbarActions<TData>({ </Badge> {selectedRfqData.totalCount !== selectedRfqData.itbCount && ( <Badge variant="outline" className="text-xs"> - ITB {selectedRfqData.itbCount}건 + ITB {selectedRfqData.itbCount}건 (지정가능 {selectedRfqData.assignableItbCount}건) </Badge> )} </div> @@ -139,8 +149,8 @@ export function RfqTableToolbarActions<TData>({ <RfqAssignPicDialog open={showAssignDialog} onOpenChange={setShowAssignDialog} - selectedRfqIds={selectedRfqData.ids} - selectedRfqCodes={selectedRfqData.codes} + selectedRfqIds={selectedRfqData.assignableIds} + selectedRfqCodes={selectedRfqData.assignableCodes} onSuccess={handleAssignSuccess} /> </> diff --git a/lib/rfq-last/vendor/rfq-vendor-table.tsx b/lib/rfq-last/vendor/rfq-vendor-table.tsx index dc5564e2..428160d5 100644 --- a/lib/rfq-last/vendor/rfq-vendor-table.tsx +++ b/lib/rfq-last/vendor/rfq-vendor-table.tsx @@ -753,8 +753,10 @@ export function RfqVendorTable({ filterFn: createFilterFn("text"), cell: ({ row }) => { - const status = row.original.tbeStatus; + const status = row.original.tbeStatus?.trim(); + const rfqCode = row.original.rfqCode?.trim(); + // 생성중/준비중은 대기 표시(비클릭) if (!status || status === "준비중") { return ( <Badge variant="outline" className="text-gray-500"> @@ -772,8 +774,28 @@ export function RfqVendorTable({ "취소": { variant: "destructive", icon: <XCircle className="h-3 w-3 mr-1" /> }, }[status] || { variant: "outline", icon: null, color: "text-gray-600" }; + const isClickable = !!rfqCode; + return ( - <Badge variant={statusConfig.variant as any} className={statusConfig.color}> + <Badge + role={isClickable ? "button" : undefined} + tabIndex={isClickable ? 0 : -1} + variant={statusConfig.variant as any} + className={cn(statusConfig.color, isClickable && "cursor-pointer hover:underline")} + onClick={(e) => { + if (!isClickable) return; + e.stopPropagation(); + e.preventDefault(); + router.push(`/evcp/tbe-last?search=${encodeURIComponent(rfqCode!)}`); + // window.open( + // `/evcp/tbe-last?search=${encodeURIComponent(rfqCode!)}`, + // "_blank", + // "noopener,noreferrer" + // ); + // 새 창으로 이동 + }} + title={isClickable ? `TBE로 이동: ${rfqCode}` : undefined} + > {statusConfig.icon} {status} </Badge> @@ -802,19 +824,19 @@ export function RfqVendorTable({ "Acceptable": { variant: "success", icon: <CheckCircle className="h-3 w-3" />, - text: "적합", + text: "Acceptable", color: "bg-green-50 text-green-700 border-green-200" }, "Acceptable with Comment": { variant: "warning", icon: <AlertCircle className="h-3 w-3" />, - text: "조건부 적합", + text: "Acceptable with Comment", color: "bg-yellow-50 text-yellow-700 border-yellow-200" }, "Not Acceptable": { variant: "destructive", icon: <XCircle className="h-3 w-3" />, - text: "부적합", + text: "Not Acceptable", color: "bg-red-50 text-red-700 border-red-200" }, }[result]; |
