'use client' import React, { useState, useEffect, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Progress } from '@/components/ui/progress'; import { Check, ChevronRight, User, Building, FileText, Plus, X, ChevronsUpDown, Loader2 } from 'lucide-react'; import { cn } from '@/lib/utils'; import { useRouter, useParams, useSearchParams } from 'next/navigation'; import { useTranslation } from '@/i18n/client'; import { toast } from '@/hooks/use-toast'; import { Popover, PopoverTrigger, PopoverContent, } from '@/components/ui/popover'; import { Command, CommandList, CommandInput, CommandEmpty, CommandGroup, CommandItem, } from '@/components/ui/command'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Dropzone, DropzoneZone, DropzoneInput, DropzoneUploadIcon, DropzoneTitle, DropzoneDescription, } from '@/components/ui/dropzone'; import { FileList, FileListItem, FileListHeader, FileListIcon, FileListInfo, FileListName, FileListDescription, FileListAction, } from '@/components/ui/file-list'; import { ScrollArea } from '@/components/ui/scroll-area'; import prettyBytes from 'pretty-bytes'; import { useToast } from "@/hooks/use-toast"; // libphonenumber-js 관련 imports import { parsePhoneNumberFromString, isPossiblePhoneNumber, isValidPhoneNumber, validatePhoneNumberLength, getExampleNumber, AsYouType, getCountries, getCountryCallingCode } from 'libphonenumber-js' import examples from 'libphonenumber-js/examples.mobile.json' // 기존 imports... import i18nIsoCountries from "i18n-iso-countries"; import enLocale from "i18n-iso-countries/langs/en.json"; import koLocale from "i18n-iso-countries/langs/ko.json"; import { getVendorTypes } from '@/lib/vendors/service'; import ConsentStep from './conset-step'; import { checkEmailExists } from '@/lib/vendor-users/service'; import { MaterialSelector } from '@/components/common/material/material-selector'; import { MaterialSearchItem } from '@/lib/material/material-group-service'; i18nIsoCountries.registerLocale(enLocale); i18nIsoCountries.registerLocale(koLocale); // Types interface CountryOption { code: string; label: string; } interface VendorType { id: number; nameKo: string; nameEn: string; } interface ContactTaskOption { value: string; label: string; } interface Contact { contactName: string; contactPosition: string; contactDepartment: string; contactTask: string; contactEmail: string; contactPhone: string; } interface AccountData { name: string; email: string; phone: string; country: string; } interface VendorData { vendorName: string; vendorTypeId?: number; items: MaterialSearchItem[]; taxId: string; address: string; addressDetail: string; postalCode: string; email: string; phone: string; country: string; website: string; representativeName: string; representativeBirth: string; representativeEmail: string; representativePhone: string; corporateRegistrationNumber: string; representativeWorkExpirence: boolean; contacts: Contact[]; } interface ConsentData { privacy: boolean; terms: boolean; marketing: boolean; } interface PhoneValidation { isValid: boolean; error: string | null; formatted: string; international?: string; } // Country data setup const getCountryData = (lng: string): CountryOption[] => { const locale = lng === 'ko' ? "ko" : "en"; const countryMap = i18nIsoCountries.getNames(locale, { select: "official" }); const countryArray = Object.entries(countryMap).map(([code, label]) => ({ code, label, })); const sortedCountryArray = [...countryArray].sort((a, b) => { if (a.code === "KR") return -1; if (b.code === "KR") return 1; return a.label.localeCompare(b.label); }); return sortedCountryArray.map(country => ({ ...country, label: locale === "ko" && country.code === "KR" ? "대한민국 (South Korea)" : country.label })); }; const MAX_FILE_SIZE = 3e9; // ========== 전화번호 정규화 함수 ========== /** * 전화번호를 E.164 형식으로 정규화 (저장용) */ function normalizePhoneForStorage(phoneNumber: string, countryCode: string): string | null { try { if (!phoneNumber || !countryCode) return null; const parsed = parsePhoneNumberFromString(phoneNumber, countryCode); if (!parsed || !parsed.isValid()) { return null; } // E.164 형식으로 반환 (+821012345678) return parsed.format('E.164'); } catch (error) { console.error('Phone normalization error:', error); return null; } } /** * E.164 형식의 전화번호를 표시용으로 포맷팅 */ function formatPhoneForDisplay(phoneNumber: string): string { try { if (!phoneNumber) return ''; const parsed = parsePhoneNumberFromString(phoneNumber); if (parsed) { return parsed.formatNational(); // 국내 형식으로 표시 } return phoneNumber; } catch { return phoneNumber; } } // ========== 전화번호 처리 유틸리티 함수들 ========== /** * 국가별 전화번호 예시를 가져오는 함수 */ function getPhoneExample(countryCode: string) { if (!countryCode) return null; try { return getExampleNumber(countryCode, examples); } catch { return null; } } /** * 국가별 전화번호 플레이스홀더를 생성하는 함수 */ function getPhonePlaceholder(countryCode: string, t: (key: string) => string): string { if (!countryCode) return t('selectCountryFirst'); const example = getPhoneExample(countryCode); if (example) { return example.formatNational(); } try { const callingCode = getCountryCallingCode(countryCode); return `+${callingCode} ...`; } catch { return t('enterPhoneNumber'); } } /** * 국가별 전화번호 설명을 생성하는 함수 */ function getPhoneDescription(countryCode: string, t: (key: string, options?: any) => string): string { if (!countryCode) return t('selectCountryForPhoneFormat'); const example = getPhoneExample(countryCode); if (example) { return t('phoneExample', { national: example.formatNational(), international: example.formatInternational() }); } try { const callingCode = getCountryCallingCode(countryCode); return t('phoneCountryCode', { code: callingCode }); } catch { return t('enterValidPhoneFormat'); } } /** * 전화번호 검증 결과를 반환하는 함수 */ function validatePhoneNumber(phoneNumber: string, countryCode: string, t: (key: string) => string): PhoneValidation { if (!phoneNumber || !countryCode) { return { isValid: false, error: null, formatted: phoneNumber }; } try { const parsed = parsePhoneNumberFromString(phoneNumber, countryCode); if (!parsed) { return { isValid: false, error: t('invalidPhoneFormat'), formatted: phoneNumber }; } const lengthValidation = validatePhoneNumberLength(phoneNumber, countryCode); if (lengthValidation !== undefined) { const lengthErrors: Record = { 'TOO_SHORT': t('phoneTooShort'), 'TOO_LONG': t('phoneTooLong'), 'INVALID_LENGTH': t('phoneInvalidLength') }; return { isValid: false, error: lengthErrors[lengthValidation] || t('phoneInvalidLength'), formatted: phoneNumber }; } if (!isPossiblePhoneNumber(phoneNumber, countryCode)) { return { isValid: false, error: t('phoneNotPossible'), formatted: phoneNumber }; } if (!isValidPhoneNumber(phoneNumber, countryCode)) { return { isValid: false, error: t('phoneInvalid'), formatted: phoneNumber }; } return { isValid: true, error: null, formatted: parsed.formatNational(), international: parsed.formatInternational() }; } catch (error) { return { isValid: false, error: t('phoneFormatError'), formatted: phoneNumber }; } } /** * 실시간 전화번호 포맷팅을 위한 커스텀 훅 */ function usePhoneFormatter(countryCode: string) { const [formatter, setFormatter] = useState(null); useEffect(() => { if (countryCode) { setFormatter(new AsYouType(countryCode)); } else { setFormatter(null); } }, [countryCode]); const formatPhone = useCallback((value: string) => { if (!formatter || !countryCode) return value; const newFormatter = new AsYouType(countryCode); return newFormatter.input(value); }, [countryCode, formatter]); return formatPhone; } // ========== 전화번호 입력 컴포넌트 ========== interface PhoneInputProps { value: string; onChange: (value: string) => void; countryCode: string; placeholder?: string; disabled?: boolean; onBlur?: () => void; className?: string; showValidation?: boolean; t: (key: string, options?: any) => string; } function PhoneInput({ value, onChange, countryCode, placeholder, disabled = false, onBlur = undefined, className = "", showValidation = true, t }: PhoneInputProps) { const [touched, setTouched] = useState(false); const [localValue, setLocalValue] = useState(value || ''); const formatPhone = usePhoneFormatter(countryCode); useEffect(() => { // E.164 형식으로 저장된 번호를 표시용으로 변환 const displayValue = value ? formatPhoneForDisplay(value) : ''; setLocalValue(displayValue); }, [value, countryCode]); const validation = validatePhoneNumber(localValue, countryCode, t); const handleInputChange = (e: React.ChangeEvent) => { const inputValue = e.target.value; const formattedValue = countryCode ? formatPhone(inputValue) : inputValue; setLocalValue(formattedValue); onChange(formattedValue); }; const handleBlur = () => { setTouched(true); if (onBlur) onBlur(); }; const showError = showValidation && touched && localValue && !validation.isValid; const showSuccess = showValidation && touched && localValue && validation.isValid; return (
{showValidation && (

{getPhoneDescription(countryCode, t)}

{showError && (

{validation.error}

)} {showSuccess && (

✓ {t('validPhoneNumber')} {validation.international && ` (${validation.international})`}

)}
)}
); } // ========== 메인 컴포넌트 ========== export default function JoinForm() { const params = useParams() || {}; const lng = params.lng ? String(params.lng) : "ko"; const { t } = useTranslation(lng, "join"); const router = useRouter(); const searchParams = useSearchParams() || new URLSearchParams(); const defaultTaxId = searchParams.get("taxID") ?? ""; const { toast } = useToast(); const [currentStep, setCurrentStep] = useState(1); const [completedSteps, setCompletedSteps] = useState(new Set()); // 각 스텝별 데이터 const [consentData, setConsentData] = useState({ privacy: false, terms: false, marketing: false }); const [accountData, setAccountData] = useState({ name: '', email: '', phone: '', country: 'KR' }); const [vendorData, setVendorData] = useState({ vendorName: "", vendorTypeId: undefined, items: [], taxId: defaultTaxId, address: "", addressDetail: "", postalCode: "", email: "", phone: "", country: "", website: "", representativeName: "", representativeBirth: "", representativeEmail: "", representativePhone: "", corporateRegistrationNumber: "", representativeWorkExpirence: false, contacts: [ { contactName: "", contactPosition: "", contactDepartment: "", contactTask: "", contactEmail: "", contactPhone: "", }, ], }); // 업체 타입 및 파일 상태 const [vendorTypes, setVendorTypes] = useState([]); const [isLoadingVendorTypes, setIsLoadingVendorTypes] = useState(true); const [businessRegistrationFiles, setBusinessRegistrationFiles] = useState([]); const [isoCertificationFiles, setIsoCertificationFiles] = useState([]); const [creditReportFiles, setCreditReportFiles] = useState([]); const [bankAccountFiles, setBankAccountFiles] = useState([]); const [policyVersions] = useState({ privacy_policy: '1.0', terms_of_service: '1.0' }); // 스텝 정의 const STEPS = [ { id: 1, title: t('consentStep'), description: t('consentStepDesc'), icon: FileText }, { id: 2, title: t('accountStep'), description: t('accountStepDesc'), icon: User }, { id: 3, title: t('vendorStep'), description: t('vendorStepDesc'), icon: Building } ]; const progress = ((currentStep - 1) / (STEPS.length - 1)) * 100; const enhancedCountryArray = getCountryData(lng); // Contact task options const contactTaskOptions: ContactTaskOption[] = [ { value: "PRESIDENT_DIRECTOR", label: t('taskPresidentDirector') }, { value: "SALES_MANAGEMENT", label: t('taskSalesManagement') }, { value: "ENGINEERING_DESIGN", label: t('taskEngineeringDesign') }, { value: "PROCUREMENT", label: t('taskProcurement') }, { value: "DELIVERY_CONTROL", label: t('taskDeliveryControl') }, { value: "PM_MANUFACTURING", label: t('taskPmManufacturing') }, { value: "QUALITY_MANAGEMENT", label: t('taskQualityManagement') }, { value: "SHIPPING_DOC_MANAGEMENT", label: t('taskShippingDocManagement') }, { value: "AS_MANAGEMENT", label: t('taskAsManagement') }, { value: "FIELD_SERVICE_ENGINEER", label: t('taskFieldServiceEngineer') } ]; // 정책 버전 및 업체 타입 로드 useEffect(() => { loadVendorTypes(); }, []); const loadVendorTypes = async () => { setIsLoadingVendorTypes(true); try { const result = await getVendorTypes(); if (result.data) { setVendorTypes(result.data); } } catch (error) { console.error("Failed to load vendor types:", error); toast({ variant: "destructive", title: t('error'), description: t('failedToLoadVendorTypes'), }); } finally { setIsLoadingVendorTypes(false); } }; const handleStepComplete = (step: number) => { setCompletedSteps(prev => new Set([...prev, step])); if (step < STEPS.length) { setCurrentStep(step + 1); } }; const handleStepClick = (stepId: number) => { if (stepId <= Math.max(...completedSteps) + 1) { setCurrentStep(stepId); } }; return (
{/* 진행률 표시 */}

