summaryrefslogtreecommitdiff
path: root/components/vendor-regular-registrations
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-08-14 11:54:47 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-08-14 11:54:47 +0000
commit969c25b56f6d29d7ffa4bc2ce04c5fb4e5846b34 (patch)
tree551d335e850e6163792ded0e7a75fa41d96d612a /components/vendor-regular-registrations
parentdd20ba9785cdbd3d61f6b014d003d3bd9646ad13 (diff)
(대표님) 정규벤더등록, 벤더문서관리, 벤더데이터입력, 첨부파일관리
Diffstat (limited to 'components/vendor-regular-registrations')
-rw-r--r--components/vendor-regular-registrations/document-status-dialog.tsx213
-rw-r--r--components/vendor-regular-registrations/registration-request-dialog.tsx691
2 files changed, 827 insertions, 77 deletions
diff --git a/components/vendor-regular-registrations/document-status-dialog.tsx b/components/vendor-regular-registrations/document-status-dialog.tsx
index f8e2e1cd..db3defe6 100644
--- a/components/vendor-regular-registrations/document-status-dialog.tsx
+++ b/components/vendor-regular-registrations/document-status-dialog.tsx
@@ -8,13 +8,12 @@ import {
} from "@/components/ui/dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
-import { FileText, Download, CheckCircle, XCircle, Clock } from "lucide-react";
+import { FileText, Download, CheckCircle, XCircle, Clock, RefreshCw } from "lucide-react";
import { toast } from "sonner";
import type { VendorRegularRegistration } from "@/config/vendorRegularRegistrationsColumnsConfig";
import {
documentStatusColumns,
- contractAgreementColumns,
} from "@/config/vendorRegularRegistrationsColumnsConfig";
import { downloadFile } from "@/lib/file-download";
@@ -22,6 +21,7 @@ interface DocumentStatusDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
registration: VendorRegularRegistration | null;
+ onRefresh?: () => void;
}
const StatusIcon = ({ status }: { status: string | boolean }) => {
@@ -68,63 +68,87 @@ export function DocumentStatusDialog({
open,
onOpenChange,
registration,
+ onRefresh,
}: DocumentStatusDialogProps) {
if (!registration) return null;
+ // 디버깅: registration 데이터 확인
+ console.log(`📋 DocumentStatusDialog - Partners 등록 데이터:`, {
+ companyName: registration.companyName,
+ businessNumber: registration.businessNumber,
+ representative: registration.representative,
+ safetyQualificationContent: registration.safetyQualificationContent,
+ basicContractsLength: registration.basicContracts?.length || 0,
+ additionalInfo: registration.additionalInfo
+ });
+
// 파일 다운로드 핸들러
- const handleFileDownload = async (docKey: string, fileIndex: number = 0) => {
- try {
- const files = registration.documentFiles[docKey as keyof typeof registration.documentFiles];
- if (!files || files.length === 0) {
- toast.error("다운로드할 파일이 없습니다.");
- return;
- }
+ // const handleFileDownload = async (docKey: string, fileIndex: number = 0) => {
+ // try {
+ // const files = registration.documentFiles[docKey as keyof typeof registration.documentFiles];
+ // if (!files || files.length === 0) {
+ // toast.error("다운로드할 파일이 없습니다.");
+ // return;
+ // }
- const file = files[fileIndex];
- if (!file) {
- toast.error("파일을 찾을 수 없습니다.");
- return;
- }
+ // const file = files[fileIndex];
+ // if (!file) {
+ // toast.error("파일을 찾을 수 없습니다.");
+ // return;
+ // }
- // filePath와 fileName 추출
- const filePath = file.filePath || file.path;
- const fileName = file.originalFileName || file.fileName || file.name;
+ // // filePath와 fileName 추출
+ // const filePath = file.filePath || file.path;
+ // const fileName = file.originalFileName || file.fileName || file.name;
- if (!filePath || !fileName) {
- toast.error("파일 정보가 올바르지 않습니다.");
- return;
- }
+ // if (!filePath || !fileName) {
+ // toast.error("파일 정보가 올바르지 않습니다.");
+ // return;
+ // }
- console.log(`📥 파일 다운로드 시작:`, { filePath, fileName, docKey });
+ // console.log(`📥 파일 다운로드 시작:`, { filePath, fileName, docKey });
- // downloadFile 함수를 사용하여 파일 다운로드
- const result = await downloadFile(filePath, fileName, {
- showToast: true,
- onError: (error) => {
- console.error("파일 다운로드 오류:", error);
- toast.error(`파일 다운로드 실패: ${error}`);
- },
- onSuccess: (fileName, fileSize) => {
- console.log(`✅ 파일 다운로드 성공:`, { fileName, fileSize });
- }
- });
+ // // downloadFile 함수를 사용하여 파일 다운로드
+ // const result = await downloadFile(filePath, fileName, {
+ // showToast: true,
+ // onError: (error) => {
+ // console.error("파일 다운로드 오류:", error);
+ // toast.error(`파일 다운로드 실패: ${error}`);
+ // },
+ // onSuccess: (fileName, fileSize) => {
+ // console.log(`✅ 파일 다운로드 성공:`, { fileName, fileSize });
+ // }
+ // });
- if (!result.success) {
- console.error("파일 다운로드 실패:", result.error);
- }
- } catch (error) {
- console.error("파일 다운로드 중 오류 발생:", error);
- toast.error("파일 다운로드 중 오류가 발생했습니다.");
- }
- };
+ // if (!result.success) {
+ // console.error("파일 다운로드 실패:", result.error);
+ // }
+ // } catch (error) {
+ // console.error("파일 다운로드 중 오류 발생:", error);
+ // toast.error("파일 다운로드 중 오류가 발생했습니다.");
+ // }
+ // };
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl max-h-[80vh] overflow-y-auto">
<DialogHeader>
- <DialogTitle className="flex items-center gap-2">
- <FileText className="w-5 h-5" />
- 문서/자료 접수 현황 - {registration.companyName}
+ <DialogTitle className="flex items-center justify-between">
+ <div className="flex items-center gap-2">
+ <FileText className="w-5 h-5" />
+ 문서/자료 접수 현황 - {registration.companyName}
+ </div>
+ {onRefresh && (
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={onRefresh}
+ className="flex items-center gap-2"
+ >
+ <RefreshCw className="w-4 h-4" />
+ 새로고침
+ </Button>
+ )}
</DialogTitle>
</DialogHeader>
@@ -160,12 +184,18 @@ export function DocumentStatusDialog({
<div>상태</div>
<div>제출일자</div>
<div>액션</div>
- </div>
+ </div>
{documentStatusColumns.map((doc) => {
const isSubmitted = registration.documentSubmissions[
doc.key as keyof typeof registration.documentSubmissions
] as boolean;
+ // 내자인 경우 통장사본은 표시하지 않음
+ const isForeign = registration.country !== 'KR';
+ if (doc.key === 'bankCopy' && !isForeign) {
+ return null;
+ }
+
return (
<div
key={doc.key}
@@ -174,6 +204,9 @@ export function DocumentStatusDialog({
<div className="flex items-center gap-2">
<StatusIcon status={isSubmitted} />
{doc.label}
+ {doc.key === 'bankCopy' && isForeign && (
+ <span className="text-xs text-blue-600">(외자 필수)</span>
+ )}
</div>
<div>
<StatusBadge status={isSubmitted} />
@@ -182,7 +215,7 @@ export function DocumentStatusDialog({
{isSubmitted ? "2024.01.01" : "-"}
</div>
<div>
- {isSubmitted && (
+ {/* {isSubmitted && (
<Button
size="sm"
variant="outline"
@@ -191,7 +224,7 @@ export function DocumentStatusDialog({
<Download className="w-4 h-4 mr-1" />
다운로드
</Button>
- )}
+ )} */}
</div>
</div>
);
@@ -211,37 +244,63 @@ export function DocumentStatusDialog({
<div>서약일자</div>
<div>액션</div>
</div>
- {contractAgreementColumns.map((agreement) => {
- const status = registration.contractAgreements[
- agreement.key as keyof typeof registration.contractAgreements
- ] as string;
-
- return (
- <div
- key={agreement.key}
- className="grid grid-cols-4 gap-4 p-4 border-t items-center"
- >
- <div className="flex items-center gap-2">
- <StatusIcon status={status} />
- {agreement.label}
- </div>
- <div>
- <StatusBadge status={status} />
- </div>
- <div className="text-sm text-gray-600">
- {status === "completed" ? "2024.01.01" : "-"}
- </div>
- <div>
- {status === "completed" && (
- <Button size="sm" variant="outline">
- <Download className="w-4 h-4 mr-1" />
- 다운로드
- </Button>
- )}
+ {!registration.basicContracts || registration.basicContracts.length === 0 ? (
+ <div className="p-4 border-t text-center text-gray-500">
+ 요청된 기본계약이 없습니다.
+ </div>
+ ) : (
+ registration.basicContracts.map((contract, index) => {
+ const isCompleted = contract.status === "COMPLETED";
+
+ return (
+ <div
+ key={`${contract.templateId}-${index}`}
+ className="grid grid-cols-4 gap-4 p-4 border-t items-center"
+ >
+ <div className="flex items-center gap-2">
+ <StatusIcon status={isCompleted} />
+ {contract.templateName || "템플릿명 없음"}
+ </div>
+ <div>
+ <StatusBadge status={isCompleted} />
+ </div>
+ <div className="text-sm text-gray-600">
+ {isCompleted && contract.createdAt
+ ? new Intl.DateTimeFormat('ko-KR').format(new Date(contract.createdAt))
+ : "-"
+ }
+ </div>
+ <div>
+ {isCompleted && (
+ <Button size="sm" variant="outline">
+ <Download className="w-4 h-4 mr-1" />
+ 다운로드
+ </Button>
+ )}
+ </div>
</div>
- </div>
- );
- })}
+ );
+ })
+ )}
+ </div>
+ </div>
+
+ {/* 안전적격성 평가 */}
+ <div>
+ <h3 className="text-lg font-semibold mb-4">안전적격성 평가</h3>
+ <div className="p-4 border rounded-lg">
+ <div className="flex items-center justify-between">
+ <div className="flex items-center gap-2">
+ <StatusIcon status={!!registration.safetyQualificationContent} />
+ <span>안전적격성 평가</span>
+ </div>
+ <StatusBadge status={!!registration.safetyQualificationContent} />
+ </div>
+ {registration.safetyQualificationContent && (
+ <div className="mt-3 p-3 bg-gray-50 rounded">
+ <p className="text-sm">{registration.safetyQualificationContent}</p>
+ </div>
+ )}
</div>
</div>
diff --git a/components/vendor-regular-registrations/registration-request-dialog.tsx b/components/vendor-regular-registrations/registration-request-dialog.tsx
new file mode 100644
index 00000000..2a79189a
--- /dev/null
+++ b/components/vendor-regular-registrations/registration-request-dialog.tsx
@@ -0,0 +1,691 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from "@/components/ui/dialog";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { Textarea } from "@/components/ui/textarea";
+import { Checkbox } from "@/components/ui/checkbox";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { toast } from "sonner";
+import { FileText, Building2, User, Phone, Mail } from "lucide-react";
+
+import type { VendorRegularRegistration } from "@/config/vendorRegularRegistrationsColumnsConfig";
+import { fetchRegistrationRequestData } from "@/lib/vendor-regular-registrations/service";
+
+interface RegistrationRequestDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ registration: VendorRegularRegistration | null;
+ onSubmit?: (data: RegistrationRequestData) => void;
+}
+
+export interface RegistrationRequestData {
+ // 업체정보
+ companyNameKor: string;
+ companyNameEng: string;
+ businessNumber: string;
+ corporateNumber: string;
+ majorItems: string;
+ establishmentDate: string;
+
+ // 소재지
+ headOfficeAddress: string;
+ headOfficePhone: string;
+ factoryAddress: string;
+ factoryPhone: string;
+ salesOfficeAddress: string;
+ salesOfficePhone: string;
+
+ // 대표자 정보
+ representativeNameKor: string;
+ representativeNameEng: string;
+ representativeContact: string;
+ representativeBirthDate: string;
+ representativeEmail: string;
+ isWorkingAtCompany: boolean;
+ isInternalPartner: boolean;
+
+ // 대표자 경력
+ representativeCareer: Array<{
+ date: string;
+ content: string;
+ }>;
+
+ // 업무담당자
+ businessContacts: {
+ sales: { name: string; position: string; department: string; responsibility: string; email: string; };
+ design: { name: string; position: string; department: string; responsibility: string; email: string; };
+ delivery: { name: string; position: string; department: string; responsibility: string; email: string; };
+ quality: { name: string; position: string; department: string; responsibility: string; email: string; };
+ taxInvoice: { name: string; position: string; department: string; responsibility: string; email: string; };
+ };
+}
+
+export function RegistrationRequestDialog({
+ open,
+ onOpenChange,
+ registration,
+ onSubmit,
+}: RegistrationRequestDialogProps) {
+ const [loading, setLoading] = useState(false);
+ const [dataLoading, setDataLoading] = useState(false);
+ const [formData, setFormData] = useState<RegistrationRequestData>({
+ // 업체정보 (기본값 설정)
+ companyNameKor: "",
+ companyNameEng: "",
+ businessNumber: "",
+ corporateNumber: "",
+ majorItems: "",
+ establishmentDate: "",
+
+ // 소재지
+ headOfficeAddress: "",
+ headOfficePhone: "",
+ factoryAddress: "",
+ factoryPhone: "",
+ salesOfficeAddress: "",
+ salesOfficePhone: "",
+
+ // 대표자 정보
+ representativeNameKor: "",
+ representativeNameEng: "",
+ representativeContact: "",
+ representativeBirthDate: "",
+ representativeEmail: "",
+ isWorkingAtCompany: false,
+ isInternalPartner: false,
+
+ // 대표자 경력
+ representativeCareer: [{ date: "", content: "" }],
+
+ // 업무담당자
+ businessContacts: {
+ sales: { name: "", position: "", department: "", responsibility: "", email: "" },
+ design: { name: "", position: "", department: "", responsibility: "", email: "" },
+ delivery: { name: "", position: "", department: "", responsibility: "", email: "" },
+ quality: { name: "", position: "", department: "", responsibility: "", email: "" },
+ taxInvoice: { name: "", position: "", department: "", responsibility: "", email: "" },
+ },
+ });
+
+ // 기존 데이터 로드 및 자동 입력
+ useEffect(() => {
+ if (open && registration?.id) {
+ loadExistingData();
+ }
+ }, [open, registration?.id]);
+
+ const loadExistingData = async () => {
+ if (!registration?.id) return;
+
+ setDataLoading(true);
+ try {
+ const result = await fetchRegistrationRequestData(registration.id);
+ if (result.success && result.data) {
+ const { vendor, businessContacts, additionalInfo } = result.data;
+
+ // 업무담당자 데이터 변환
+ const contactsMap = {
+ sales: { name: "", position: "", department: "", responsibility: "", email: "" },
+ design: { name: "", position: "", department: "", responsibility: "", email: "" },
+ delivery: { name: "", position: "", department: "", responsibility: "", email: "" },
+ quality: { name: "", position: "", department: "", responsibility: "", email: "" },
+ taxInvoice: { name: "", position: "", department: "", responsibility: "", email: "" },
+ };
+
+ console.log("🔍 업무담당자 데이터 매핑:", {
+ businessContacts: businessContacts.map(c => ({
+ contactType: c.contactType,
+ contactName: c.contactName
+ })),
+ contactsMapKeys: Object.keys(contactsMap)
+ });
+
+ businessContacts.forEach(contact => {
+ let mappedType = contact.contactType;
+
+ // DB의 snake_case를 camelCase로 변환
+ if (contact.contactType === 'tax_invoice') {
+ mappedType = 'taxInvoice';
+ }
+
+ if (mappedType in contactsMap) {
+ contactsMap[mappedType as keyof typeof contactsMap] = {
+ name: contact.contactName || "",
+ position: contact.position || "",
+ department: contact.department || "",
+ responsibility: contact.responsibility || "",
+ email: contact.email || "",
+ };
+
+ console.log(`✅ 매핑 성공: ${contact.contactType} -> ${mappedType}`, {
+ name: contact.contactName,
+ position: contact.position
+ });
+ } else {
+ console.warn(`❌ 매핑 실패: ${contact.contactType} -> ${mappedType} not found in contactsMap`);
+ }
+ });
+
+ // 날짜 포맷팅 함수
+ const formatDate = (date: Date | string | null) => {
+ if (!date) return "";
+ try {
+ const d = new Date(date);
+ // Invalid Date 체크
+ if (isNaN(d.getTime())) return "";
+ return d.toISOString().split('T')[0]; // YYYY-MM-DD 형식
+ } catch (error) {
+ console.warn("날짜 포맷팅 오류:", date, error);
+ return "";
+ }
+ };
+
+ // 폼 데이터 업데이트
+ setFormData({
+ // 업체정보
+ companyNameKor: vendor.vendorName || "",
+ companyNameEng: "", // TODO: 영문명이 있다면 추가
+ businessNumber: vendor.taxId || "",
+ corporateNumber: vendor.corporateRegistrationNumber || "",
+ majorItems: registration.majorItems || "",
+ establishmentDate: "", // vendors 테이블에 establishmentDate 필드가 없음
+
+ // 소재지 (vendors 테이블에는 address, phone만 있음)
+ headOfficeAddress: vendor.address || "",
+ headOfficePhone: vendor.phone || "",
+ factoryAddress: "", // vendors 테이블에 공장주소 필드가 없음
+ factoryPhone: "", // vendors 테이블에 공장전화 필드가 없음
+ salesOfficeAddress: "", // vendors 테이블에 영업소주소 필드가 없음
+ salesOfficePhone: "", // vendors 테이블에 영업소전화 필드가 없음
+
+ // 대표자 정보
+ representativeNameKor: vendor.representativeName || "",
+ representativeNameEng: "", // vendors 테이블에 영문명 필드가 없음
+ representativeContact: vendor.representativePhone || "",
+ representativeBirthDate: vendor.representativeBirth || "", // 문자열로 직접 사용
+ representativeEmail: vendor.representativeEmail || "",
+ isWorkingAtCompany: vendor.representativeWorkExpirence || false,
+ isInternalPartner: false, // vendors 테이블에 해당 필드가 없음
+
+ // 대표자 경력 (기본값 유지)
+ representativeCareer: [{ date: "", content: "" }],
+
+ // 업무담당자
+ businessContacts: contactsMap,
+ });
+
+ console.log("✅ 기존 데이터 로드 완료:", {
+ vendor: vendor.vendorName,
+ contactsCount: businessContacts.length,
+ hasAdditionalInfo: !!additionalInfo,
+ vendorData: {
+ representativeBirth: vendor.representativeBirth,
+ representativeBirthType: typeof vendor.representativeBirth,
+ createdAt: vendor.createdAt,
+ createdAtType: typeof vendor.createdAt
+ }
+ });
+
+ } else {
+ toast.error(result.error || "데이터 로드 실패");
+ }
+ } catch (error) {
+ console.error("데이터 로드 오류:", error);
+ toast.error("데이터 로드 중 오류가 발생했습니다.");
+ } finally {
+ setDataLoading(false);
+ }
+ };
+
+ if (!registration) return null;
+
+ const handleInputChange = (field: string, value: any) => {
+ setFormData(prev => ({
+ ...prev,
+ [field]: value
+ }));
+ };
+
+ const handleBusinessContactChange = (type: keyof typeof formData.businessContacts, field: 'name' | 'position' | 'department' | 'responsibility' | 'email', value: string) => {
+ setFormData(prev => ({
+ ...prev,
+ businessContacts: {
+ ...prev.businessContacts,
+ [type]: {
+ ...prev.businessContacts[type],
+ [field]: value
+ }
+ }
+ }));
+ };
+
+ const handleCareerChange = (index: number, field: 'date' | 'content', value: string) => {
+ setFormData(prev => ({
+ ...prev,
+ representativeCareer: prev.representativeCareer.map((career, i) =>
+ i === index ? { ...career, [field]: value } : career
+ )
+ }));
+ };
+
+ const addCareerEntry = () => {
+ setFormData(prev => ({
+ ...prev,
+ representativeCareer: [...prev.representativeCareer, { date: "", content: "" }]
+ }));
+ };
+
+ const removeCareerEntry = (index: number) => {
+ if (formData.representativeCareer.length > 1) {
+ setFormData(prev => ({
+ ...prev,
+ representativeCareer: prev.representativeCareer.filter((_, i) => i !== index)
+ }));
+ }
+ };
+
+ const handleSubmit = async () => {
+ setLoading(true);
+ try {
+ // 필수 필드 검증
+ const requiredFields = [
+ { field: formData.companyNameKor, name: "업체명(국문)" },
+ { field: formData.businessNumber, name: "사업자번호" },
+ { field: formData.representativeNameKor, name: "대표자 성명(국문)" },
+ { field: formData.representativeContact, name: "대표자 연락처" },
+ { field: formData.representativeBirthDate, name: "대표자 생년월일" },
+ { field: formData.representativeEmail, name: "대표자 이메일" },
+ ];
+
+ const missingFields = requiredFields.filter(({ field }) => !field?.trim());
+ if (missingFields.length > 0) {
+ toast.error(`다음 필수 항목을 입력해주세요: ${missingFields.map(f => f.name).join(", ")}`);
+ return;
+ }
+
+ if (onSubmit) {
+ await onSubmit(formData);
+ } else {
+ // TODO: 실제 정규업체 등록 요청 API 호출
+ toast.success("정규업체 등록 요청이 제출되었습니다.");
+ onOpenChange(false);
+ }
+ } catch (error) {
+ console.error("정규업체 등록 요청 오류:", error);
+ toast.error("정규업체 등록 요청 중 오류가 발생했습니다.");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-6xl max-h-[90vh] overflow-y-auto">
+ <DialogHeader>
+ <DialogTitle className="flex items-center gap-2">
+ <FileText className="w-5 h-5" />
+ 정규업체 등록 요청
+ </DialogTitle>
+ <p className="text-sm text-muted-foreground">
+ 정규업체 등록을 요청할 협력업체 정보 확인 및 누락정보를 입력하세요.
+ </p>
+ </DialogHeader>
+
+ {dataLoading ? (
+ <div className="flex items-center justify-center p-8">
+ <div className="text-center">
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
+ <p>기존 데이터를 불러오는 중...</p>
+ </div>
+ </div>
+ ) : (
+ <div className="space-y-6">
+ {/* 신규 협력사 등록 카드 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="flex items-center gap-2">
+ <Building2 className="w-5 h-5" />
+ 신규 협력사 등록 카드
+ </CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-6">
+
+ {/* 업체정보 */}
+ <div>
+ <h4 className="font-semibold mb-3">업체정보</h4>
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <div>
+ <Label htmlFor="companyNameKor">업체명(국문) <span className="text-red-500">*</span></Label>
+ <Input
+ id="companyNameKor"
+ value={formData.companyNameKor}
+ onChange={(e) => handleInputChange('companyNameKor', e.target.value)}
+ className="bg-yellow-50"
+ />
+ </div>
+ <div>
+ <Label htmlFor="businessNumber">사업자번호 <span className="text-red-500">*</span></Label>
+ <Input
+ id="businessNumber"
+ value={formData.businessNumber}
+ onChange={(e) => handleInputChange('businessNumber', e.target.value)}
+ className="bg-yellow-50"
+ />
+ </div>
+ <div>
+ <Label htmlFor="companyNameEng">업체명(영문)</Label>
+ <Input
+ id="companyNameEng"
+ value={formData.companyNameEng}
+ onChange={(e) => handleInputChange('companyNameEng', e.target.value)}
+ />
+ </div>
+ <div>
+ <Label htmlFor="corporateNumber">법인등록번호</Label>
+ <Input
+ id="corporateNumber"
+ value={formData.corporateNumber}
+ onChange={(e) => handleInputChange('corporateNumber', e.target.value)}
+ />
+ </div>
+ <div>
+ <Label htmlFor="majorItems">주요품목</Label>
+ <Input
+ id="majorItems"
+ value={formData.majorItems}
+ onChange={(e) => handleInputChange('majorItems', e.target.value)}
+ />
+ </div>
+ <div>
+ <Label htmlFor="establishmentDate">설립일자</Label>
+ <Input
+ id="establishmentDate"
+ type="date"
+ value={formData.establishmentDate}
+ onChange={(e) => handleInputChange('establishmentDate', e.target.value)}
+ />
+ </div>
+ </div>
+ </div>
+
+ {/* 소재지 */}
+ <div>
+ <h4 className="font-semibold mb-3">소재지</h4>
+ <div className="space-y-4">
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <div>
+ <Label htmlFor="headOfficeAddress">본사 주소</Label>
+ <Input
+ id="headOfficeAddress"
+ value={formData.headOfficeAddress}
+ onChange={(e) => handleInputChange('headOfficeAddress', e.target.value)}
+ placeholder="PQ 내 회사주소"
+ />
+ </div>
+ <div>
+ <Label htmlFor="headOfficePhone">본사 전화번호</Label>
+ <Input
+ id="headOfficePhone"
+ value={formData.headOfficePhone}
+ onChange={(e) => handleInputChange('headOfficePhone', e.target.value)}
+ placeholder="PQ 내 회사전화"
+ />
+ </div>
+ </div>
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <div>
+ <Label htmlFor="factoryAddress">공장 주소</Label>
+ <Input
+ id="factoryAddress"
+ value={formData.factoryAddress}
+ onChange={(e) => handleInputChange('factoryAddress', e.target.value)}
+ placeholder="PQ 내 공장주소"
+ />
+ </div>
+ <div>
+ <Label htmlFor="factoryPhone">공장 전화번호</Label>
+ <Input
+ id="factoryPhone"
+ value={formData.factoryPhone}
+ onChange={(e) => handleInputChange('factoryPhone', e.target.value)}
+ placeholder="PQ 내 공장전화"
+ />
+ </div>
+ </div>
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <div>
+ <Label htmlFor="salesOfficeAddress">영업소 주소</Label>
+ <Input
+ id="salesOfficeAddress"
+ value={formData.salesOfficeAddress}
+ onChange={(e) => handleInputChange('salesOfficeAddress', e.target.value)}
+ placeholder="필요시 입력"
+ />
+ </div>
+ <div>
+ <Label htmlFor="salesOfficePhone">영업소 전화번호</Label>
+ <Input
+ id="salesOfficePhone"
+ value={formData.salesOfficePhone}
+ onChange={(e) => handleInputChange('salesOfficePhone', e.target.value)}
+ placeholder="필요시 입력"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+
+ {/* 대표자 정보 */}
+ <div>
+ <h4 className="font-semibold mb-3">대표자 정보</h4>
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <div>
+ <Label htmlFor="representativeNameKor">대표자 성명(국문) <span className="text-red-500">*</span></Label>
+ <Input
+ id="representativeNameKor"
+ value={formData.representativeNameKor}
+ onChange={(e) => handleInputChange('representativeNameKor', e.target.value)}
+ className="bg-yellow-50"
+ />
+ </div>
+ <div>
+ <Label htmlFor="representativeContact">대표자 연락처 <span className="text-red-500">*</span></Label>
+ <Input
+ id="representativeContact"
+ value={formData.representativeContact}
+ onChange={(e) => handleInputChange('representativeContact', e.target.value)}
+ className="bg-yellow-50"
+ placeholder="010-0000-0000"
+ />
+ </div>
+ <div>
+ <Label htmlFor="representativeNameEng">대표자 성명(영문)</Label>
+ <Input
+ id="representativeNameEng"
+ value={formData.representativeNameEng}
+ onChange={(e) => handleInputChange('representativeNameEng', e.target.value)}
+ />
+ </div>
+ <div>
+ <Label htmlFor="representativeBirthDate">대표자 생년월일 <span className="text-red-500">*</span></Label>
+ <Input
+ id="representativeBirthDate"
+ type="date"
+ value={formData.representativeBirthDate}
+ onChange={(e) => handleInputChange('representativeBirthDate', e.target.value)}
+ className="bg-yellow-50"
+ />
+ </div>
+ <div>
+ <Label htmlFor="representativeEmail">대표자 이메일 <span className="text-red-500">*</span></Label>
+ <Input
+ id="representativeEmail"
+ type="email"
+ value={formData.representativeEmail}
+ onChange={(e) => handleInputChange('representativeEmail', e.target.value)}
+ className="bg-yellow-50"
+ />
+ </div>
+ </div>
+
+ <div className="mt-4 space-y-3">
+ <div className="flex items-center space-x-2">
+ <Checkbox
+ id="isWorkingAtCompany"
+ checked={formData.isWorkingAtCompany}
+ onCheckedChange={(checked) => handleInputChange('isWorkingAtCompany', checked)}
+ />
+ <Label htmlFor="isWorkingAtCompany">당사근무여부 <span className="text-red-500">*</span></Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <Checkbox
+ id="isInternalPartner"
+ checked={formData.isInternalPartner}
+ onCheckedChange={(checked) => handleInputChange('isInternalPartner', checked)}
+ />
+ <Label htmlFor="isInternalPartner">사내협력사 <span className="text-red-500">*</span></Label>
+ </div>
+ </div>
+ </div>
+
+ {/* 대표자 경력 */}
+ <div>
+ <h4 className="font-semibold mb-3">대표자 경력</h4>
+ <div className="space-y-3">
+ {formData.representativeCareer.map((career, index) => (
+ <div key={index} className="flex gap-3 items-center">
+ <div className="flex-1">
+ <Input
+ placeholder="일자 (예: 24.01.01)"
+ value={career.date}
+ onChange={(e) => handleCareerChange(index, 'date', e.target.value)}
+ />
+ </div>
+ <div className="flex-[3]">
+ <Input
+ placeholder="대표자 이력내용 (직장이력)"
+ value={career.content}
+ onChange={(e) => handleCareerChange(index, 'content', e.target.value)}
+ />
+ </div>
+ {formData.representativeCareer.length > 1 && (
+ <Button
+ type="button"
+ variant="outline"
+ size="sm"
+ onClick={() => removeCareerEntry(index)}
+ >
+ 삭제
+ </Button>
+ )}
+ </div>
+ ))}
+ <Button
+ type="button"
+ variant="outline"
+ size="sm"
+ onClick={addCareerEntry}
+ >
+ 경력 추가
+ </Button>
+ </div>
+ </div>
+
+ {/* 업무담당자 */}
+ <div>
+ <h4 className="font-semibold mb-3">업무담당자</h4>
+ <div className="space-y-4">
+ {Object.entries(formData.businessContacts).map(([type, contact]) => {
+ const labels = {
+ sales: "영업",
+ design: "설계",
+ delivery: "납기",
+ quality: "품질",
+ taxInvoice: "세금계산서"
+ };
+
+ return (
+ <div key={type} className="border rounded-lg p-4 space-y-4">
+ <h5 className="font-medium text-sm">{labels[type as keyof typeof labels]} 담당자</h5>
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+ <div>
+ <Label>담당자명</Label>
+ <Input
+ value={contact.name}
+ onChange={(e) => handleBusinessContactChange(type as keyof typeof formData.businessContacts, 'name', e.target.value)}
+ />
+ </div>
+ <div>
+ <Label>직급</Label>
+ <Input
+ value={contact.position}
+ onChange={(e) => handleBusinessContactChange(type as keyof typeof formData.businessContacts, 'position', e.target.value)}
+ placeholder="예: 부장, 과장"
+ />
+ </div>
+ <div>
+ <Label>부서</Label>
+ <Input
+ value={contact.department}
+ onChange={(e) => handleBusinessContactChange(type as keyof typeof formData.businessContacts, 'department', e.target.value)}
+ placeholder="예: 영업부, 기술부"
+ />
+ </div>
+ <div>
+ <Label>담당업무</Label>
+ <Input
+ value={contact.responsibility}
+ onChange={(e) => handleBusinessContactChange(type as keyof typeof formData.businessContacts, 'responsibility', e.target.value)}
+ placeholder="예: 고객 대응, 품질 관리"
+ />
+ </div>
+ <div>
+ <Label>이메일</Label>
+ <Input
+ type="email"
+ value={contact.email}
+ onChange={(e) => handleBusinessContactChange(type as keyof typeof formData.businessContacts, 'email', e.target.value)}
+ placeholder="example@company.com"
+ />
+ </div>
+ </div>
+ </div>
+ );
+ })}
+ </div>
+ </div>
+
+ </CardContent>
+ </Card>
+ </div>
+ )}
+
+ <DialogFooter>
+ <Button
+ variant="outline"
+ onClick={() => onOpenChange(false)}
+ disabled={loading}
+ >
+ 취소
+ </Button>
+ <Button
+ onClick={handleSubmit}
+ disabled={loading}
+ >
+ {loading ? "요청 중..." : "등록요청"}
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ );
+}