diff options
14 files changed, 797 insertions, 433 deletions
diff --git a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/actions.ts b/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/actions.ts deleted file mode 100644 index 866103a6..00000000 --- a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/actions.ts +++ /dev/null @@ -1,44 +0,0 @@ -"use server"; - -import { getVendorBasicInfo } from "@/lib/vendors/service"; -import { VendorFormData } from "./types"; - -/** - * 벤더 기본정보를 가져오는 서버 액션 - */ -export async function getVendorData(vendorId: string) { - try { - const id = parseInt(vendorId); - if (isNaN(id)) { - return null; - } - - const vendorData = await getVendorBasicInfo(id); - return vendorData; - } catch (error) { - console.error("Error in getVendorData:", error); - return null; - } -} - -/** - * 벤더 기본정보를 업데이트하는 서버 액션 (향후 구현) - */ -export async function updateVendorData(vendorId: string, formData: VendorFormData) { - try { - // TODO: 실제 업데이트 로직 구현 - console.log("Updating vendor data:", { vendorId, formData }); - - // 임시로 성공 응답 반환 - return { - success: true, - message: "(개발중입니다) 벤더 정보가 성공적으로 업데이트되었습니다.", - }; - } catch (error) { - console.error("Error in updateVendorData:", error); - return { - success: false, - message: "업데이트 중 오류가 발생했습니다.", - }; - } -}
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/page.tsx b/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/page.tsx index ae63d77d..629717fb 100644 --- a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/page.tsx +++ b/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/page.tsx @@ -1,5 +1,5 @@ -import { getVendorData } from "./actions"; -import BasicInfoClient from "./basic-info-client"; +import { getVendorData } from "@/lib/vendor-basic-info/actions"; +import BasicInfoClient from "@/lib/vendor-basic-info/basic-info-client"; interface VendorBasicPageProps { params: { diff --git a/app/[lng]/partners/(partners)/sales-force-test/page.tsx b/app/[lng]/partners/(partners)/sales-force-test/page.tsx deleted file mode 100644 index 8d6cbfbc..00000000 --- a/app/[lng]/partners/(partners)/sales-force-test/page.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import path from "path"; -import { promises as fs } from "fs"; - -type PageProps = { - params: { lng: string }; -}; - -export default async function Page({ params }: PageProps) { - const filePath = path.join( - process.cwd(), - "app", - "[lng]", - "partners", - "(partners)", - "sales-force-test", - "AF_poc.html" - ); - - const html = await fs.readFile(filePath, "utf8"); - - return ( - <div className="w-full h-[100vh]"> - <iframe - title="Salesforce LWC Test" - className="w-full h-full border-0" - srcDoc={html} - /> - </div> - ); -} - - diff --git a/app/[lng]/partners/pq_new/[id]/page.tsx b/app/[lng]/partners/pq_new/[id]/page.tsx index 41c59b47..3c2858f2 100644 --- a/app/[lng]/partners/pq_new/[id]/page.tsx +++ b/app/[lng]/partners/pq_new/[id]/page.tsx @@ -160,7 +160,7 @@ export default async function PQEditPage(props: PQEditPageProps) { )} {/* PQ 입력 컴포넌트 */} - <div className={isReadOnly ? "pointer-events-none opacity-60" : ""}> + <div className={isReadOnly ? "opacity-60" : ""}> <PQInputTabs data={pqData} vendorId={idAsNumber} diff --git a/components/additional-info/join-form.tsx b/components/additional-info/join-form.tsx index 4f3998e3..220547df 100644 --- a/components/additional-info/join-form.tsx +++ b/components/additional-info/join-form.tsx @@ -1879,6 +1879,7 @@ export function InfoForm() { }, } as VendorRegularRegistration} onRefresh={handleAdditionalInfoSave} + isVendorUser={true} /> {/* 추가정보 입력 Dialog */} diff --git a/components/pq-input/pq-input-tabs.tsx b/components/pq-input/pq-input-tabs.tsx index 0c3b2276..7ae6d16a 100644 --- a/components/pq-input/pq-input-tabs.tsx +++ b/components/pq-input/pq-input-tabs.tsx @@ -159,6 +159,27 @@ export function PQInputTabs({ const shouldDisableInput = isReadOnly; + // 코드 순서로 정렬하는 함수 (1-1-1, 1-1-2, 1-2-1 순서) + const sortByCode = (items: any[]) => { + return [...items].sort((a, b) => { + const parseCode = (code: string) => { + return code.split('-').map(part => parseInt(part, 10)) + } + + const aCode = parseCode(a.code) + const bCode = parseCode(b.code) + + for (let i = 0; i < Math.max(aCode.length, bCode.length); i++) { + const aPart = aCode[i] || 0 + const bPart = bCode[i] || 0 + if (aPart !== bPart) { + return aPart - bPart + } + } + return 0 + }) + } + // ---------------------------------------------------------------------- // A) Create initial form values // Mark items as "saved" if they have existing answer or attachments @@ -167,7 +188,10 @@ export function PQInputTabs({ const answers: PQFormValues["answers"] = [] data.forEach((group) => { - group.items.forEach((item) => { + // 그룹 내 아이템들을 코드 순서로 정렬 + const sortedItems = sortByCode(group.items) + + sortedItems.forEach((item) => { // Check if the server item is already "complete" const hasExistingAnswer = item.answer && item.answer.trim().length > 0 const hasExistingAttachments = item.attachments && item.attachments.length > 0 @@ -634,7 +658,7 @@ export function PQInputTabs({ disabled={isSaving || !isAnyItemDirty || shouldDisableInput} onClick={handleSaveAll} > - {isSaving ? "Saving..." : "Save All"} + {isSaving ? "Saving..." : "임시 저장"} <Save className="ml-2 h-4 w-4" /> </Button> @@ -644,7 +668,7 @@ export function PQInputTabs({ disabled={!allSaved || isSubmitting || shouldDisableInput} onClick={handleSubmitPQ} > - {isSubmitting ? "Submitting..." : "Submit PQ"} + {isSubmitting ? "Submitting..." : "최종 제출"} <CheckCircle2 className="ml-2 h-4 w-4" /> </Button> </div> @@ -655,7 +679,7 @@ export function PQInputTabs({ <TabsContent key={group.groupName} value={group.groupName}> {/* 2-column grid */} <div className="grid grid-cols-1 md:grid-cols-2 gap-4 pb-4"> - {group.items.map((item) => { + {sortByCode(group.items).map((item) => { const { criteriaId, code, checkPoint, description, contractInfo, additionalRequirement } = item const answerIndex = getAnswerIndex(criteriaId) if (answerIndex === -1) return null @@ -670,7 +694,7 @@ export function PQInputTabs({ return ( - <Collapsible key={criteriaId} defaultOpen={!isSaved} className="w-full"> + <Collapsible key={criteriaId} defaultOpen={isReadOnly || !isSaved} className="w-full"> <Card className={isSaved ? "border-green-200" : ""}> <CardHeader className="pb-1"> <div className="flex justify-between"> diff --git a/components/pq-input/pq-review-wrapper.tsx b/components/pq-input/pq-review-wrapper.tsx index 1056189e..cc0f1b40 100644 --- a/components/pq-input/pq-review-wrapper.tsx +++ b/components/pq-input/pq-review-wrapper.tsx @@ -67,6 +67,27 @@ export function PQReviewWrapper({ const [shiComments, setShiComments] = React.useState<Record<number, string>>({}) const [isUpdatingComment, setIsUpdatingComment] = React.useState<number | null>(null) + // 코드 순서로 정렬하는 함수 (1-1-1, 1-1-2, 1-2-1 순서) + const sortByCode = (items: any[]) => { + return [...items].sort((a, b) => { + const parseCode = (code: string) => { + return code.split('-').map(part => parseInt(part, 10)) + } + + const aCode = parseCode(a.code) + const bCode = parseCode(b.code) + + for (let i = 0; i < Math.max(aCode.length, bCode.length); i++) { + const aPart = aCode[i] || 0 + const bPart = bCode[i] || 0 + if (aPart !== bPart) { + return aPart - bPart + } + } + return 0 + }) + } + // 기존 SHI 코멘트를 로컬 상태에 초기화 React.useEffect(() => { const initialComments: Record<number, string> = {} @@ -344,7 +365,7 @@ export function PQReviewWrapper({ <h3 className="text-lg font-medium">{group.groupName}</h3> <div className="grid grid-cols-1 gap-4"> - {group.items.map((item) => ( + {sortByCode(group.items).map((item) => ( <Card key={item.criteriaId}> <CardHeader> <div className="flex justify-between items-start"> diff --git a/components/vendor-regular-registrations/document-status-dialog.tsx b/components/vendor-regular-registrations/document-status-dialog.tsx index 848e4977..cfdd03fd 100644 --- a/components/vendor-regular-registrations/document-status-dialog.tsx +++ b/components/vendor-regular-registrations/document-status-dialog.tsx @@ -21,6 +21,7 @@ interface DocumentStatusDialogProps { onOpenChange: (open: boolean) => void;
registration: VendorRegularRegistration | null;
onRefresh?: () => void;
+ isVendorUser?: boolean;
}
const StatusIcon = ({ status }: { status: string | boolean }) => {
@@ -68,6 +69,7 @@ export function DocumentStatusDialog({ onOpenChange,
registration,
onRefresh,
+ isVendorUser = false,
}: DocumentStatusDialogProps) {
if (!registration) return null;
@@ -82,6 +84,11 @@ export function DocumentStatusDialog({ registrationKeys: Object.keys(registration),
fullRegistration: registration
});
+ //isvendoruser인 경우는 실사 결과 파일 다운로드 불가능
+ if (isVendorUser && docKey === "auditResult") {
+ toast.error("실사 결과 파일은 다운로드할 수 없습니다.");
+ return;
+ }
// documentFiles가 없는 경우 처리
if (!registration.documentFiles) {
diff --git a/lib/vendor-basic-info/actions.ts b/lib/vendor-basic-info/actions.ts new file mode 100644 index 00000000..8428deb9 --- /dev/null +++ b/lib/vendor-basic-info/actions.ts @@ -0,0 +1,193 @@ +"use server"; + +import { getVendorBasicInfo } from "@/lib/vendors/service"; +import { VendorFormData } from "./types"; +import { getPQDataByVendorId } from "@/lib/pq/service"; +import db from "@/db/db" +import { vendorPQSubmissions, vendorPqCriteriaAnswers, pqCriterias, vendorCriteriaAttachments, vendorAdditionalInfo } from "@/db/schema" +import { vendorBusinessContacts } from "@/db/schema" + +import { eq } from "drizzle-orm"; + +/** + * 벤더 기본정보를 가져오는 서버 액션 + */ +export async function getVendorData(vendorId: string) { + try { + const id = parseInt(vendorId); + if (isNaN(id)) { + return null; + } + + const vendorData = await getVendorBasicInfo(id); + return vendorData; + } catch (error) { + console.error("Error in getVendorData:", error); + return null; + } +} + +/** + * 벤더의 PQ 데이터를 가져오는 서버 액션 + */ +export async function getVendorPQData(vendorId: string) { + try { + const id = parseInt(vendorId); + if (isNaN(id)) { + return null; + } + + const pqData = await getPQDataByVendorId(id); + return pqData; + } catch (error) { + console.error("Error in getVendorPQData:", error); + return null; + } +} + +/** + * 벤더의 PQ 제출 데이터와 답변을 가져오는 서버 액션 + */ +export async function getVendorPQSubmissionData(vendorId: string) { + try { + const id = parseInt(vendorId); + if (isNaN(id)) { + return null; + } + + // 벤더의 모든 PQ 제출 데이터 조회 + const submissions = await db + .select() + .from(vendorPQSubmissions) + .where(eq(vendorPQSubmissions.vendorId, id)); + + if (submissions.length === 0) { + return null; + } + + // 각 제출에 대한 답변 데이터 조회 + const submissionData = await Promise.all( + submissions.map(async (submission) => { + const answers = await db + .select({ + id: vendorPqCriteriaAnswers.id, + vendorId: vendorPqCriteriaAnswers.vendorId, + criteriaId: vendorPqCriteriaAnswers.criteriaId, + projectId: vendorPqCriteriaAnswers.projectId, + answer: vendorPqCriteriaAnswers.answer, + shiComment: vendorPqCriteriaAnswers.shiComment, + vendorReply: vendorPqCriteriaAnswers.vendorReply, + createdAt: vendorPqCriteriaAnswers.createdAt, + updatedAt: vendorPqCriteriaAnswers.updatedAt, + criteriaCode: pqCriterias.code, + checkPoint: pqCriterias.checkPoint, + description: pqCriterias.description, + groupName: pqCriterias.groupName, + subGroupName: pqCriterias.subGroupName, + inputFormat: pqCriterias.inputFormat + }) + .from(vendorPqCriteriaAnswers) + .leftJoin(pqCriterias, eq(vendorPqCriteriaAnswers.criteriaId, pqCriterias.id)) + .where(eq(vendorPqCriteriaAnswers.vendorId, id)); + + // 각 답변에 대한 첨부파일 정보 조회 + const answersWithAttachments = await Promise.all( + answers.map(async (answer) => { + const attachments = await db + .select({ + id: vendorCriteriaAttachments.id, + fileName: vendorCriteriaAttachments.fileName, + originalFileName: vendorCriteriaAttachments.originalFileName, + filePath: vendorCriteriaAttachments.filePath, + fileType: vendorCriteriaAttachments.fileType, + fileSize: vendorCriteriaAttachments.fileSize + }) + .from(vendorCriteriaAttachments) + .where(eq(vendorCriteriaAttachments.vendorCriteriaAnswerId, answer.id)); + + return { + ...answer, + uploadedFiles: attachments + }; + }) + ); + + return { + submission, + answers: answersWithAttachments + }; + }) + ); + + return submissionData; + } catch (error) { + console.error("Error in getVendorPQSubmissionData:", error); + return null; + } +} + +/** + * 벤더의 추가정보를 가져오는 서버 액션 + */ +export async function getVendorAdditionalInfo(vendorId: string) { + try { + const id = parseInt(vendorId); + if (isNaN(id)) { + return null; + } + + const additionalInfo = await db + .select() + .from(vendorAdditionalInfo) + .where(eq(vendorAdditionalInfo.vendorId, id)); + + return additionalInfo.length > 0 ? additionalInfo[0] : null; + } catch (error) { + console.error("Error in getVendorAdditionalInfo:", error); + return null; + } +} + +/** + * 벤더의 업무담당자 정보를 가져오는 서버 액션 + */ +export async function getVendorBusinessContacts(vendorId: string) { + try { + const id = parseInt(vendorId); + if (isNaN(id)) { + return []; + } + + const rows = await db + .select() + .from(vendorBusinessContacts) + .where(eq(vendorBusinessContacts.vendorId, id)); + + return rows; + } catch (error) { + console.error("Error in getVendorBusinessContacts:", error); + return []; + } +} + +/** + * 벤더 기본정보를 업데이트하는 서버 액션 (향후 구현) + */ +export async function updateVendorData(vendorId: string, formData: VendorFormData) { + try { + // TODO: 실제 업데이트 로직 구현 + console.log("Updating vendor data:", { vendorId, formData }); + + // 임시로 성공 응답 반환 + return { + success: true, + message: "(개발중입니다) 벤더 정보가 성공적으로 업데이트되었습니다.", + }; + } catch (error) { + console.error("Error in updateVendorData:", error); + return { + success: false, + message: "업데이트 중 오류가 발생했습니다.", + }; + } +}
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/basic-info-client.tsx b/lib/vendor-basic-info/basic-info-client.tsx index 78d21719..ce8e4dfc 100644 --- a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/basic-info-client.tsx +++ b/lib/vendor-basic-info/basic-info-client.tsx @@ -6,6 +6,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Select, SelectContent, @@ -16,7 +17,8 @@ import { import { Edit, Save, X } from "lucide-react"; import { toast } from "sonner"; import { VendorData, VendorFormData, VendorAttachment } from "./types"; -import { updateVendorData } from "./actions"; +import { updateVendorData, getVendorPQData, getVendorPQSubmissionData, getVendorAdditionalInfo } from "./actions"; +import { getVendorBusinessContacts } from "./actions"; import { noDataString } from "./constants"; import { PQSimpleDialog } from "@/components/vendor-info/pq-simple-dialog"; import { SiteVisitDetailDialog } from "@/lib/site-visit/site-visit-detail-dialog"; @@ -204,18 +206,20 @@ const OrganizationChart = ({ data, editMode = false, onChange, + pqData, }: { data: any; editMode?: boolean; onChange?: (field: string, value: string) => void; + pqData: any[]; }) => { const organizationFields = [ - { key: "representative", label: "대표" }, - { key: "sales", label: "영업" }, - { key: "design", label: "설계" }, - { key: "procurement", label: "구매" }, - { key: "production", label: "생산" }, - { key: "quality", label: "품질" }, + { key: "representative", label: "대표", code: "1-5-1" }, + { key: "sales", label: "영업", code: "1-5-2" }, + { key: "design", label: "설계", code: "1-5-3" }, + { key: "procurement", label: "구매", code: "1-5-4" }, + { key: "production", label: "생산", code: "1-5-5" }, + { key: "quality", label: "품질", code: "1-5-6" }, ]; return ( @@ -237,7 +241,7 @@ const OrganizationChart = ({ /> ) : ( <span className="text-sm text-muted-foreground"> - {data?.[field.key]?.toString() || noDataString} + {pqData && Array.isArray(pqData) && pqData.find((a: any) => a.criteriaCode === field.code)?.answer || data?.[field.key]?.toString() || noDataString} </span> )} </div> @@ -337,6 +341,10 @@ export default function BasicInfoClient({ const [attachmentsByType, setAttachmentsByType] = useState<Record<string, any[]>>({}); const [periodicGrade, setPeriodicGrade] = useState<string | null>(null); const [vendorTypeInfo, setVendorTypeInfo] = useState<any>(null); + const [pqData, setPqData] = useState<any[]>([]); + const [pqSubmissionData, setPqSubmissionData] = useState<any[]>([]); + const [additionalInfo, setAdditionalInfo] = useState<any>(null); + const [businessContacts, setBusinessContacts] = useState<any[]>([]); const [formData, setFormData] = useState<VendorFormData>({ vendorName: initialData?.vendorName || "", representativeName: initialData?.representativeName || "", @@ -360,19 +368,19 @@ export default function BasicInfoClient({ }); const handleSave = () => { - startTransition(async () => { - try { - const result = await updateVendorData(vendorId, formData); - if (result.success) { - toast.success("[개발중] 저장되지 않습니다. 업데이트는 구현중입니다."); - setEditMode(false); - } else { - toast.error(result.message || "저장에 실패했습니다."); - } - } catch { - toast.error("저장 중 오류가 발생했습니다."); - } - }); + // startTransition(async () => { + // try { + // const result = await updateVendorData(vendorId, formData); + // if (result.success) { + // toast.success("[개발중] 저장되지 않습니다. 업데이트는 구현중입니다."); + // setEditMode(false); + // } else { + // toast.error(result.message || "저장에 실패했습니다."); + // } + // } catch { + // toast.error("저장 중 오류가 발생했습니다."); + // } + // }); }; const handleCancel = () => { @@ -407,27 +415,6 @@ export default function BasicInfoClient({ setFormData((prev) => ({ ...prev, [field]: value })); }; - // PQ 조회 핸들러 - const handlePQView = () => { - setPqDialogOpen(true); - }; - - // 실사 정보 조회 핸들러 - const handleSiteVisitView = async () => { - try { - const siteVisitRequests = await getSiteVisitRequestsByVendorId(parseInt(vendorId)); - if (siteVisitRequests.length === 0) { - toast.info("실사 정보가 없습니다."); - return; - } - setSelectedSiteVisitRequest(siteVisitRequests[0]); // 첫 번째 실사 정보 선택 - setSiteVisitDialogOpen(true); - } catch (error) { - console.error("실사 정보 조회 오류:", error); - toast.error("실사 정보를 불러오는데 실패했습니다."); - } - }; - // 기본계약 현황 조회 핸들러 const handleContractView = async () => { try { @@ -477,32 +464,6 @@ export default function BasicInfoClient({ } }; - // 추가정보 조회 핸들러 - const handleAdditionalInfoView = async () => { - try { - const result = await fetchVendorRegistrationStatus(parseInt(vendorId)); - if (!result.success || !result.data) { - toast.info("추가정보가 없습니다."); - return; - } - - // 추가정보가 있는지 확인 (업무담당자 또는 추가정보 데이터가 있는지 체크) - const { businessContacts, additionalInfo } = result.data; - const hasBusinessContacts = businessContacts && businessContacts.length > 0; - const hasAdditionalInfo = additionalInfo && Object.keys(additionalInfo).length > 0; - - if (!hasBusinessContacts && !hasAdditionalInfo) { - toast.info("추가정보가 없습니다."); - return; - } - - setAdditionalInfoDialogOpen(true); - } catch (error) { - console.error("추가정보 조회 오류:", error); - toast.error("추가정보를 불러오는데 실패했습니다."); - } - }; - // 첨부파일 및 평가 정보 로드 const loadVendorData = async () => { try { @@ -523,6 +484,28 @@ export default function BasicInfoClient({ if (typeResult.success && typeResult.data) { setVendorTypeInfo(typeResult.data); } + + // PQ 데이터 조회 + const pqResult = await getVendorPQData(vendorId); + if (pqResult) { + setPqData(pqResult); + } + + // PQ 제출 데이터 조회 + const pqSubmissionResult = await getVendorPQSubmissionData(vendorId); + if (pqSubmissionResult) { + setPqSubmissionData(pqSubmissionResult); + } + + // 추가정보 조회 + const additionalInfoResult = await getVendorAdditionalInfo(vendorId); + if (additionalInfoResult) { + setAdditionalInfo(additionalInfoResult); + } + + // 업무담당자 정보 조회 + const contacts = await getVendorBusinessContacts(vendorId); + setBusinessContacts(contacts || []); } catch (error) { console.error("벤더 데이터 로드 오류:", error); } @@ -553,6 +536,121 @@ export default function BasicInfoClient({ } }; + // PQ 데이터에서 실사 정보 추출 + const extractFactoryInfo = (pqData: any[]) => { + const factoryInfo = { + factoryAddress: "", + factoryPhone: "", + factoryFax: "", + factoryPIC: "", + factoryPICPosition: "", + factoryPICContact: "", + factoryPICEmail: "", + mainSupplyItems: "", + inspectionResult: "", + inspectionDate: "", + inspectionFiles: [] as any[] + }; + + if (!pqData || !Array.isArray(pqData)) { + return factoryInfo; + } + + pqData.forEach(group => { + if (group && group.items && Array.isArray(group.items)) { + group.items.forEach((item: any) => { + const code = item.code; + const answer = item.answer; + const files = item.uploadedFiles || []; + + // 공장주소 (1-4-1) + if (code === "1-4-1") { + factoryInfo.factoryAddress = answer || ""; + } + // 공장 전화 (1-4-2) + else if (code === "1-4-2") { + factoryInfo.factoryPhone = answer || ""; + } + // 공장 팩스 (1-4-3) + else if (code === "1-4-3") { + factoryInfo.factoryFax = answer || ""; + } + // 공장 대표/담당자 이름 (1-4-4) + else if (code === "1-4-4") { + factoryInfo.factoryPIC = answer || ""; + } + // 공장 대표/담당자 직책 (1-4-5) + else if (code === "1-4-5") { + factoryInfo.factoryPICPosition = answer || ""; + } + // 공장 대표/담당자 전화 (1-4-6) + else if (code === "1-4-6") { + factoryInfo.factoryPICContact = answer || ""; + } + // 공장 대표/담당자 이메일 (1-4-7) + else if (code === "1-4-7") { + factoryInfo.factoryPICEmail = answer || ""; + } + // 공급품목 (첫 번째 것만 가져오기) + else if (code.startsWith("1-5") && !factoryInfo.mainSupplyItems) { + try { + const supplyItems = JSON.parse(answer || "[]"); + if (Array.isArray(supplyItems) && supplyItems.length > 0) { + factoryInfo.mainSupplyItems = supplyItems[0].name || supplyItems[0] || ""; + } + } catch { + factoryInfo.mainSupplyItems = answer || ""; + } + } + // 실사 결과 + else if (code.startsWith("4-") && answer && !factoryInfo.inspectionResult) { + factoryInfo.inspectionResult = answer; + factoryInfo.inspectionFiles = files; + } + }); + } + }); + + return factoryInfo; + }; + + // PQ 제출 데이터에서 특정 코드의 답변 가져오기 + const getPQAnswerByCode = (targetCode: string) => { + if (!pqSubmissionData || !Array.isArray(pqSubmissionData)) { + return ""; + } + + for (const submission of pqSubmissionData) { + if (submission && submission.answers && Array.isArray(submission.answers)) { + const answer = submission.answers.find((a: any) => a.criteriaCode === targetCode); + if (answer) { + return answer.answer; + } + } + } + return ""; + }; + + // PQ 제출 데이터에서 특정 코드의 첨부파일 가져오기 + const getPQAttachmentsByCode = (targetCode: string) => { + const files: any[] = []; + + if (!pqSubmissionData || !Array.isArray(pqSubmissionData)) { + return files; + } + + for (const submission of pqSubmissionData) { + if (submission && submission.answers && Array.isArray(submission.answers)) { + const answer = submission.answers.find((a: any) => a.criteriaCode === targetCode); + if (answer && answer.uploadedFiles) { + files.push(...answer.uploadedFiles); + } + } + } + + return files; + }; + // 첨부파일 관리 핸들러 (타입별) const handleAttachmentFileManagement = (attachmentType: string, typeName: string) => { const files = attachmentsByType[attachmentType] || []; @@ -615,13 +713,22 @@ export default function BasicInfoClient({ </Button> </> ) : ( - <Button - onClick={() => setEditMode(true)} - className="flex items-center gap-1" - > - <Edit className="w-4 h-4" /> - 수정 - </Button> + <> + <Button + onClick={() => setEditMode(true)} + className="flex items-center gap-1" + > + <Edit className="w-4 h-4" /> + 수정 + </Button> + <Button + variant="outline" + onClick={handleContractView} + className="flex items-center gap-1" + > + 정규업체등록 현황 + </Button> + </> )} </div> </div> @@ -798,8 +905,6 @@ export default function BasicInfoClient({ } /> - <Separator /> - {/* 첨부파일 */} <WideInfoSection title="첨부파일" @@ -819,7 +924,7 @@ export default function BasicInfoClient({ className="mt-2" onClick={() => handleAttachmentFileManagement("BUSINESS_REGISTRATION", "사업자등록증")} > - 파일 보기 + 파일 다운로드 </Button> )} </div> @@ -839,7 +944,7 @@ export default function BasicInfoClient({ className="mt-2" onClick={() => handleAttachmentFileManagement("CREDIT_REPORT", "신용평가보고서")} > - 파일 보기 + 파일 다운로드 </Button> )} </div> @@ -859,7 +964,7 @@ export default function BasicInfoClient({ className="mt-2" onClick={() => handleAttachmentFileManagement("BANK_ACCOUNT_COPY", "통장사본")} > - 파일 보기 + 파일 다운로드 </Button> )} </div> @@ -879,7 +984,7 @@ export default function BasicInfoClient({ className="mt-2" onClick={() => handleAttachmentFileManagement("ISO_CERTIFICATION", "ISO 인증서")} > - 파일 보기 + 파일 다운로드 </Button> )} </div> @@ -899,7 +1004,7 @@ export default function BasicInfoClient({ className="mt-2" onClick={() => handleAttachmentFileManagement("GENERAL", "기타 첨부파일")} > - 파일 보기 + 파일 다운로드 </Button> )} </div> @@ -908,10 +1013,9 @@ export default function BasicInfoClient({ } /> - <Separator /> {/* 상세정보 */} - {/* <InfoSection + <InfoSection title="상세정보" column1={ <div className="space-y-2"> @@ -936,13 +1040,16 @@ export default function BasicInfoClient({ /> <InfoItem title="대표자 생년월일" - value={formData.representativeBirth || null} + value={formData.representativeBirth || ""} isEditable={true} + editMode={editMode} + fieldKey="representativeBirth" + onChange={(value) => updateField("representativeBirth", value)} /> <InfoItem title="임직원수" - value={formData.employeeCount.toString() || null} - isEditable={true} + value={getPQAnswerByCode("1-8-3") || formData.employeeCount.toString()} + type="readonly" /> </div> } @@ -958,13 +1065,16 @@ export default function BasicInfoClient({ /> <InfoItem title="대표자 주소" - value={formData.address || null} + value={formData.address || ""} isEditable={true} + editMode={editMode} + fieldKey="address" + onChange={(value) => updateField("address", value)} /> <InfoItem title="연간 매출" - value={initialData.capacityInfo?.annualSales || null} - isEditable={true} + value={getPQAnswerByCode("1-7-1") || getPQAnswerByCode("1-7-2") || initialData.capacityInfo?.annualSales || ""} + type="readonly" /> </div> } @@ -980,8 +1090,8 @@ export default function BasicInfoClient({ /> <InfoItem title="생산능력" - value={initialData.capacityInfo?.productionCapacity || null} - isEditable={true} + value={getPQAnswerByCode("1-9-1") || getPQAnswerByCode("1-9-2") || initialData.capacityInfo?.productionCapacity || ""} + type="readonly" /> </div> } @@ -990,49 +1100,80 @@ export default function BasicInfoClient({ <OrganizationChart data={initialData.organization} editMode={editMode} + pqData={pqSubmissionData && Array.isArray(pqSubmissionData) ? pqSubmissionData.flatMap(s => s.answers || []) : []} onChange={(field, value) => { // TODO: 조직도 업데이트 로직 구현 - toast.info( - `[개발중] 조직도 ${field} 필드 업데이트 기능을 구현 예정입니다.` - ); }} /> <div className="flex flex-col items-center gap-3"> <div className="text-sm font-semibold text-center"> - 관련 정보 + 관련 첨부파일 </div> <div className="space-y-2"> <Button variant="outline" className="text-xs w-32 flex items-center gap-2" - onClick={() => handleFileManagement("협력업체정보")} + onClick={() => { + const files = getPQAttachmentsByCode("1-10"); + if (files.length > 0) { + files.forEach((file, index) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, index * 500); + }); + } else { + toast.info("협력업체정보 파일이 없습니다."); + } + }} > - 협력업체정보 + 협력업체정보 ({getPQAttachmentsByCode("1-10").length}건) </Button> <Button variant="outline" className="text-xs w-32 flex items-center gap-2" - onClick={() => handleFileManagement("외주화정보")} + onClick={() => { + const files = getPQAttachmentsByCode("1-12"); + if (files.length > 0) { + files.forEach((file, index) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, index * 500); + }); + } else { + toast.info("외주화정보 파일이 없습니다."); + } + }} > - 외주화정보 + 외주화정보 ({getPQAttachmentsByCode("1-12").length}건) </Button> <Button variant="outline" className="text-xs w-32 flex items-center gap-2" - onClick={() => handleFileManagement("A/S 네트워크")} + onClick={() => { + const files = getPQAttachmentsByCode("1-13"); + if (files.length > 0) { + files.forEach((file, index) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, index * 500); + }); + } else { + toast.info("A/S 네트워크 파일이 없습니다."); + } + }} > - A/S 네트워크 + A/S 네트워크 ({getPQAttachmentsByCode("1-13").length}건) </Button> </div> </div> </div> } - /> */} + /> {/* <Separator /> */} {/* 매출정보 */} - {/* <WideInfoSection + <WideInfoSection title="매출정보" subtitle="(3개년)" noPadding={true} @@ -1176,283 +1317,334 @@ export default function BasicInfoClient({ </TableBody> </Table> } - /> */} + /> {/* <Separator /> */} {/* 실사정보 */} - {/* <InfoSection - title="실사정보" - subtitle="(3년)" + {pqSubmissionData && pqSubmissionData.length > 0 && pqSubmissionData.map((submission, index) => { + const factoryInfo = extractFactoryInfo([{ + groupName: "Factory Info", + items: submission.answers.map((answer: any) => ({ + code: answer.criteriaCode, + answer: answer.answer, + uploadedFiles: answer.uploadedFiles || [] + })) + }]); + const inspectionFiles = getPQAttachmentsByCode("4-1"); + + return ( + <InfoSection + key={submission.submission.id} + title="실사정보" + subtitle={`${submission.submission.type || "일반"} - ${submission.submission.status || "상태없음"}`} + column1={ + <div className="space-y-2"> + <InfoItem + title="공장주소" + value={factoryInfo.factoryAddress || ""} + type="readonly" + /> + <InfoItem + title="공장 전화" + value={factoryInfo.factoryPhone || ""} + type="readonly" + /> + <InfoItem + title="공장 팩스" + value={factoryInfo.factoryFax || ""} + type="readonly" + /> + </div> + } + column2={ + <div className="space-y-2"> + <InfoItem + title="공장 담당자" + value={ + factoryInfo.factoryPIC + ? `${factoryInfo.factoryPIC} [${factoryInfo.factoryPICPosition || ""}] [${factoryInfo.factoryPICContact || ""}] [${factoryInfo.factoryPICEmail || ""}]` + : "" + } + type="readonly" + /> + <InfoItem + title="실사결과" + value={factoryInfo.inspectionResult || ""} + type="readonly" + /> + {inspectionFiles.length > 0 && ( + <Button + variant="outline" + size="sm" + onClick={() => { + inspectionFiles.forEach((file, idx) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, idx * 500); + }); + }} + > + 실사결과 파일 다운로드 ({inspectionFiles.length}건) + </Button> + )} + </div> + } + column3={ + <div className="flex flex-col gap-2"> + <div className="space-y-2"> + <InfoItem + title="대표공급품목" + value={factoryInfo.mainSupplyItems || ""} + type="readonly" + /> + </div> + </div> + } + additionalContent={ + <div className="grid grid-cols-5 gap-4 min-w-0 overflow-x-auto"> + <div className="text-center min-w-0"> + <div className="text-sm font-medium mb-2 break-words"> + 공장소개자료 + </div> + <div className="text-sm text-muted-foreground"> + {getPQAttachmentsByCode("1-4").length}건 + </div> + {getPQAttachmentsByCode("1-4").length > 0 && ( + <Button + variant="outline" + size="sm" + className="mt-2" + onClick={() => { + const files = getPQAttachmentsByCode("1-4"); + files.forEach((file, idx) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, idx * 500); + }); + }} + > + 파일 다운로드 + </Button> + )} + </div> + <div className="text-center min-w-0"> + <div className="text-sm font-medium mb-2 break-words"> + QMS Cert + </div> + <div className="text-sm text-muted-foreground"> + {getPQAttachmentsByCode("2-1").length}건 + </div> + {getPQAttachmentsByCode("2-1").length > 0 && ( + <Button + variant="outline" + size="sm" + className="mt-2" + onClick={() => { + const files = getPQAttachmentsByCode("2-1"); + files.forEach((file, idx) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, idx * 500); + }); + }} + > + 파일 다운로드 + </Button> + )} + </div> + <div className="text-center min-w-0"> + <div className="text-sm font-medium mb-2 break-words"> + Product Cert + </div> + <div className="text-sm text-muted-foreground"> + {getPQAttachmentsByCode("2-2").length}건 + </div> + {getPQAttachmentsByCode("2-2").length > 0 && ( + <Button + variant="outline" + size="sm" + className="mt-2" + onClick={() => { + const files = getPQAttachmentsByCode("2-2"); + files.forEach((file, idx) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, idx * 500); + }); + }} + > + 파일 다운로드 + </Button> + )} + </div> + <div className="text-center min-w-0"> + <div className="text-sm font-medium mb-2 break-words"> + Ex. Cert + </div> + <div className="text-sm text-muted-foreground"> + {getPQAttachmentsByCode("2-17").length}건 + </div> + {getPQAttachmentsByCode("2-17").length > 0 && ( + <Button + variant="outline" + size="sm" + className="mt-2" + onClick={() => { + const files = getPQAttachmentsByCode("2-17"); + files.forEach((file, idx) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, idx * 500); + }); + }} + > + 파일 다운로드 + </Button> + )} + </div> + <div className="text-center min-w-0"> + <div className="text-sm font-medium mb-2 break-words"> + HSE Cert + </div> + <div className="text-sm text-muted-foreground"> + {getPQAttachmentsByCode("3-1").length}건 + </div> + {getPQAttachmentsByCode("3-1").length > 0 && ( + <Button + variant="outline" + size="sm" + className="mt-2" + onClick={() => { + const files = getPQAttachmentsByCode("3-1"); + files.forEach((file, idx) => { + setTimeout(() => { + handleAttachmentDownload(file.filePath, file.fileName); + }, idx * 500); + }); + }} + > + 파일 다운로드 + </Button> + )} + </div> + </div> + } + /> + ); + })} + {/* 추가정보 */} + <InfoSection + title="추가정보" + // subtitle="정규업체 등록 시 입력된 정보" column1={ <div className="space-y-2"> <InfoItem - title="공장주소" - value={initialData.factoryInfo?.factoryAddress || null} + title="사업유형" + value={additionalInfo?.businessType || ""} + type="readonly" /> <InfoItem - title="공장설립일" - value={ - initialData.factoryInfo?.factoryEstablishmentDate || null - } + title="산업유형" + value={additionalInfo?.industryType || ""} + type="readonly" + /> + <InfoItem + title="회사규모" + value={additionalInfo?.companySize || ""} + type="readonly" /> </div> } column2={ <div className="space-y-2"> <InfoItem - title="공장 담당자" - value={ - initialData.factoryInfo?.factoryPIC - ? `${initialData.factoryInfo.factoryPIC} [${ - initialData.factoryInfo.factoryPICContact || "" - }] [${initialData.factoryInfo.factoryPICEmail || ""}]` - : null - } + title="매출액" + value={additionalInfo?.revenue ? `${additionalInfo.revenue.toLocaleString()}원` : ""} + type="readonly" /> <InfoItem - title="실사결과" - value={ - initialData.inspectionInfo?.inspectionResult - ? `${initialData.inspectionInfo.inspectionResult} (${ - initialData.inspectionInfo.inspectionDate || "" - })` - : null - } + title="공장설립일" + value={additionalInfo?.factoryEstablishedDate ? new Date(additionalInfo.factoryEstablishedDate).toLocaleDateString('ko-KR') : ""} + type="readonly" + /> + <InfoItem + title="선호계약조건" + value={additionalInfo?.preferredContractTerms || ""} + type="readonly" /> </div> } column3={ - <div className="flex flex-col gap-2"> - <div className="space-y-2"> - <InfoItem - title="대표공급품목" - value={initialData.capacityInfo?.mainSupplyItems || null} - /> - </div> - <Button - variant="outline" - onClick={() => handleFileManagement("대표공급품목")} - > - 대표 공급품목 상세보기 - </Button> - </div> - } - additionalContent={ - <div className="grid grid-cols-5 gap-4 min-w-0 overflow-x-auto"> - <div className="text-center min-w-0"> - <div className="text-sm font-medium mb-2 break-words"> - 공정소개자료 - </div> - <div className="text-sm text-muted-foreground"> - {attachmentsByType.BUSINESS_REGISTRATION?.length || 0}건 - </div> - </div> - <div className="text-center min-w-0"> - <div className="text-sm font-medium mb-2 break-words"> - QMS Cert - </div> - <div className="text-sm text-muted-foreground"> - {attachmentsByType.ISO_CERTIFICATION?.length || 0}건 - </div> - </div> - <div className="text-center min-w-0"> - <div className="text-sm font-medium mb-2 break-words"> - Product Cert - </div> - <div className="text-sm text-muted-foreground"> - {attachmentsByType.PRODUCT_CERT?.length || 0}건 - </div> - </div> - <div className="text-center min-w-0"> - <div className="text-sm font-medium mb-2 break-words"> - Ex. Cert - </div> - <div className="text-sm text-muted-foreground"> - {attachmentsByType.EX_CERT?.length || 0}건 - </div> - </div> - <div className="text-center min-w-0"> - <div className="text-sm font-medium mb-2 break-words"> - HSE Cert - </div> - <div className="text-sm text-muted-foreground"> - {attachmentsByType.HSE_CERT?.length || 0}건 - </div> - </div> + <div className="space-y-2"> + {/* 추가 정보가 더 있다면 여기에 배치 */} </div> } - /> */} - - {/* <Separator /> */} - - {/* 계약정보 */} - {/* <InfoSection - title="계약정보" + /> + {/* 업무담당자 */} + <InfoSection + title="업무담당자" column1={ - <div className="space-y-2"> - <InfoItem - title="정규등록현황" - value={ - initialData.contractDetails?.regularRegistrationStatus || null - } - /> + <div className="space-y-3"> + <div className="space-y-1"> + <div className="text-sm font-medium">영업 담당자</div> + <InfoItem title="이름" value={businessContacts.find(c => c.contactType === "sales")?.contactName || ""} type="readonly" /> + <InfoItem title="직급" value={businessContacts.find(c => c.contactType === "sales")?.position || ""} type="readonly" /> + <InfoItem title="부서" value={businessContacts.find(c => c.contactType === "sales")?.department || ""} type="readonly" /> + <InfoItem title="담당업무" value={businessContacts.find(c => c.contactType === "sales")?.responsibility || ""} type="readonly" /> + <InfoItem title="E-mail" value={businessContacts.find(c => c.contactType === "sales")?.email || ""} type="readonly" /> + </div> </div> } column2={ - <div className="space-y-2"> - <InfoItem - title="선호 계약조건" - value={ - initialData.contractDetails?.preferredContractTerms || null - } - /> + <div className="space-y-3"> + <div className="space-y-1"> + <div className="text-sm font-medium">설계 담당자</div> + <InfoItem title="이름" value={businessContacts.find(c => c.contactType === "design")?.contactName || ""} type="readonly" /> + <InfoItem title="직급" value={businessContacts.find(c => c.contactType === "design")?.position || ""} type="readonly" /> + <InfoItem title="부서" value={businessContacts.find(c => c.contactType === "design")?.department || ""} type="readonly" /> + <InfoItem title="담당업무" value={businessContacts.find(c => c.contactType === "design")?.responsibility || ""} type="readonly" /> + <InfoItem title="E-mail" value={businessContacts.find(c => c.contactType === "design")?.email || ""} type="readonly" /> + </div> + <div className="space-y-1"> + <div className="text-sm font-medium">납기 담당자</div> + <InfoItem title="이름" value={businessContacts.find(c => c.contactType === "delivery")?.contactName || ""} type="readonly" /> + <InfoItem title="직급" value={businessContacts.find(c => c.contactType === "delivery")?.position || ""} type="readonly" /> + <InfoItem title="부서" value={businessContacts.find(c => c.contactType === "delivery")?.department || ""} type="readonly" /> + <InfoItem title="담당업무" value={businessContacts.find(c => c.contactType === "delivery")?.responsibility || ""} type="readonly" /> + <InfoItem title="E-mail" value={businessContacts.find(c => c.contactType === "delivery")?.email || ""} type="readonly" /> + </div> </div> } column3={ - <div className="space-y-2"> - <InfoItem - title="최근 거래현황" - value={ - initialData.contractDetails?.recentTransactionStatus || null - } - /> - </div> - } - additionalContent={ - <div className="grid grid-cols-10 gap-4 min-w-0 overflow-x-auto"> - {[ - { - title: "준법서약", - value: - initialData.contractDetails?.compliancePledgeDate || null, - }, - { - title: "기술자료", - value: initialData.contractDetails?.technicalDataDate || null, - }, - { - title: "비밀유지", - value: - initialData.contractDetails?.confidentialityDate || null, - }, - { - title: "GTC", - value: initialData.contractDetails?.gtcDate || null, - }, - { - title: "표준하도급", - value: - initialData.contractDetails?.standardSubcontractDate || - null, - }, - { - title: "안전보건", - value: initialData.contractDetails?.safetyHealthDate || null, - }, - { - title: "직납자재", - value: - initialData.contractDetails?.directMaterialDate || null, - }, - { - title: "내국신용장", - value: initialData.contractDetails?.domesticLCDate || null, - }, - { - title: "동반성장", - value: initialData.contractDetails?.mutualGrowthDate || null, - }, - { - title: "윤리규범", - value: initialData.contractDetails?.ethicsDate || null, - }, - ].map((item, index) => ( - <div key={index} className="text-center min-w-0"> - <div className="text-sm font-medium mb-2 break-words"> - {item.title} - </div> - <div className="text-sm text-muted-foreground"> - {item.value || "-"} - </div> - </div> - ))} + <div className="space-y-3"> + <div className="space-y-1"> + <div className="text-sm font-medium">품질 담당자</div> + <InfoItem title="이름" value={businessContacts.find(c => c.contactType === "quality")?.contactName || ""} type="readonly" /> + <InfoItem title="직급" value={businessContacts.find(c => c.contactType === "quality")?.position || ""} type="readonly" /> + <InfoItem title="부서" value={businessContacts.find(c => c.contactType === "quality")?.department || ""} type="readonly" /> + <InfoItem title="담당업무" value={businessContacts.find(c => c.contactType === "quality")?.responsibility || ""} type="readonly" /> + <InfoItem title="E-mail" value={businessContacts.find(c => c.contactType === "quality")?.email || ""} type="readonly" /> + </div> + <div className="space-y-1"> + <div className="text-sm font-medium">세금계산서 담당자</div> + <InfoItem title="이름" value={businessContacts.find(c => c.contactType === "tax_invoice")?.contactName || ""} type="readonly" /> + <InfoItem title="직급" value={businessContacts.find(c => c.contactType === "tax_invoice")?.position || ""} type="readonly" /> + <InfoItem title="부서" value={businessContacts.find(c => c.contactType === "tax_invoice")?.department || ""} type="readonly" /> + <InfoItem title="담당업무" value={businessContacts.find(c => c.contactType === "tax_invoice")?.responsibility || ""} type="readonly" /> + <InfoItem title="E-mail" value={businessContacts.find(c => c.contactType === "tax_invoice")?.email || ""} type="readonly" /> + </div> </div> } - /> */} - - - - {/* 추가 조회 기능 버튼들 */} - <div className="border rounded-lg p-6"> - <div className="text-lg font-semibold mb-4">상세 정보 조회</div> - <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> - <Button - variant="outline" - onClick={handlePQView} - className="h-20 flex flex-col items-center justify-center space-y-2" - > - <div className="text-sm font-medium">PQ 조회</div> - <div className="text-xs text-muted-foreground">제출된 PQ 정보 확인</div> - </Button> - - <Button - variant="outline" - onClick={handleSiteVisitView} - className="h-20 flex flex-col items-center justify-center space-y-2" - > - <div className="text-sm font-medium">실사 정보</div> - <div className="text-xs text-muted-foreground">협력업체 방문실사 조회</div> - </Button> - - <Button - variant="outline" - onClick={handleContractView} - className="h-20 flex flex-col items-center justify-center space-y-2" - > - <div className="text-sm font-medium">정규업체 등록 현황</div> - <div className="text-xs text-muted-foreground">정규업체 등록 현황 보기</div> - </Button> - - <Button - variant="outline" - onClick={handleAdditionalInfoView} - className="h-20 flex flex-col items-center justify-center space-y-2" - > - <div className="text-sm font-medium">추가정보</div> - <div className="text-xs text-muted-foreground">업체 추가정보 조회</div> - </Button> - </div> - </div> + /> + </div> - - {/* 다이얼로그들 */} - <PQSimpleDialog - open={pqDialogOpen} - onOpenChange={setPqDialogOpen} - vendorId={vendorId} - /> - - <SiteVisitDetailDialog - isOpen={siteVisitDialogOpen} - onOpenChange={setSiteVisitDialogOpen} - selectedRequest={selectedSiteVisitRequest} - /> - - {registrationData && ( <DocumentStatusDialog open={contractDialogOpen} onOpenChange={setContractDialogOpen} registration={registrationData} + isVendorUser={false} /> - )} - - <AdditionalInfoDialog - open={additionalInfoDialogOpen} - onOpenChange={setAdditionalInfoDialogOpen} - vendorId={parseInt(vendorId)} - readonly={true} - /> </div> ); } diff --git a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/constants.ts b/lib/vendor-basic-info/constants.ts index d16f791f..d16f791f 100644 --- a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/constants.ts +++ b/lib/vendor-basic-info/constants.ts diff --git a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/types.ts b/lib/vendor-basic-info/types.ts index ead3a44c..ead3a44c 100644 --- a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/types.ts +++ b/lib/vendor-basic-info/types.ts diff --git a/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-columns.tsx b/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-columns.tsx index 7446716b..8d21df24 100644 --- a/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-columns.tsx +++ b/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-columns.tsx @@ -214,6 +214,7 @@ export function getColumns(): ColumnDef<VendorRegularRegistration>[] { open={documentDialogOpen}
onOpenChange={setDocumentDialogOpen}
registration={registration}
+ isVendorUser={false}
/>
</>
)
diff --git a/lib/vendors/service.ts b/lib/vendors/service.ts index e3a38891..5d790a6e 100644 --- a/lib/vendors/service.ts +++ b/lib/vendors/service.ts @@ -2779,10 +2779,8 @@ export async function requestPQVendors(input: ApproveVendorsInput & { ? `[eVCP] You are invited to submit Non-Inspection PQ ${vendorPQ?.pqNumber || ""}` : `[eVCP] You are invited to submit PQ ${vendorPQ?.pqNumber || ""}`; - const baseLoginUrl = `${host}/partners/pq`; - const loginUrl = input.projectId - ? `${baseLoginUrl}?projectId=${input.projectId}` - : baseLoginUrl; + const baseUrl = process.env.NEXT_PUBLIC_URL || `http://${host}`; + const loginUrl = `${baseUrl}/partners/pq_new`; // 체크된 계약 항목 배열 생성 const contracts = input.agreements @@ -2807,6 +2805,8 @@ export async function requestPQVendors(input: ApproveVendorsInput & { } } + console.log("loginUrl-pq", loginUrl); + await sendEmail({ to: vendor.email, subject, @@ -2959,8 +2959,9 @@ export async function requestBasicContractInfo({ const headersList = await headers(); const host = headersList.get('host') || 'localhost:3000'; // 로그인 또는 서명 페이지 URL 생성 - const baseUrl = `http://${host}` + const baseUrl = process.env.NEXT_PUBLIC_URL || `http://${host}`; const loginUrl = `${baseUrl}/partners/basic-contract`; + console.log("loginUrl-basic-contract", loginUrl); // 사용자 언어 설정 (기본값은 한국어) const userLang = "ko"; |