{t('partnerRegistration')}

{currentStep} / {STEPS.length}
{/* 스텝 네비게이션 */}
{STEPS.map((step, index) => { const Icon = step.icon; const isCompleted = completedSteps.has(step.id); const isCurrent = currentStep === step.id; const isAccessible = step.id <= Math.max(...completedSteps) + 1; return (
handleStepClick(step.id)} >
{isCompleted ? ( ) : ( )}
{step.title}
{step.description}
{index < STEPS.length - 1 && ( )}
); })}
{/* 스텝 콘텐츠 */}
{currentStep === 1 && ( handleStepComplete(1)} lng={lng} /> )} {currentStep === 2 && ( handleStepComplete(2)} onBack={() => setCurrentStep(1)} enhancedCountryArray={enhancedCountryArray} t={t} /> )} {currentStep === 3 && ( setCurrentStep(2)} onComplete={() => { handleStepComplete(3); router.push(`/${lng}/partners/dashboard`); }} accountData={accountData} consentData={consentData} vendorTypes={vendorTypes} isLoadingVendorTypes={isLoadingVendorTypes} businessRegistrationFiles={businessRegistrationFiles} setBusinessRegistrationFiles={setBusinessRegistrationFiles} isoCertificationFiles={isoCertificationFiles} setIsoCertificationFiles={setIsoCertificationFiles} creditReportFiles={creditReportFiles} setCreditReportFiles={setCreditReportFiles} bankAccountFiles={bankAccountFiles} setBankAccountFiles={setBankAccountFiles} enhancedCountryArray={enhancedCountryArray} contactTaskOptions={contactTaskOptions} lng={lng} policyVersions={policyVersions} t={t} /> )}
); } // Step 2: 계정 생성 interface AccountStepProps { data: AccountData; onChange: (updater: (prev: AccountData) => AccountData) => void; onNext: () => void; onBack: () => void; enhancedCountryArray: CountryOption[]; t: (key: string, options?: any) => string; } function AccountStep({ data, onChange, onNext, onBack, enhancedCountryArray, t }: AccountStepProps) { const [isLoading, setIsLoading] = useState(false); const [emailCheckError, setEmailCheckError] = useState(''); const handleInputChange = (field: keyof AccountData, value: string) => { onChange(prev => ({ ...prev, [field]: value })); }; const phoneValidation = validatePhoneNumber(data.phone, data.country, t); const isValid = data.name && data.email && data.country && data.phone && phoneValidation.isValid; const handleNext = async () => { if (!isValid) return; setIsLoading(true); setEmailCheckError(''); try { // 전화번호 정규화 const normalizedPhone = normalizePhoneForStorage(data.phone, data.country); if (!normalizedPhone) { setEmailCheckError('전화번호 형식이 올바르지 않습니다'); return; } const isUsed = await checkEmailExists(data.email); if (isUsed) { setEmailCheckError(t('emailAlreadyInUse')); return; } // 정규화된 전화번호로 데이터 업데이트 onChange(prev => ({ ...prev, phone: normalizedPhone })); onNext(); } catch (error) { setEmailCheckError(t('emailCheckError')); } finally { setIsLoading(false); } }; return (

