diff options
Diffstat (limited to 'components/vendor-regular-registrations/registration-request-dialog.tsx')
| -rw-r--r-- | components/vendor-regular-registrations/registration-request-dialog.tsx | 691 |
1 files changed, 691 insertions, 0 deletions
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> + ); +} |
