From 8a19a6fa336768d8b6712752c9d713360067ecb0 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 8 Dec 2025 08:45:20 +0000 Subject: (최겸) 구매 피드백 수정, 안전담당자, pq항목 내 첨부, 내외자 구분, 도로명주소 api 반영(운영기준) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pq-input/pq-input-tabs.tsx | 90 ++++++++++++++++++++++++++++--- components/pq-input/pq-review-wrapper.tsx | 78 +++++++++++++++++++++++++-- 2 files changed, 159 insertions(+), 9 deletions(-) (limited to 'components/pq-input') diff --git a/components/pq-input/pq-input-tabs.tsx b/components/pq-input/pq-input-tabs.tsx index df911d5e..6c9a1254 100644 --- a/components/pq-input/pq-input-tabs.tsx +++ b/components/pq-input/pq-input-tabs.tsx @@ -152,6 +152,7 @@ export function PQInputTabs({ projectData, isReadOnly = false, currentPQ, // 추가: 현재 PQ Submission 정보 + vendorCountry, }: { data: PQGroupData[] vendorId: number @@ -163,6 +164,7 @@ export function PQInputTabs({ status: string; type: string; } | null + vendorCountry?: string | null }) { const [isSaving, setIsSaving] = React.useState(false) @@ -208,6 +210,33 @@ export function PQInputTabs({ }) } + // 벤더 내자/외자 판별 (국가 코드 기반) + const isDomesticVendor = React.useMemo(() => { + if (!vendorCountry) return null; // null 이면 필터 미적용 + return vendorCountry === "KR" || vendorCountry === "한국"; + }, [vendorCountry]); + + // 벤더 유형에 따라 PQ 항목 필터링 + const filteredData: PQGroupData[] = React.useMemo(() => { + // 벤더 타입 정보가 없으면 전체 노출 + if (isDomesticVendor === null) return data; + + const filterItemByType = (item: any) => { + const itemType = item.type || "내외자"; + if (itemType === "내외자") return true; + if (itemType === "내자") return isDomesticVendor === true; + if (itemType === "외자") return isDomesticVendor === false; + return true; + }; + + return data + .map((group) => ({ + ...group, + items: group.items.filter(filterItemByType), + })) + .filter((group) => group.items.length > 0); + }, [data, isDomesticVendor]); + // 필터링 함수 const shouldShowItem = (isSaved: boolean) => { if (filterOptions.showAll) return true; @@ -223,7 +252,7 @@ export function PQInputTabs({ function createInitialFormValues(): PQFormValues { const answers: PQFormValues["answers"] = [] - data.forEach((group) => { + filteredData.forEach((group) => { // 그룹 내 아이템들을 코드 순서로 정렬 const sortedItems = sortByCode(group.items) @@ -383,7 +412,7 @@ export function PQInputTabs({ try { const answerData = form.getValues(`answers.${answerIndex}`) const criteriaId = answerData.criteriaId - const item = data.flatMap(group => group.items).find(item => item.criteriaId === criteriaId) + const item = filteredData.flatMap(group => group.items).find(item => item.criteriaId === criteriaId) const inputFormat = item?.inputFormat || "TEXT" // Validation // 모든 항목은 필수로 처리 (isRequired 제거됨) @@ -723,7 +752,14 @@ export function PQInputTabs({ {/* 프로젝트 정보 섹션 */} {renderProjectInfo()} - + {filteredData.length === 0 ? ( +
+ 표시할 PQ 항목이 없습니다. (벤더 내/외자 구분 필터 적용) +
+ ) : ( + <> + + {/* Top Controls - Sticky Header */}
{/* Item Count Display */} @@ -810,7 +846,7 @@ export function PQInputTabs({
- {data.map((group) => { + {filteredData.map((group) => { const colorClasses = getTabColorClasses(group.groupName) return ( {/* Render each group */} - {data.map((group) => ( + {filteredData.map((group) => ( {/* 2-column grid */}
@@ -958,6 +994,46 @@ export function PQInputTabs({ + {/* 기준 첨부 파일 */} + {item.criteriaAttachments && item.criteriaAttachments.length > 0 && ( +
+ 기준 첨부파일 + + {item.criteriaAttachments.map((file, idx) => ( + + + + + {file.fileName} + {file.fileSize && ( + {prettyBytes(file.fileSize)} + )} + + { + try { + const { downloadFile } = await import('@/lib/file-download') + await downloadFile(file.filePath, file.fileName, { showToast: true }) + } catch (error) { + console.error('다운로드 오류:', error) + toast({ + title: "다운로드 실패", + description: "파일 다운로드 중 오류가 발생했습니다.", + variant: "destructive" + }) + } + }} + > + + Download + + + + ))} + +
+ )} + {/* 프로젝트별 추가 필드 (contractInfo, additionalRequirement) */} {projectId && contractInfo && (
@@ -1379,6 +1455,8 @@ export function PQInputTabs({ ))} + + )} {/* Confirmation Dialog */} @@ -1395,7 +1473,7 @@ export function PQInputTabs({
- {data.map((group, groupIndex) => ( + {filteredData.map((group, groupIndex) => (
{group.items.map((item) => { const answerObj = form diff --git a/components/pq-input/pq-review-wrapper.tsx b/components/pq-input/pq-review-wrapper.tsx index 1e172744..efb078e0 100644 --- a/components/pq-input/pq-review-wrapper.tsx +++ b/components/pq-input/pq-review-wrapper.tsx @@ -21,9 +21,10 @@ import { DialogTitle } from "@/components/ui/dialog" import { useToast } from "@/hooks/use-toast" -import { CheckCircle, AlertCircle, Paperclip, Square } from "lucide-react" +import { CheckCircle, AlertCircle, Paperclip, Square, Download } from "lucide-react" import { PQGroupData } from "@/lib/pq/service" import { approvePQAction, rejectPQAction, updateSHICommentAction, approveQMReviewAction, rejectQMReviewAction, requestPqSupplementAction } from "@/lib/pq/service" +import { FileList, FileListHeader, FileListInfo, FileListItem, FileListName, FileListDescription, FileListAction, FileListIcon } from "@/components/ui/file-list" // import * as ExcelJS from 'exceljs'; // import { saveAs } from "file-saver"; @@ -49,13 +50,15 @@ interface PQReviewWrapperProps { vendorId: number pqSubmission: PQSubmission vendorInfo?: any // 협력업체 정보 (선택사항) + vendorCountry?: string | null } export function PQReviewWrapper({ pqData, vendorId, pqSubmission, - vendorInfo + vendorInfo, + vendorCountry, }: PQReviewWrapperProps) { const router = useRouter() const { toast } = useToast() @@ -96,6 +99,32 @@ export function PQReviewWrapper({ return 0 }) } + + // 벤더 내자/외자 판별 (국가 코드 기반) + const isDomesticVendor = React.useMemo(() => { + if (!vendorCountry) return null; // 정보 없으면 필터 미적용 + return vendorCountry === "KR" || vendorCountry === "한국"; + }, [vendorCountry]); + + // 벤더 유형에 따라 PQ 항목 필터링 + const filteredData: PQGroupData[] = React.useMemo(() => { + if (isDomesticVendor === null) return pqData; + + const filterItemByType = (item: any) => { + const itemType = item.type || "내외자"; + if (itemType === "내외자") return true; + if (itemType === "내자") return isDomesticVendor === true; + if (itemType === "외자") return isDomesticVendor === false; + return true; + }; + + return pqData + .map((group) => ({ + ...group, + items: group.items.filter(filterItemByType), + })) + .filter((group) => group.items.length > 0); + }, [pqData, isDomesticVendor]); // 기존 SHI 코멘트를 로컬 상태에 초기화 @@ -482,8 +511,14 @@ export function PQReviewWrapper({ return (
+ {filteredData.length === 0 && ( +
+ 표시할 PQ 항목이 없습니다. (벤더 내/외자 구분 필터 적용) +
+ )} + {/* 그룹별 PQ 항목 표시 */} - {pqData.map((group) => ( + {filteredData.map((group) => (

{group.groupName}

@@ -530,6 +565,43 @@ export function PQReviewWrapper({
+ {item.criteriaAttachments && item.criteriaAttachments.length > 0 && ( +
+

기준 첨부파일

+ + {item.criteriaAttachments.map((file) => ( + + + + + {file.fileName} + {file.fileSize && ( + {file.fileSize} bytes + )} + + { + try { + const { downloadFile } = await import('@/lib/file-download') + await downloadFile(file.filePath, file.fileName, { showToast: true }) + } catch (error) { + toast({ + title: "다운로드 실패", + description: "파일 다운로드 중 오류가 발생했습니다.", + variant: "destructive" + }) + } + }} + > + + Download + + + + ))} + +
+ )} {/* 프로젝트별 추가 정보 */} {pqSubmission.projectId && item.contractInfo && (
-- cgit v1.2.3