{t('accountInfoInput')}

{t('accountInfoDescription')}

handleInputChange('name', e.target.value)} />
handleInputChange('email', e.target.value)} /> {emailCheckError && (

{emailCheckError}

)}
{t('noCountryFound')} {enhancedCountryArray.map((country) => ( handleInputChange('country', country.code)} > {country.label} ))}
handleInputChange('phone', value)} countryCode={data.country} showValidation={true} t={t} />
); } // Step 3: 업체 등록 interface VendorStepProps { data: VendorData; onChange: (updater: (prev: VendorData) => VendorData) => void; onBack: () => void; onComplete: () => void; accountData: AccountData; consentData: ConsentData; vendorTypes: VendorType[]; isLoadingVendorTypes: boolean; businessRegistrationFiles: File[]; setBusinessRegistrationFiles: (files: File[]) => void; isoCertificationFiles: File[]; setIsoCertificationFiles: (files: File[]) => void; creditReportFiles: File[]; setCreditReportFiles: (files: File[]) => void; bankAccountFiles: File[]; setBankAccountFiles: (files: File[]) => void; enhancedCountryArray: CountryOption[]; contactTaskOptions: ContactTaskOption[]; lng: string; policyVersions: { privacy_policy: string; terms_of_service: string; }; t: (key: string, options?: any) => string; } function VendorStep(props: VendorStepProps) { return ; } function CompleteVendorForm({ data, onChange, onBack, onComplete, accountData, consentData, vendorTypes, isLoadingVendorTypes, businessRegistrationFiles, setBusinessRegistrationFiles, isoCertificationFiles, setIsoCertificationFiles, creditReportFiles, setCreditReportFiles, bankAccountFiles, setBankAccountFiles, enhancedCountryArray, contactTaskOptions, lng, policyVersions, t }: VendorStepProps) { const [isSubmitting, setIsSubmitting] = useState(false); const { toast } = useToast(); // 담당자 관리 함수들 const addContact = () => { onChange(prev => ({ ...prev, contacts: [...prev.contacts, { contactName: "", contactPosition: "", contactDepartment: "", contactTask: "", contactEmail: "", contactPhone: "", }] })); }; const removeContact = (index: number) => { onChange(prev => ({ ...prev, contacts: prev.contacts.filter((_, i) => i !== index) })); }; const updateContact = (index: number, field: keyof Contact, value: string) => { onChange(prev => ({ ...prev, contacts: prev.contacts.map((contact, i) => i === index ? { ...contact, [field]: value } : contact ) })); }; const handleInputChange = (field: keyof VendorData, value: any) => { onChange(prev => ({ ...prev, [field]: value })); }; // 자재 변경 핸들러 const handleMaterialsChange = (materials: MaterialSearchItem[]) => { handleInputChange('items', materials); }; // 파일 업로드 핸들러들 const createFileUploadHandler = (setFiles: (files: File[]) => void, currentFiles: File[]) => ({ onDropAccepted: (acceptedFiles: File[]) => { const newFiles = [...currentFiles, ...acceptedFiles]; setFiles(newFiles); }, onDropRejected: (fileRejections: any[]) => { fileRejections.forEach((rej) => { toast({ variant: "destructive", title: t('fileError'), description: `${rej.file.name}: ${rej.errors[0]?.message || t('uploadFailed')}`, }); }); }, removeFile: (index: number) => { const updated = [...currentFiles]; updated.splice(index, 1); setFiles(updated); } }); const businessRegistrationHandler = createFileUploadHandler(setBusinessRegistrationFiles, businessRegistrationFiles); const isoCertificationHandler = createFileUploadHandler(setIsoCertificationFiles, isoCertificationFiles); const creditReportHandler = createFileUploadHandler(setCreditReportFiles, creditReportFiles); const bankAccountHandler = createFileUploadHandler(setBankAccountFiles, bankAccountFiles); // 유효성 검사 const validateRequiredFiles = (): string[] => { const errors: string[] = []; if (businessRegistrationFiles.length === 0) { errors.push(t('uploadBusinessRegistration')); } if (isoCertificationFiles.length === 0) { errors.push(t('uploadIsoCertification')); } if (creditReportFiles.length === 0) { errors.push(t('uploadCreditReport')); } if (data.country !== "KR" && bankAccountFiles.length === 0) { errors.push(t('uploadBankAccount')); } return errors; }; // 전화번호 검증 const vendorPhoneValidation = validatePhoneNumber(data.phone, data.country, t); const contactsValid = data.contacts.length > 0 && data.contacts[0].contactName && data.contacts.every(contact => contact.contactPhone ? validatePhoneNumber(contact.contactPhone, data.country, t).isValid : true ); // 대표자 이메일 검증, 비워두면 계정 이메일({{email}})을 사용합니다.(0825최겸수정) const isFormValid = data.vendorName && data.vendorTypeId && data.items && data.country && data.phone && vendorPhoneValidation.isValid && contactsValid && validateRequiredFiles().length === 0 // 최종 제출 const handleSubmit = async () => { const fileErrors = validateRequiredFiles(); if (fileErrors.length > 0) { toast({ variant: "destructive", title: t('fileUploadRequired'), description: fileErrors.join("\n"), }); return; } setIsSubmitting(true); try { // 업체 전화번호 정규화 const normalizedVendorPhone = normalizePhoneForStorage(data.phone, data.country); if (!normalizedVendorPhone) { toast({ variant: "destructive", title: t('error'), description: '업체 전화번호 형식이 올바르지 않습니다', }); return; } // 담당자 전화번호들 정규화 const normalizedContacts = data.contacts.map(contact => { if (contact.contactPhone) { const normalizedContactPhone = normalizePhoneForStorage(contact.contactPhone, data.country); if (!normalizedContactPhone) { throw new Error(`담당자 ${contact.contactName}의 전화번호 형식이 올바르지 않습니다`); } return { ...contact, contactPhone: normalizedContactPhone }; } return contact; }); // 대표자 전화번호 정규화 (한국 업체인 경우) let normalizedRepresentativePhone = data.representativePhone; if (data.country === "KR" && data.representativePhone) { const normalized = normalizePhoneForStorage(data.representativePhone, "KR"); if (!normalized) { throw new Error('대표자 전화번호 형식이 올바르지 않습니다'); } normalizedRepresentativePhone = normalized; } const formData = new FormData(); const completeData = { account: { ...accountData, // accountData.phone은 이미 AccountStep에서 정규화됨 }, vendor: { ...data, items: data.items, // 자재 배열을 그대로 전달 (서버에서 vendor_possible_materials 테이블에 저장) phone: normalizedVendorPhone, representativePhone: normalizedRepresentativePhone, contacts: normalizedContacts, email: data.email || accountData.email, }, consents: { privacy_policy: { agreed: consentData.privacy, version: policyVersions.privacy_policy }, terms_of_service: { agreed: consentData.terms, version: policyVersions.terms_of_service }, marketing: { agreed: consentData.marketing, version: policyVersions.privacy_policy } } }; formData.append('completeData', JSON.stringify(completeData)); businessRegistrationFiles.forEach(file => { formData.append('businessRegistration', file); }); isoCertificationFiles.forEach(file => { formData.append('isoCertification', file); }); creditReportFiles.forEach(file => { formData.append('creditReport', file); }); if (data.country !== "KR") { bankAccountFiles.forEach(file => { formData.append('bankAccount', file); }); } const response = await fetch('/api/auth/signup-with-vendor', { method: 'POST', body: formData, }); const result = await response.json(); if (response.ok) { toast({ title: t('registrationComplete'), description: t('registrationCompleteDesc'), }); onComplete(); } else { toast({ variant: "destructive", title: t('error'), description: result.error || t('registrationFailed'), }); } } catch (error) { console.error(error); toast({ variant: "destructive", title: t('error'), description: error instanceof Error ? error.message : t('errorOccurred'), }); } finally { setIsSubmitting(false); } }; return (

