summaryrefslogtreecommitdiff
path: root/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-08-04 09:39:21 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-08-04 09:39:21 +0000
commit53ad72732f781e6c6d5ddb3776ea47aec010af8e (patch)
treee676287827f8634be767a674b8ad08b6ed7eb3e6 /lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx
parent3e4d15271322397764601dee09441af8a5b3adf5 (diff)
(최겸) PQ/실사 수정 및 개발
Diffstat (limited to 'lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx')
-rw-r--r--lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx756
1 files changed, 406 insertions, 350 deletions
diff --git a/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx b/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx
index abba72d1..48aeb552 100644
--- a/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx
+++ b/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx
@@ -1,351 +1,407 @@
-"use client"
-
-import * as React from "react"
-import { type Table } from "@tanstack/react-table"
-import { Download, ClipboardCheck, X, Send } from "lucide-react"
-import { toast } from "sonner"
-
-import { exportTableToExcel } from "@/lib/export"
-import { Button } from "@/components/ui/button"
-import { PQSubmission } from "./vendors-table-columns"
-import {
- requestInvestigationAction,
- cancelInvestigationAction,
- sendInvestigationResultsAction,
- getFactoryLocationAnswer
-} from "@/lib/pq/service"
-import { RequestInvestigationDialog } from "./request-investigation-dialog"
-import { CancelInvestigationDialog } from "./cancel-investigation-dialog"
-import { SendResultsDialog } from "./send-results-dialog"
-
-interface VendorsTableToolbarActionsProps {
- table: Table<PQSubmission>
-}
-
-interface InvestigationInitialData {
- evaluationType?: "SITE_AUDIT" | "QM_SELF_AUDIT";
- qmManagerId?: number;
- forecastedAt?: Date;
- createdAt?: Date;
- investigationAddress?: string;
- investigationNotes?: string;
-}
-
-export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActionsProps) {
- const selectedRows = table.getFilteredSelectedRowModel().rows
- const [isLoading, setIsLoading] = React.useState(false)
-
- // Dialog 상태 관리
- const [isRequestDialogOpen, setIsRequestDialogOpen] = React.useState(false)
- const [isCancelDialogOpen, setIsCancelDialogOpen] = React.useState(false)
- const [isSendResultsDialogOpen, setIsSendResultsDialogOpen] = React.useState(false)
-
- // 초기 데이터 상태
- const [dialogInitialData, setDialogInitialData] = React.useState<InvestigationInitialData | undefined>(undefined)
-
- // 실사 의뢰 대화상자 열기 핸들러
-// 실사 의뢰 대화상자 열기 핸들러
-const handleOpenRequestDialog = async () => {
- setIsLoading(true);
- const initialData: InvestigationInitialData = {};
-
- try {
- // 선택된 행이 정확히 1개인 경우에만 초기값 설정
- if (selectedRows.length === 1) {
- const row = selectedRows[0].original;
-
- // 승인된 PQ이고 아직 실사가 없는 경우
- if (row.status === "APPROVED" && !row.investigation) {
- // Factory Location 정보 가져오기
- const locationResponse = await getFactoryLocationAnswer(
- row.vendorId,
- row.projectId
- );
-
- // 기본 주소 설정 - Factory Location 응답 또는 fallback
- let defaultAddress = "";
- if (locationResponse.success && locationResponse.factoryLocation) {
- defaultAddress = locationResponse.factoryLocation;
- } else {
- // Factory Location을 찾지 못한 경우 fallback
- defaultAddress = row.taxId ?
- `${row.vendorName} 사업장 (${row.taxId})` :
- `${row.vendorName} 사업장`;
- }
-
- // 이미 같은 회사에 대한 다른 실사가 있는지 확인
- const existingInvestigations = table.getFilteredRowModel().rows
- .map(r => r.original)
- .filter(r =>
- r.vendorId === row.vendorId &&
- r.investigation !== null
- );
-
- // 같은 업체의 이전 실사 기록이 있다면 참고하되, 주소는 Factory Location 사용
- if (existingInvestigations.length > 0) {
- // 날짜 기준으로 정렬하여 가장 최근 것을 가져옴
- const latestInvestigation = existingInvestigations.sort((a, b) => {
- const dateA = a.investigation?.createdAt || new Date(0);
- const dateB = b.investigation?.createdAt || new Date(0);
- return (dateB as Date).getTime() - (dateA as Date).getTime();
- })[0].investigation;
-
- if (latestInvestigation) {
- initialData.evaluationType = latestInvestigation.evaluationType || "SITE_AUDIT";
- initialData.qmManagerId = latestInvestigation.qmManagerId || undefined;
- initialData.investigationAddress = defaultAddress; // Factory Location 사용
-
- // 날짜는 미래로 설정
- const futureDate = new Date();
- futureDate.setDate(futureDate.getDate() + 14); // 기본값으로 2주 후
- initialData.forecastedAt = futureDate;
- }
- } else {
- // 기본값 설정
- initialData.evaluationType = "SITE_AUDIT";
- const futureDate = new Date();
- futureDate.setDate(futureDate.getDate() + 14); // 기본값으로 2주 후
- initialData.forecastedAt = futureDate;
- initialData.investigationAddress = defaultAddress; // Factory Location 사용
- }
- }
- // 실사가 이미 있고 수정하는 경우
- else if (row.investigation) {
- initialData.evaluationType = row.investigation.evaluationType || "SITE_AUDIT";
- initialData.qmManagerId = row.investigation.qmManagerId !== null ?
- row.investigation.qmManagerId : undefined;
- initialData.forecastedAt = row.investigation.forecastedAt || new Date();
- initialData.investigationAddress = row.investigation.investigationAddress || "";
- initialData.investigationNotes = row.investigation.investigationNotes || "";
- }
- }
- } catch (error) {
- console.error("초기 데이터 로드 중 오류:", error);
- toast.error("초기 데이터 로드 중 오류가 발생했습니다.");
- } finally {
- setIsLoading(false);
-
- // 초기 데이터 설정 및 대화상자 열기
- setDialogInitialData(Object.keys(initialData).length > 0 ? initialData : undefined);
- setIsRequestDialogOpen(true);
- }
-};
- // 실사 의뢰 요청 처리
- const handleRequestInvestigation = async (formData: {
- evaluationType: "SITE_AUDIT" | "QM_SELF_AUDIT",
- qmManagerId: number,
- forecastedAt: Date,
- investigationAddress: string,
- investigationNotes?: string
- }) => {
- setIsLoading(true)
- try {
- // 승인된 PQ 제출만 필터링
- const approvedPQs = selectedRows.filter(row =>
- row.original.status === "APPROVED" && !row.original.investigation
- )
-
- if (approvedPQs.length === 0) {
- toast.error("실사를 의뢰할 수 있는 업체가 없습니다. 승인된 PQ 제출만 실사 의뢰가 가능합니다.")
- return
- }
-
- // 서버 액션 호출
- const result = await requestInvestigationAction(
- approvedPQs.map(row => row.original.id),
- formData
- )
-
- if (result.success) {
- toast.success(`${result.count}개 업체에 대한 ${formData.evaluationType === "SITE_AUDIT" ? "실사의뢰평가" : "QM자체평가"}가 의뢰되었습니다.`)
- window.location.reload()
- } else {
- toast.error(result.error || "실사 의뢰 처리 중 오류가 발생했습니다.")
- }
- } catch (error) {
- console.error("실사 의뢰 중 오류 발생:", error)
- toast.error("실사 의뢰 중 오류가 발생했습니다.")
- } finally {
- setIsLoading(false)
- setIsRequestDialogOpen(false)
- setDialogInitialData(undefined); // 초기 데이터 초기화
- }
- }
-
- const handleCloseRequestDialog = () => {
- setIsRequestDialogOpen(false);
- setDialogInitialData(undefined);
- };
-
-
- // 실사 의뢰 취소 처리
- const handleCancelInvestigation = async () => {
- setIsLoading(true)
- try {
- // 실사가 계획됨 상태인 PQ만 필터링
- const plannedInvestigations = selectedRows.filter(row =>
- row.original.investigation &&
- row.original.investigation.investigationStatus === "PLANNED"
- )
-
- if (plannedInvestigations.length === 0) {
- toast.error("취소할 수 있는 실사 의뢰가 없습니다. 계획 상태의 실사만 취소할 수 있습니다.")
- return
- }
-
- // 서버 액션 호출
- const result = await cancelInvestigationAction(
- plannedInvestigations.map(row => row.original.investigation!.id)
- )
-
- if (result.success) {
- toast.success(`${result.count}개 업체에 대한 실사 의뢰가 취소되었습니다.`)
- window.location.reload()
- } else {
- toast.error(result.error || "실사 취소 처리 중 오류가 발생했습니다.")
- }
- } catch (error) {
- console.error("실사 의뢰 취소 중 오류 발생:", error)
- toast.error("실사 의뢰 취소 중 오류가 발생했습니다.")
- } finally {
- setIsLoading(false)
- setIsCancelDialogOpen(false)
- }
- }
-
- // 실사 결과 발송 처리
- const handleSendInvestigationResults = async () => {
- setIsLoading(true)
- try {
- // 완료된 실사만 필터링
- const completedInvestigations = selectedRows.filter(row =>
- row.original.investigation &&
- row.original.investigation.investigationStatus === "COMPLETED"
- )
-
- if (completedInvestigations.length === 0) {
- toast.error("발송할 실사 결과가 없습니다. 완료된 실사만 결과를 발송할 수 있습니다.")
- return
- }
-
- // 서버 액션 호출
- const result = await sendInvestigationResultsAction(
- completedInvestigations.map(row => row.original.investigation!.id)
- )
-
- if (result.success) {
- toast.success(`${result.count}개 업체에 대한 실사 결과가 발송되었습니다.`)
- window.location.reload()
- } else {
- toast.error(result.error || "실사 결과 발송 처리 중 오류가 발생했습니다.")
- }
- } catch (error) {
- console.error("실사 결과 발송 중 오류 발생:", error)
- toast.error("실사 결과 발송 중 오류가 발생했습니다.")
- } finally {
- setIsLoading(false)
- setIsSendResultsDialogOpen(false)
- }
- }
-
- // 승인된 업체 수 확인
- const approvedPQsCount = selectedRows.filter(row =>
- row.original.status === "APPROVED" && !row.original.investigation
- ).length
-
- // 계획 상태 실사 수 확인
- const plannedInvestigationsCount = selectedRows.filter(row =>
- row.original.investigation &&
- row.original.investigation.investigationStatus === "PLANNED"
- ).length
-
- // 완료된 실사 수 확인
- const completedInvestigationsCount = selectedRows.filter(row =>
- row.original.investigation &&
- row.original.investigation.investigationStatus === "COMPLETED"
- ).length
-
- return (
- <>
- <div className="flex items-center gap-2">
- {/* 실사 의뢰 버튼 */}
- <Button
- variant="outline"
- size="sm"
- onClick={handleOpenRequestDialog} // 여기를 수정: 새로운 핸들러 함수 사용
- disabled={isLoading || selectedRows.length === 0}
- className="gap-2"
- >
- <ClipboardCheck className="size-4" aria-hidden="true" />
- <span className="hidden sm:inline">실사 의뢰</span>
- </Button>
-
- {/* 실사 의뢰 취소 버튼 */}
- <Button
- variant="outline"
- size="sm"
- onClick={() => setIsCancelDialogOpen(true)}
- disabled={isLoading || selectedRows.length === 0}
- className="gap-2"
- >
- <X className="size-4" aria-hidden="true" />
- <span className="hidden sm:inline">실사 취소</span>
- </Button>
-
- {/* 실사 결과 발송 버튼 */}
- <Button
- variant="outline"
- size="sm"
- onClick={() => setIsSendResultsDialogOpen(true)}
- disabled={isLoading || selectedRows.length === 0}
- className="gap-2"
- >
- <Send className="size-4" aria-hidden="true" />
- <span className="hidden sm:inline">결과 발송</span>
- </Button>
-
- {/** Export 버튼 */}
- <Button
- variant="outline"
- size="sm"
- onClick={() =>
- exportTableToExcel(table, {
- filename: "vendors-pq-submissions",
- excludeColumns: ["select", "actions"],
- })
- }
- className="gap-2"
- >
- <Download className="size-4" aria-hidden="true" />
- <span className="hidden sm:inline">Export</span>
- </Button>
- </div>
-
- {/* 실사 의뢰 Dialog */}
- <RequestInvestigationDialog
- isOpen={isRequestDialogOpen}
- onClose={handleCloseRequestDialog} // 새로운 핸들러로 변경
- onSubmit={handleRequestInvestigation}
- selectedCount={approvedPQsCount}
- initialData={dialogInitialData} // 초기 데이터 전달
- />
-
-
- {/* 실사 취소 Dialog */}
- <CancelInvestigationDialog
- isOpen={isCancelDialogOpen}
- onClose={() => setIsCancelDialogOpen(false)}
- onConfirm={handleCancelInvestigation}
- selectedCount={plannedInvestigationsCount}
- />
-
- {/* 결과 발송 Dialog */}
- <SendResultsDialog
- isOpen={isSendResultsDialogOpen}
- onClose={() => setIsSendResultsDialogOpen(false)}
- onConfirm={handleSendInvestigationResults}
- selectedCount={completedInvestigationsCount}
- />
- </>
- )
+"use client"
+
+import * as React from "react"
+import { type Table } from "@tanstack/react-table"
+import { Download, ClipboardCheck, X, Send } from "lucide-react"
+import { toast } from "sonner"
+
+import { exportTableToExcel } from "@/lib/export"
+import { Button } from "@/components/ui/button"
+import { PQSubmission } from "./vendors-table-columns"
+import {
+ requestInvestigationAction,
+ cancelInvestigationAction,
+ sendInvestigationResultsAction,
+ getFactoryLocationAnswer
+} from "@/lib/pq/service"
+import { RequestInvestigationDialog } from "./request-investigation-dialog"
+import { CancelInvestigationDialog } from "./cancel-investigation-dialog"
+import { SendResultsDialog } from "./send-results-dialog"
+
+interface VendorsTableToolbarActionsProps {
+ table: Table<PQSubmission>
+}
+
+interface InvestigationInitialData {
+ evaluationType?: "PURCHASE_SELF_EVAL" | "DOCUMENT_EVAL" | "PRODUCT_INSPECTION" | "SITE_VISIT_EVAL";
+ qmManagerId?: number;
+ forecastedAt?: Date;
+ createdAt?: Date;
+ investigationAddress?: string;
+ investigationNotes?: string;
+}
+
+export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActionsProps) {
+ const selectedRows = table.getFilteredSelectedRowModel().rows
+ const [isLoading, setIsLoading] = React.useState(false)
+
+ // Dialog 상태 관리
+ const [isRequestDialogOpen, setIsRequestDialogOpen] = React.useState(false)
+ const [isCancelDialogOpen, setIsCancelDialogOpen] = React.useState(false)
+ const [isSendResultsDialogOpen, setIsSendResultsDialogOpen] = React.useState(false)
+
+ // 초기 데이터 상태
+ const [dialogInitialData, setDialogInitialData] = React.useState<InvestigationInitialData | undefined>(undefined)
+
+ // 실사 의뢰 대화상자 열기 핸들러
+// 실사 의뢰 대화상자 열기 핸들러
+const handleOpenRequestDialog = async () => {
+ setIsLoading(true);
+ const initialData: InvestigationInitialData = {};
+
+ try {
+ // 선택된 행이 정확히 1개인 경우에만 초기값 설정
+ if (selectedRows.length === 1) {
+ const row = selectedRows[0].original;
+
+ // 승인된 PQ이고 아직 실사가 없는 경우
+ if (row.status === "APPROVED" && !row.investigation) {
+ // Factory Location 정보 가져오기
+ const locationResponse = await getFactoryLocationAnswer(
+ row.vendorId,
+ row.projectId
+ );
+
+ // 기본 주소 설정 - Factory Location 응답 또는 fallback
+ let defaultAddress = "";
+ if (locationResponse.success && locationResponse.factoryLocation) {
+ defaultAddress = locationResponse.factoryLocation;
+ } else {
+ // Factory Location을 찾지 못한 경우 fallback
+ defaultAddress = row.taxId ?
+ `${row.vendorName} 사업장 (${row.taxId})` :
+ `${row.vendorName} 사업장`;
+ }
+
+ // 이미 같은 회사에 대한 다른 실사가 있는지 확인
+ const existingInvestigations = table.getFilteredRowModel().rows
+ .map(r => r.original)
+ .filter(r =>
+ r.vendorId === row.vendorId &&
+ r.investigation !== null
+ );
+
+ // 같은 업체의 이전 실사 기록이 있다면 참고하되, 주소는 Factory Location 사용
+ if (existingInvestigations.length > 0) {
+ // 날짜 기준으로 정렬하여 가장 최근 것을 가져옴
+ const latestInvestigation = existingInvestigations.sort((a, b) => {
+ const dateA = a.investigation?.createdAt || new Date(0);
+ const dateB = b.investigation?.createdAt || new Date(0);
+ return (dateB as Date).getTime() - (dateA as Date).getTime();
+ })[0].investigation;
+
+ if (latestInvestigation) {
+ initialData.evaluationType = latestInvestigation.evaluationType || "SITE_VISIT_EVAL";
+ initialData.qmManagerId = latestInvestigation.qmManagerId || undefined;
+ initialData.investigationAddress = defaultAddress; // Factory Location 사용
+
+ // 날짜는 미래로 설정
+ const futureDate = new Date();
+ futureDate.setDate(futureDate.getDate() + 14); // 기본값으로 2주 후
+ initialData.forecastedAt = futureDate;
+ }
+ } else {
+ // 기본값 설정
+ initialData.evaluationType = "SITE_VISIT_EVAL";
+ const futureDate = new Date();
+ futureDate.setDate(futureDate.getDate() + 14); // 기본값으로 2주 후
+ initialData.forecastedAt = futureDate;
+ initialData.investigationAddress = defaultAddress; // Factory Location 사용
+ }
+ }
+ // 실사가 이미 있고 수정하는 경우
+ else if (row.investigation) {
+ initialData.evaluationType = row.investigation.evaluationType || "SITE_VISIT_EVAL";
+ initialData.qmManagerId = row.investigation.qmManagerId !== null ?
+ row.investigation.qmManagerId : undefined;
+ initialData.forecastedAt = row.investigation.forecastedAt || new Date();
+ initialData.investigationAddress = row.investigation.investigationAddress || "";
+ initialData.investigationNotes = row.investigation.investigationNotes || "";
+ }
+ }
+ } catch (error) {
+ console.error("초기 데이터 로드 중 오류:", error);
+ toast.error("초기 데이터 로드 중 오류가 발생했습니다.");
+ } finally {
+ setIsLoading(false);
+
+ // 초기 데이터 설정 및 대화상자 열기
+ setDialogInitialData(Object.keys(initialData).length > 0 ? initialData : undefined);
+ setIsRequestDialogOpen(true);
+ }
+};
+ // 실사 의뢰 요청 처리
+ const handleRequestInvestigation = async (formData: {
+ evaluationType: "PURCHASE_SELF_EVAL" | "DOCUMENT_EVAL" | "PRODUCT_INSPECTION" | "SITE_VISIT_EVAL",
+ qmManagerId: number,
+ forecastedAt: Date,
+ investigationAddress: string,
+ investigationMethod?: string,
+ investigationNotes?: string
+ }) => {
+ setIsLoading(true)
+ try {
+ // 승인된 PQ 제출만 필터링
+ const approvedPQs = selectedRows.filter(row =>
+ row.original.status === "APPROVED" && !row.original.investigation
+ )
+
+ if (approvedPQs.length === 0) {
+ toast.error("실사를 의뢰할 수 있는 업체가 없습니다. 승인된 PQ 제출만 실사 의뢰가 가능합니다.")
+ return
+ }
+
+ // 서버 액션 호출
+ const result = await requestInvestigationAction(
+ approvedPQs.map(row => row.original.id),
+ formData
+ )
+
+ if (result.success) {
+ const evaluationTypeLabels = {
+ "PURCHASE_SELF_EVAL": "구매자체평가",
+ "DOCUMENT_EVAL": "서류평가",
+ "PRODUCT_INSPECTION": "제품검사평가",
+ "SITE_VISIT_EVAL": "방문실사평가"
+ };
+ toast.success(`${result.count}개 업체에 대한 ${evaluationTypeLabels[formData.evaluationType]}가 의뢰되었습니다.`)
+ window.location.reload()
+ } else {
+ toast.error(result.error || "실사 의뢰 처리 중 오류가 발생했습니다.")
+ }
+ } catch (error) {
+ console.error("실사 의뢰 중 오류 발생:", error)
+ toast.error("실사 의뢰 중 오류가 발생했습니다.")
+ } finally {
+ setIsLoading(false)
+ setIsRequestDialogOpen(false)
+ setDialogInitialData(undefined); // 초기 데이터 초기화
+ }
+ }
+
+ const handleCloseRequestDialog = () => {
+ setIsRequestDialogOpen(false);
+ setDialogInitialData(undefined);
+ };
+
+
+ // 실사 의뢰 취소 처리
+ const handleCancelInvestigation = async () => {
+ setIsLoading(true)
+ try {
+ // 실사가 계획됨 상태인 PQ만 필터링
+ const plannedInvestigations = selectedRows.filter(row =>
+ row.original.investigation &&
+ row.original.investigation.investigationStatus === "PLANNED"
+ )
+
+ if (plannedInvestigations.length === 0) {
+ toast.error("취소할 수 있는 실사 의뢰가 없습니다. 계획 상태의 실사만 취소할 수 있습니다.")
+ return
+ }
+
+ // 서버 액션 호출
+ const result = await cancelInvestigationAction(
+ plannedInvestigations.map(row => row.original.investigation!.id)
+ )
+
+ if (result.success) {
+ toast.success(`${result.count}개 업체에 대한 실사 의뢰가 취소되었습니다.`)
+ window.location.reload()
+ } else {
+ toast.error(result.error || "실사 취소 처리 중 오류가 발생했습니다.")
+ }
+ } catch (error) {
+ console.error("실사 의뢰 취소 중 오류 발생:", error)
+ toast.error("실사 의뢰 취소 중 오류가 발생했습니다.")
+ } finally {
+ setIsLoading(false)
+ setIsCancelDialogOpen(false)
+ }
+ }
+
+ // 실사 결과 발송 처리
+ const handleSendInvestigationResults = async (data: { purchaseComment?: string }) => {
+ try {
+ setIsLoading(true)
+
+ // 완료된 실사 중 승인된 결과만 필터링
+ const approvedInvestigations = selectedRows.filter(row =>
+ row.original.investigation &&
+ row.original.investigation.investigationStatus === "COMPLETED" &&
+ row.original.investigation.evaluationResult === "APPROVED"
+ )
+
+ if (approvedInvestigations.length === 0) {
+ toast.error("발송할 실사 결과가 없습니다. 완료되고 승인된 실사만 결과를 발송할 수 있습니다.")
+ return
+ }
+
+ // 서버 액션 호출
+ const result = await sendInvestigationResultsAction({
+ investigationIds: approvedInvestigations.map(row => row.original.investigation!.id),
+ purchaseComment: data.purchaseComment,
+ })
+
+ if (result.success) {
+ toast.success(result.message || `${result.data?.successCount || 0}개 업체에 대한 실사 결과가 발송되었습니다.`)
+ window.location.reload()
+ } else {
+ toast.error(result.error || "실사 결과 발송 처리 중 오류가 발생했습니다.")
+ }
+ } catch (error) {
+ console.error("실사 결과 발송 중 오류 발생:", error)
+ toast.error("실사 결과 발송 중 오류가 발생했습니다.")
+ } finally {
+ setIsLoading(false)
+ setIsSendResultsDialogOpen(false)
+ }
+ }
+
+ // 승인된 업체 수 확인
+ const approvedPQsCount = selectedRows.filter(row =>
+ row.original.status === "APPROVED" && !row.original.investigation
+ ).length
+
+ // 계획 상태 실사 수 확인
+ const plannedInvestigationsCount = selectedRows.filter(row =>
+ row.original.investigation &&
+ row.original.investigation.investigationStatus === "PLANNED"
+ ).length
+
+ // 완료된 실사 수 확인 (승인된 결과만)
+ const completedInvestigationsCount = selectedRows.filter(row =>
+ row.original.investigation &&
+ row.original.investigation.investigationStatus === "COMPLETED" &&
+ row.original.investigation.evaluationResult === "APPROVED"
+ ).length
+
+ // 실사 방법 라벨 변환 함수
+ const getInvestigationMethodLabel = (method: string): string => {
+ switch (method) {
+ case "PURCHASE_SELF_EVAL":
+ return "구매자체평가"
+ case "DOCUMENT_EVAL":
+ return "서류평가"
+ case "PRODUCT_INSPECTION":
+ return "제품검사평가"
+ case "SITE_VISIT_EVAL":
+ return "방문실사평가"
+ default:
+ return method
+ }
+ }
+
+ // 실사 결과 발송용 데이터 준비
+ const auditResults = selectedRows
+ .filter(row =>
+ row.original.investigation &&
+ row.original.investigation.investigationStatus === "COMPLETED" &&
+ row.original.investigation.evaluationResult === "APPROVED"
+ )
+ .map(row => {
+ const investigation = row.original.investigation!
+ const pqSubmission = row.original
+
+ return {
+ id: investigation.id,
+ vendorCode: row.original.vendorCode || "N/A",
+ vendorName: row.original.vendorName || "N/A",
+ vendorEmail: row.original.email || "N/A",
+ pqNumber: pqSubmission.pqNumber || "N/A",
+ auditItem: pqSubmission.pqItems || pqSubmission.projectName || "N/A",
+ auditFactoryAddress: investigation.investigationAddress || "N/A",
+ auditMethod: getInvestigationMethodLabel(investigation.investigationMethod || ""),
+ auditResult: investigation.evaluationResult === "APPROVED" ? "Pass(승인)" :
+ investigation.evaluationResult === "SUPPLEMENT" ? "Pass(조건부승인)" :
+ investigation.evaluationResult === "REJECTED" ? "Fail(미승인)" : "N/A",
+ additionalNotes: investigation.investigationNotes,
+ investigationNotes: investigation.investigationNotes,
+ }
+ })
+
+ return (
+ <>
+ <div className="flex items-center gap-2">
+ {/* 실사 의뢰 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleOpenRequestDialog} // 여기를 수정: 새로운 핸들러 함수 사용
+ disabled={isLoading || selectedRows.length === 0}
+ className="gap-2"
+ >
+ <ClipboardCheck className="size-4" aria-hidden="true" />
+ <span className="hidden sm:inline">실사 의뢰</span>
+ </Button>
+
+ {/* 실사 의뢰 취소 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => setIsCancelDialogOpen(true)}
+ disabled={isLoading || selectedRows.length === 0}
+ className="gap-2"
+ >
+ <X className="size-4" aria-hidden="true" />
+ <span className="hidden sm:inline">실사 취소</span>
+ </Button>
+
+ {/* 실사 결과 발송 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => setIsSendResultsDialogOpen(true)}
+ disabled={isLoading || selectedRows.length === 0}
+ className="gap-2"
+ >
+ <Send className="size-4" aria-hidden="true" />
+ <span className="hidden sm:inline">결과 발송</span>
+ </Button>
+
+ {/** Export 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() =>
+ exportTableToExcel(table, {
+ filename: "vendors-pq-submissions",
+ excludeColumns: ["select", "actions"],
+ })
+ }
+ className="gap-2"
+ >
+ <Download className="size-4" aria-hidden="true" />
+ <span className="hidden sm:inline">Export</span>
+ </Button>
+ </div>
+
+ {/* 실사 의뢰 Dialog */}
+ <RequestInvestigationDialog
+ isOpen={isRequestDialogOpen}
+ onClose={handleCloseRequestDialog} // 새로운 핸들러로 변경
+ onSubmit={handleRequestInvestigation}
+ selectedCount={approvedPQsCount}
+ initialData={dialogInitialData} // 초기 데이터 전달
+ />
+
+
+ {/* 실사 취소 Dialog */}
+ <CancelInvestigationDialog
+ isOpen={isCancelDialogOpen}
+ onClose={() => setIsCancelDialogOpen(false)}
+ onConfirm={handleCancelInvestigation}
+ selectedCount={plannedInvestigationsCount}
+ />
+
+ {/* 결과 발송 Dialog */}
+ <SendResultsDialog
+ isOpen={isSendResultsDialogOpen}
+ onClose={() => setIsSendResultsDialogOpen(false)}
+ onConfirm={handleSendInvestigationResults}
+ selectedCount={completedInvestigationsCount}
+ auditResults={auditResults}
+ />
+ </>
+ )
} \ No newline at end of file