summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-09-02 10:15:08 +0000
committerjoonhoekim <26rote@gmail.com>2025-09-02 10:15:08 +0000
commitd5ddafa4276b0031538261400e431009b0734be9 (patch)
tree4af37243d10f8c750ca1c2df3843cfca9506fe2e /lib
parentfccb00d15466cd0b2d861163663a5070c768ff77 (diff)
(김준회) 협력업체 기본정보 --> 매출정보 구현사항 통합
Diffstat (limited to 'lib')
-rw-r--r--lib/vendor-basic-info/basic-info-client.tsx417
1 files changed, 228 insertions, 189 deletions
diff --git a/lib/vendor-basic-info/basic-info-client.tsx b/lib/vendor-basic-info/basic-info-client.tsx
index 82133f28..fbf19fe9 100644
--- a/lib/vendor-basic-info/basic-info-client.tsx
+++ b/lib/vendor-basic-info/basic-info-client.tsx
@@ -341,17 +341,17 @@ export default function BasicInfoClient({
creditServices,
reload: reloadCreditData
} = useCreditIntegration(vendorId);
-
+
// 다이얼로그 상태
const [pqDialogOpen, setPqDialogOpen] = useState(false);
const [siteVisitDialogOpen, setSiteVisitDialogOpen] = useState(false);
const [contractDialogOpen, setContractDialogOpen] = useState(false);
const [additionalInfoDialogOpen, setAdditionalInfoDialogOpen] = useState(false);
-
+
// 각 다이얼로그에 필요한 데이터 상태
const [selectedSiteVisitRequest, setSelectedSiteVisitRequest] = useState<any>(null);
const [registrationData, setRegistrationData] = useState<any>(null);
-
+
// 첨부파일 및 평가 정보 상태
const [attachmentsByType, setAttachmentsByType] = useState<Record<string, any[]>>({});
const [periodicGrade, setPeriodicGrade] = useState<string | null>(null);
@@ -438,7 +438,7 @@ export default function BasicInfoClient({
toast.info("기본계약 정보가 없습니다.");
return;
}
-
+
// DocumentStatusDialog가 기대하는 형태로 데이터 구성
const dialogData = {
// 기본 정보
@@ -449,10 +449,10 @@ export default function BasicInfoClient({
representative: result.data.vendor.representativeName,
country: result.data.vendor.country,
status: result.data.registration?.status || "정보없음",
-
+
// 문서 제출 현황 - documentSubmissions 속성으로 매핑
documentSubmissions: result.data.documentStatus,
-
+
// 문서별 파일 정보 추가
documentFiles: result.data.documentFiles || {
businessRegistration: [],
@@ -460,17 +460,17 @@ export default function BasicInfoClient({
bankCopy: [],
auditResult: []
},
-
+
// 기본계약 정보
basicContracts: result.data.basicContracts || [],
-
+
// 안전적격성 평가
safetyQualificationContent: result.data.registration?.safetyQualificationContent || null,
-
+
// 추가정보 완료 여부
additionalInfo: result.data.additionalInfoCompleted,
};
-
+
setRegistrationData(dialogData);
setContractDialogOpen(true);
} catch (error) {
@@ -538,7 +538,7 @@ export default function BasicInfoClient({
try {
// 동적으로 downloadFile 함수 import
const { downloadFile } = await import('@/lib/file-download')
-
+
const result = await downloadFile(filePath, fileName);
if (result.success) {
toast.success(`${fileName} 파일이 다운로드되었습니다.`);
@@ -574,71 +574,72 @@ export default function BasicInfoClient({
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 || [];
+ 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] || "";
+ // 공장주소 (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 || "";
}
- } catch {
- factoryInfo.mainSupplyItems = answer || "";
}
- }
- // 실사 결과
- else if (code.startsWith("4-") && answer && !factoryInfo.inspectionResult) {
- factoryInfo.inspectionResult = answer;
- factoryInfo.inspectionFiles = files;
- }
- });
+ // 실사 결과
+ else if (code.startsWith("4-") && answer && !factoryInfo.inspectionResult) {
+ factoryInfo.inspectionResult = answer;
+ factoryInfo.inspectionFiles = files;
+ }
+ });
}
});
return factoryInfo;
};
- // PQ 제출 데이터에서 특정 코드의 답변 가져오기
+ // 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) {
+ if (answer && answer.answer) {
return answer.answer;
}
}
@@ -646,30 +647,32 @@ export default function BasicInfoClient({
return "";
};
- // PQ 제출 데이터에서 특정 코드의 첨부파일 가져오기
+ // 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) {
+ if (answer && answer.uploadedFiles && answer.uploadedFiles.length > 0) {
files.push(...answer.uploadedFiles);
+ break; // 첫 번째로 찾은 파일들만 반환
}
}
}
-
+
return files;
};
// 첨부파일 관리 핸들러 (타입별)
const handleAttachmentFileManagement = (attachmentType: string, typeName: string) => {
const files = attachmentsByType[attachmentType] || [];
-
+
if (files.length === 0) {
toast.info(`${typeName} 파일이 없습니다.`);
return;
@@ -690,6 +693,39 @@ export default function BasicInfoClient({
});
};
+ // 소개자료 파일 관리 핸들러
+ const handleIntroFileManagement = () => {
+ const companyFiles = getPQAttachmentsByCode("1-1");
+ const productFiles = getPQAttachmentsByCode("1-2");
+
+ if (companyFiles.length === 0 && productFiles.length === 0) {
+ toast.info("소개자료 파일이 없습니다.");
+ return;
+ }
+
+ // 회사 소개자료 다운로드
+ if (companyFiles.length > 0) {
+ toast.info("회사 소개자료 파일을 다운로드합니다.");
+ companyFiles.forEach((file, index) => {
+ setTimeout(() => {
+ handleAttachmentDownload(file.filePath, file.fileName);
+ }, index * 500);
+ });
+ }
+
+ // 제품 소개자료 다운로드 (회사 소개자료 다운로드 완료 후)
+ if (productFiles.length > 0) {
+ setTimeout(() => {
+ toast.info("제품 소개자료 파일을 다운로드합니다.");
+ productFiles.forEach((file, index) => {
+ setTimeout(() => {
+ handleAttachmentDownload(file.filePath, file.fileName);
+ }, index * 500);
+ });
+ }, companyFiles.length * 500 + 1000);
+ }
+ };
+
if (!initialData) {
return (
<div className="p-6 bg-background max-w-full">
@@ -762,12 +798,11 @@ export default function BasicInfoClient({
fieldKey="vendorName"
onChange={(value) => updateField("vendorName", value)}
/>
- {/* <InfoItem
+ <InfoItem
title="설립일"
- // 현재 필드 없고 linter error 나도 무시. createdAt은 데이터베이스 생성시점이므로 잘못된 필드.
- value={initialData.establishmentDate}
+ value={getPQAnswerByCode("1-6-1") || getPQAnswerByCode("1-6-2") || ""}
type="readonly"
- /> */}
+ />
<InfoItem
title="대표전화"
value={formData.phone}
@@ -776,14 +811,14 @@ export default function BasicInfoClient({
fieldKey="phone"
onChange={(value) => updateField("phone", value)}
/>
- {/* <InfoItem
+ <InfoItem
title="팩스"
- value={formData.fax}
+ value={getPQAnswerByCode("1-3-3") || formData.fax || ""}
isEditable={true}
editMode={editMode}
fieldKey="fax"
onChange={(value) => updateField("fax", value)}
- /> */}
+ />
<InfoItem
title="업체유형"
value={formData.businessType}
@@ -792,16 +827,15 @@ export default function BasicInfoClient({
fieldKey="businessType"
onChange={(value) => updateField("businessType", value)}
/>
- {/* <InfoItem
+ <InfoItem
title="소개자료"
- value={`회사: ${
- attachmentsByType.COMPANY_INTRO?.length || 0
- }건 / 제품: ${attachmentsByType.PRODUCT_INTRO?.length || 0}건`}
+ value={`회사: ${getPQAttachmentsByCode("1-1").length || 0
+ }건 / 제품: ${getPQAttachmentsByCode("1-2").length || 0}건`}
isEditable={true}
editMode={editMode}
type="file-button"
- onFileButtonClick={() => handleFileManagement("소개자료")}
- /> */}
+ onFileButtonClick={handleIntroFileManagement}
+ />
<InfoItem
title="정기평가 등급"
value={periodicGrade || ""}
@@ -866,14 +900,11 @@ export default function BasicInfoClient({
placeholder="기업규모를 선택하세요"
/> */}
- {/* <InfoItem
+ <InfoItem
title="안전적격성평가"
- value={
- initialData.evaluationInfo?.safetyQualificationEvaluation ||
- null
- }
+ value=""
type="readonly"
- /> */}
+ />
</div>
}
column3={
@@ -883,11 +914,11 @@ export default function BasicInfoClient({
value={vendorTypeInfo?.vendorTypeName || ""}
type="readonly"
/>
- {/* <InfoItem
+ <InfoItem
title="그룹사"
- value={initialData.classificationInfo?.groupCompany || null}
+ value={getPQAnswerByCode("1-6-5") || initialData.classificationInfo?.groupCompany || ""}
isEditable={true}
- /> */}
+ />
<InfoItem
title="국가"
value={formData.country}
@@ -903,11 +934,6 @@ export default function BasicInfoClient({
}
isEditable={true}
/>
- {/* <InfoItem
- title="산업유형"
- value={initialData.classificationInfo?.industryType || ""}
- isEditable={true}
- /> */}
{/* <InfoItem
title="당사거래비중"
@@ -924,105 +950,95 @@ export default function BasicInfoClient({
<WideInfoSection
title="첨부파일"
content={
- <div className="grid grid-cols-2 md:grid-cols-5 gap-4 p-4">
+ <div className="grid grid-cols-5 gap-4 min-w-0 overflow-x-auto">
{/* 사업자등록증 */}
- <div className="flex flex-col items-center gap-3">
- <div className="text-sm font-medium text-center">사업자등록증</div>
- <div className="text-center">
- <div className="text-lg font-semibold text-primary">
- {attachmentsByType.BUSINESS_REGISTRATION?.length || 0}건
- </div>
- {attachmentsByType.BUSINESS_REGISTRATION?.length > 0 && (
- <Button
- variant="outline"
- size="sm"
- className="mt-2"
- onClick={() => handleAttachmentFileManagement("BUSINESS_REGISTRATION", "사업자등록증")}
- >
- 파일 다운로드
- </Button>
- )}
+ <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>
+ {attachmentsByType.BUSINESS_REGISTRATION?.length > 0 && (
+ <Button
+ variant="outline"
+ size="sm"
+ className="mt-2"
+ onClick={() => handleAttachmentFileManagement("BUSINESS_REGISTRATION", "사업자등록증")}
+ >
+ 파일 다운로드
+ </Button>
+ )}
</div>
{/* 신용평가보고서 */}
- <div className="flex flex-col items-center gap-3">
- <div className="text-sm font-medium text-center">신용평가보고서</div>
- <div className="text-center">
- <div className="text-lg font-semibold text-primary">
- {attachmentsByType.CREDIT_REPORT?.length || 0}건
- </div>
- {attachmentsByType.CREDIT_REPORT?.length > 0 && (
- <Button
- variant="outline"
- size="sm"
- className="mt-2"
- onClick={() => handleAttachmentFileManagement("CREDIT_REPORT", "신용평가보고서")}
- >
- 파일 다운로드
- </Button>
- )}
+ <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.CREDIT_REPORT?.length || 0}건
</div>
+ {attachmentsByType.CREDIT_REPORT?.length > 0 && (
+ <Button
+ variant="outline"
+ size="sm"
+ className="mt-2"
+ onClick={() => handleAttachmentFileManagement("CREDIT_REPORT", "신용평가보고서")}
+ >
+ 파일 다운로드
+ </Button>
+ )}
</div>
{/* 통장사본 */}
- <div className="flex flex-col items-center gap-3">
- <div className="text-sm font-medium text-center">통장사본</div>
- <div className="text-center">
- <div className="text-lg font-semibold text-primary">
- {attachmentsByType.BANK_ACCOUNT_COPY?.length || 0}건
- </div>
- {attachmentsByType.BANK_ACCOUNT_COPY?.length > 0 && (
- <Button
- variant="outline"
- size="sm"
- className="mt-2"
- onClick={() => handleAttachmentFileManagement("BANK_ACCOUNT_COPY", "통장사본")}
- >
- 파일 다운로드
- </Button>
- )}
+ <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.BANK_ACCOUNT_COPY?.length || 0}건
</div>
+ {attachmentsByType.BANK_ACCOUNT_COPY?.length > 0 && (
+ <Button
+ variant="outline"
+ size="sm"
+ className="mt-2"
+ onClick={() => handleAttachmentFileManagement("BANK_ACCOUNT_COPY", "통장사본")}
+ >
+ 파일 다운로드
+ </Button>
+ )}
</div>
{/* ISO 인증서 */}
- <div className="flex flex-col items-center gap-3">
- <div className="text-sm font-medium text-center">ISO 인증서</div>
- <div className="text-center">
- <div className="text-lg font-semibold text-primary">
- {attachmentsByType.ISO_CERTIFICATION?.length || 0}건
- </div>
- {attachmentsByType.ISO_CERTIFICATION?.length > 0 && (
- <Button
- variant="outline"
- size="sm"
- className="mt-2"
- onClick={() => handleAttachmentFileManagement("ISO_CERTIFICATION", "ISO 인증서")}
- >
- 파일 다운로드
- </Button>
- )}
+ <div className="text-center min-w-0">
+ <div className="text-sm font-medium mb-2 break-words">ISO 인증서</div>
+ <div className="text-sm text-muted-foreground">
+ {attachmentsByType.ISO_CERTIFICATION?.length || 0}건
</div>
+ {attachmentsByType.ISO_CERTIFICATION?.length > 0 && (
+ <Button
+ variant="outline"
+ size="sm"
+ className="mt-2"
+ onClick={() => handleAttachmentFileManagement("ISO_CERTIFICATION", "ISO 인증서")}
+ >
+ 파일 다운로드
+ </Button>
+ )}
</div>
{/* 기타 첨부파일 (GENERAL) */}
- <div className="flex flex-col items-center gap-3">
- <div className="text-sm font-medium text-center">기타 첨부파일</div>
- <div className="text-center">
- <div className="text-lg font-semibold text-primary">
- {attachmentsByType.GENERAL?.length || 0}건
- </div>
- {attachmentsByType.GENERAL?.length > 0 && (
- <Button
- variant="outline"
- size="sm"
- className="mt-2"
- onClick={() => handleAttachmentFileManagement("GENERAL", "기타 첨부파일")}
- >
- 파일 다운로드
- </Button>
- )}
+ <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.GENERAL?.length || 0}건
</div>
+ {attachmentsByType.GENERAL?.length > 0 && (
+ <Button
+ variant="outline"
+ size="sm"
+ className="mt-2"
+ onClick={() => handleAttachmentFileManagement("GENERAL", "기타 첨부파일")}
+ >
+ 파일 다운로드
+ </Button>
+ )}
</div>
</div>
}
@@ -1141,6 +1157,24 @@ export default function BasicInfoClient({
}
}}
>
+ 조직도 ({getPQAttachmentsByCode("1-5").length}건)
+ </Button>
+ <Button
+ variant="outline"
+ className="text-xs w-32 flex items-center gap-2"
+ 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
@@ -1352,20 +1386,25 @@ export default function BasicInfoClient({
const metricsData = creditTransformedData?.calculatedMetrics || initialData.calculatedMetrics || {};
// 실제 데이터에서 연도 추출 (고정 연도 사용하지 않음)
- const years = Object.keys(salesData).sort().reverse().slice(0, 3);
+ let years = Object.keys(salesData).sort().reverse().slice(0, 3);
- // 데이터가 없는 경우 기본 연도 사용
- if (years.length === 0 && !getCurrentResult()?.data) {
- years.push("20231231", "20221231", "20211231");
+ // 데이터가 없는 경우 빈 행 3개 생성
+ if (years.length === 0) {
+ years = ["", "", ""];
}
- return years.map((dateKey) => {
- const formattedDate = dateKey; // YYYYMMDD 형식으로 표시
+ // 3개 미만인 경우 빈 행으로 채우기
+ while (years.length < 3) {
+ years.push("");
+ }
+
+ return years.map((dateKey, index) => {
+ const formattedDate = dateKey; // YYYYMMDD 형식으로 표시 (빈 문자열일 수 있음)
const yearSalesData = salesData[dateKey];
const yearMetricsData = metricsData[dateKey];
return (
- <TableRow key={dateKey}>
+ <TableRow key={dateKey || `empty-${index}`}>
<TableCell className="text-center font-medium border-r bg-yellow-50">
{formattedDate}
</TableCell>
@@ -1430,7 +1469,7 @@ export default function BasicInfoClient({
}))
}]);
const inspectionFiles = getPQAttachmentsByCode("4-1");
-
+
return (
<InfoSection
key={submission.submission.id}
@@ -1735,14 +1774,14 @@ export default function BasicInfoClient({
</div>
}
/>
-
+
</div>
- <DocumentStatusDialog
- open={contractDialogOpen}
- onOpenChange={setContractDialogOpen}
- registration={registrationData}
- isVendorUser={false}
- />
+ <DocumentStatusDialog
+ open={contractDialogOpen}
+ onOpenChange={setContractDialogOpen}
+ registration={registrationData}
+ isVendorUser={false}
+ />
</div>
);
}