{t('vendorInfoRegistration')}

{t('vendorInfoDescription')}

{/* 기본 정보 */}

{t('basicInformation')}

{/* 업체 유형 */}
{t('noVendorTypeFound')} {vendorTypes.map((type) => ( handleInputChange('vendorTypeId', type.id)} > {lng === "ko" ? type.nameKo : type.nameEn} ))}
{/* 업체명 */}
handleInputChange('vendorName', e.target.value)} disabled={isSubmitting} />

{(data.country || accountData.country) === "KR" ? t('vendorNameHintKorea') : t('vendorNameHintOverseas')}

{/* 공급품목 */}

{t('supplyItemsHint')}

{/* 사업자등록번호 */}
handleInputChange('taxId', e.target.value)} disabled={isSubmitting} placeholder="123-45-67890" />
{/* 주소 */}
handleInputChange('address', e.target.value)} disabled={isSubmitting} />
{/* 상세주소 */}
handleInputChange('addressDetail', e.target.value)} disabled={isSubmitting} placeholder="상세주소를 입력해주세요" />
{/* 우편번호 */}
handleInputChange('postalCode', e.target.value)} disabled={isSubmitting} placeholder="우편번호를 입력해주세요" />
{/* 국가 */}
{t('noCountryFound')} {enhancedCountryArray.map((country) => ( handleInputChange('country', country.code)} > {country.label} ))}
{/* 대표 전화 - PhoneInput 사용 */}
handleInputChange('phone', value)} countryCode={data.country} disabled={isSubmitting} showValidation={true} t={t} />
{/* 대표 이메일 */}
handleInputChange('email', e.target.value)} disabled={isSubmitting} placeholder={accountData.email} />

