summaryrefslogtreecommitdiff
path: root/lib/rfq-last/vendor/rfq-vendor-table.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfq-last/vendor/rfq-vendor-table.tsx')
-rw-r--r--lib/rfq-last/vendor/rfq-vendor-table.tsx223
1 files changed, 162 insertions, 61 deletions
diff --git a/lib/rfq-last/vendor/rfq-vendor-table.tsx b/lib/rfq-last/vendor/rfq-vendor-table.tsx
index 830fd448..d451b2ba 100644
--- a/lib/rfq-last/vendor/rfq-vendor-table.tsx
+++ b/lib/rfq-last/vendor/rfq-vendor-table.tsx
@@ -27,7 +27,9 @@ import {
Info,
Loader2,
Router,
- Shield
+ Shield,
+ CheckSquare,
+ GitCompare
} from "lucide-react";
import { format } from "date-fns";
import { ko } from "date-fns/locale";
@@ -59,6 +61,7 @@ import {
getRfqSendData,
getSelectedVendorsWithEmails,
sendRfqToVendors,
+ updateShortList,
type RfqSendData,
type VendorEmailInfo
} from "../service"
@@ -278,7 +281,7 @@ export function RfqVendorTable({
});
const [editContractVendor, setEditContractVendor] = React.useState<any | null>(null);
-
+ const [isUpdatingShortList, setIsUpdatingShortList] = React.useState(false);
const router = useRouter()
@@ -290,6 +293,51 @@ export function RfqVendorTable({
console.log(mergedData, "mergedData")
+ // Short List 확정 핸들러
+ const handleShortListConfirm = React.useCallback(async () => {
+
+ try {
+ setIsUpdatingShortList(true);
+
+ const vendorIds = selectedRows
+ .map(vendor => vendor.vendorId)
+ .filter(id => id != null);
+
+ const result = await updateShortList(rfqId, vendorIds, true);
+
+ if (result.success) {
+ toast.success(`${result.updatedCount}개 벤더를 Short List로 확정했습니다.`);
+ setSelectedRows([]);
+ router.refresh();
+ }
+ } catch (error) {
+ console.error("Short List 확정 실패:", error);
+ toast.error("Short List 확정에 실패했습니다.");
+ } finally {
+ setIsUpdatingShortList(false);
+ }
+ }, [selectedRows, rfqId, router]);
+
+ // 견적 비교 핸들러
+ const handleQuotationCompare = React.useCallback(() => {
+ const vendorsWithQuotation = selectedRows.filter(row =>
+ row.response?.submission?.submittedAt
+ );
+
+ if (vendorsWithQuotation.length < 2) {
+ toast.warning("비교를 위해 최소 2개 이상의 견적서가 필요합니다.");
+ return;
+ }
+
+ // 견적 비교 페이지로 이동 또는 모달 열기
+ const vendorIds = vendorsWithQuotation
+ .map(v => v.vendorId)
+ .filter(id => id != null)
+ .join(',');
+
+ router.push(`/evcp/rfq-last/${rfqId}/compare?vendors=${vendorIds}`);
+ }, [selectedRows, rfqId, router]);
+
// 일괄 발송 핸들러
const handleBulkSend = React.useCallback(async () => {
if (selectedRows.length === 0) {
@@ -302,6 +350,7 @@ export function RfqVendorTable({
// 선택된 벤더 ID들 추출
const selectedVendorIds = selectedRows
+ .filter(v=>v.shortList)
.map(row => row.vendorId)
.filter(id => id != null);
@@ -1142,65 +1191,117 @@ export function RfqVendorTable({
}, [selectedRows]);
// 추가 액션 버튼들
- const additionalActions = React.useMemo(() => (
- <div className="flex items-center gap-2">
- <Button
- variant="outline"
- size="sm"
- onClick={() => setIsAddDialogOpen(true)}
- disabled={isLoadingSendData}
- >
- <Plus className="h-4 w-4 mr-2" />
- 벤더 추가
- </Button>
- {selectedRows.length > 0 && (
- <>
- <Button
- variant="outline"
- size="sm"
- onClick={() => setIsBatchUpdateOpen(true)}
- disabled={isLoadingSendData}
- >
- <Settings2 className="h-4 w-4 mr-2" />
- 정보 일괄 입력 ({selectedRows.length})
- </Button>
- <Button
- variant="outline"
- size="sm"
- onClick={handleBulkSend}
- disabled={isLoadingSendData || selectedRows.length === 0}
- >
- {isLoadingSendData ? (
- <>
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
- 데이터 준비중...
- </>
- ) : (
- <>
- <Send className="h-4 w-4 mr-2" />
- RFQ 발송 ({selectedRows.length})
- </>
- )}
- </Button>
- </>
- )}
- <Button
- variant="outline"
- size="sm"
- onClick={() => {
- setIsRefreshing(true);
- setTimeout(() => {
- setIsRefreshing(false);
- toast.success("데이터를 새로고침했습니다.");
- }, 1000);
- }}
- disabled={isRefreshing || isLoadingSendData}
- >
- <RefreshCw className={cn("h-4 w-4 mr-2", isRefreshing && "animate-spin")} />
- 새로고침
- </Button>
- </div>
- ), [selectedRows, isRefreshing, isLoadingSendData, handleBulkSend]);
+ const additionalActions = React.useMemo(() => {
+
+ // 참여 의사가 있는 선택된 벤더 수 계산
+ const participatingCount = selectedRows.length;
+ const shortListCount = selectedRows.filter(v=>v.shortList).length;
+
+ // 견적서가 있는 선택된 벤더 수 계산
+ const quotationCount = selectedRows.filter(row =>
+ row.response?.submission?.submittedAt
+ ).length;
+
+ return (
+ <div className="flex items-center gap-2">
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => setIsAddDialogOpen(true)}
+ disabled={isLoadingSendData}
+ >
+ <Plus className="h-4 w-4 mr-2" />
+ 벤더 추가
+ </Button>
+
+ {selectedRows.length > 0 && (
+ <>
+ {/* Short List 확정 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleShortListConfirm}
+ disabled={isUpdatingShortList }
+ // className={ "border-green-500 text-green-600 hover:bg-green-50" }
+ >
+ {isUpdatingShortList ? (
+ <>
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+ 처리중...
+ </>
+ ) : (
+ <>
+ <CheckSquare className="h-4 w-4 mr-2" />
+ Short List 확정
+ {participatingCount > 0 && ` (${participatingCount})`}
+ </>
+ )}
+ </Button>
+
+ {/* 견적 비교 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleQuotationCompare}
+ disabled={quotationCount < 1}
+ className={quotationCount >= 2 ? "border-blue-500 text-blue-600 hover:bg-blue-50" : ""}
+ >
+ <GitCompare className="h-4 w-4 mr-2" />
+ 견적 비교
+ {quotationCount > 0 && ` (${quotationCount})`}
+ </Button>
+
+ {/* 정보 일괄 입력 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => setIsBatchUpdateOpen(true)}
+ disabled={isLoadingSendData}
+ >
+ <Settings2 className="h-4 w-4 mr-2" />
+ 정보 일괄 입력 ({selectedRows.length})
+ </Button>
+
+ {/* RFQ 발송 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleBulkSend}
+ disabled={isLoadingSendData || selectedRows.length === 0}
+ >
+ {isLoadingSendData ? (
+ <>
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+ 데이터 준비중...
+ </>
+ ) : (
+ <>
+ <Send className="h-4 w-4 mr-2" />
+ RFQ 발송 ({shortListCount})
+ </>
+ )}
+ </Button>
+ </>
+ )}
+
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => {
+ setIsRefreshing(true);
+ setTimeout(() => {
+ setIsRefreshing(false);
+ toast.success("데이터를 새로고침했습니다.");
+ }, 1000);
+ }}
+ disabled={isRefreshing || isLoadingSendData}
+ >
+ <RefreshCw className={cn("h-4 w-4 mr-2", isRefreshing && "animate-spin")} />
+ 새로고침
+ </Button>
+ </div>
+ );
+ }, [selectedRows, isRefreshing, isLoadingSendData, handleBulkSend, handleShortListConfirm, handleQuotationCompare, isUpdatingShortList]);
return (
<>