{t('emailHint', { email: accountData.email })}

{/* 웹사이트 */}
handleInputChange('website', e.target.value)} disabled={isSubmitting} />
{/* 담당자 정보 */}

{t('contactInfo')}

{data.contacts.map((contact, index) => (
updateContact(index, 'contactName', e.target.value)} disabled={isSubmitting} />
updateContact(index, 'contactPosition', e.target.value)} disabled={isSubmitting} />
updateContact(index, 'contactDepartment', e.target.value)} disabled={isSubmitting} />
updateContact(index, 'contactEmail', e.target.value)} disabled={isSubmitting} />
updateContact(index, 'contactPhone', value)} countryCode={data.country} disabled={isSubmitting} showValidation={true} t={t} />
{data.contacts.length > 1 && (
)}
))}
{/* 한국 사업자 정보 */} {data.country === "KR" && (

{t('koreanBusinessInfo')}

handleInputChange('representativeName', e.target.value)} disabled={isSubmitting} />
handleInputChange('representativeBirth', e.target.value)} disabled={isSubmitting} />
handleInputChange('representativeEmail', e.target.value)} disabled={isSubmitting} />
handleInputChange('representativePhone', value)} countryCode="KR" disabled={isSubmitting} showValidation={true} t={t} />
handleInputChange('corporateRegistrationNumber', e.target.value)} disabled={isSubmitting} />
handleInputChange('representativeWorkExpirence', e.target.checked)} disabled={isSubmitting} className="h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" />
)} {/* 필수 첨부 서류 */}

{t('requiredDocuments')}

{data.country !== "KR" && ( )}
); } // 파일 업로드 섹션 컴포넌트 interface FileUploadSectionProps { title: string; description: string; files: File[]; onDropAccepted: (files: File[]) => void; onDropRejected: (fileRejections: any[]) => void; removeFile: (index: number) => void; isSubmitting: boolean; required?: boolean; t: (key: string, options?: any) => string; } function FileUploadSection({ title, description, files, onDropAccepted, onDropRejected, removeFile, isSubmitting, required = true, t }: FileUploadSectionProps) { return (
{title} {required && *}

{description}

{({ maxSize }) => (
{t('fileUpload')} {t('dragOrClick')} {maxSize ? ` (${t('maxSize')}: ${prettyBytes(maxSize)})` : null}
)}
{files.length > 0 && (
{files.map((file, i) => ( {file.name} {prettyBytes(file.size)} removeFile(i)}> ))}
)}
); }