diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-13 11:05:09 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-13 11:05:09 +0000 |
| commit | 33be47506f0aa62b969d82521580a29e95080268 (patch) | |
| tree | 6b7e232f2d78ef8775944ea085a36b3ccbce7d95 /components/vendor-regular-registrations/additional-info-dialog.tsx | |
| parent | 2ac95090157c355ea1bd0b8eb1e1e5e2bd56faf4 (diff) | |
(대표님) 입찰, 법무검토, EDP 변경사항 대응, dolce 개선, form-data 개선, 정규업체 등록관리 추가
(최겸) pq 미사용 컴포넌트 및 페이지 제거, 파일 라우트에 pq 적용
Diffstat (limited to 'components/vendor-regular-registrations/additional-info-dialog.tsx')
| -rw-r--r-- | components/vendor-regular-registrations/additional-info-dialog.tsx | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/components/vendor-regular-registrations/additional-info-dialog.tsx b/components/vendor-regular-registrations/additional-info-dialog.tsx new file mode 100644 index 00000000..fbd60515 --- /dev/null +++ b/components/vendor-regular-registrations/additional-info-dialog.tsx @@ -0,0 +1,502 @@ +"use client";
+
+import * as React from "react";
+import { useState } from "react";
+import { useForm } from "react-hook-form";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { z } from "zod";
+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 {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Plus, Trash2 } from "lucide-react";
+import { toast } from "sonner";
+import {
+ saveVendorBusinessContacts,
+ saveVendorAdditionalInfo,
+ fetchVendorRegistrationStatus
+} from "@/lib/vendor-regular-registrations/service";
+
+// 업무담당자 정보 스키마
+const businessContactSchema = z.object({
+ contactType: z.enum(["sales", "design", "delivery", "quality", "tax_invoice"]),
+ contactName: z.string().min(1, "담당자명은 필수입니다"),
+ position: z.string().min(1, "직급은 필수입니다"),
+ department: z.string().min(1, "부서는 필수입니다"),
+ responsibility: z.string().min(1, "담당업무는 필수입니다"),
+ email: z.string().email("올바른 이메일 형식이 아닙니다"),
+});
+
+// 추가정보 스키마
+const additionalInfoSchema = z.object({
+ businessType: z.string().optional(),
+ industryType: z.string().optional(),
+ companySize: z.string().optional(),
+ revenue: z.string().optional(),
+ factoryEstablishedDate: z.string().optional(),
+ preferredContractTerms: z.string().optional(),
+});
+
+// 전체 폼 스키마
+const formSchema = z.object({
+ businessContacts: z.array(businessContactSchema).min(5, "모든 업무담당자 정보를 입력해주세요"),
+ additionalInfo: additionalInfoSchema,
+});
+
+type FormData = z.infer<typeof formSchema>;
+
+interface AdditionalInfoDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ vendorId: number;
+ onSave?: () => void;
+}
+
+const contactTypes = [
+ { value: "sales", label: "영업", required: true },
+ { value: "design", label: "설계", required: true },
+ { value: "delivery", label: "납기", required: true },
+ { value: "quality", label: "품질", required: true },
+ { value: "tax_invoice", label: "세금계산서", required: true },
+];
+
+const businessTypes = [
+ { value: "manufacturing", label: "제조업" },
+ { value: "trading", label: "무역업" },
+ { value: "service", label: "서비스업" },
+ { value: "construction", label: "건설업" },
+ { value: "other", label: "기타" },
+];
+
+const industryTypes = [
+ { value: "shipbuilding", label: "조선업" },
+ { value: "marine", label: "해양플랜트" },
+ { value: "energy", label: "에너지" },
+ { value: "automotive", label: "자동차" },
+ { value: "other", label: "기타" },
+];
+
+const companySizes = [
+ { value: "large", label: "대기업" },
+ { value: "medium", label: "중견기업" },
+ { value: "small", label: "중소기업" },
+ { value: "startup", label: "스타트업" },
+];
+
+export function AdditionalInfoDialog({
+ open,
+ onOpenChange,
+ vendorId,
+ onSave,
+}: AdditionalInfoDialogProps) {
+ const [saving, setSaving] = useState(false);
+ const [loading, setLoading] = useState(false);
+
+ const form = useForm<FormData>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ businessContacts: contactTypes.map(type => ({
+ contactType: type.value as any,
+ contactName: "",
+ position: "",
+ department: "",
+ responsibility: "",
+ email: "",
+ })),
+ additionalInfo: {
+ businessType: "",
+ industryType: "",
+ companySize: "",
+ revenue: "",
+ factoryEstablishedDate: "",
+ preferredContractTerms: "",
+ },
+ },
+ });
+
+ // 기존 데이터 로드
+ const loadExistingData = async () => {
+ if (!vendorId || !open) return;
+
+ setLoading(true);
+ try {
+ const result = await fetchVendorRegistrationStatus(vendorId);
+ if (result.success && result.data) {
+ const { businessContacts, additionalInfo } = result.data;
+
+ // 업무담당자 데이터 설정
+ const contactsData = contactTypes.map(type => {
+ const existingContact = businessContacts.find(c => c.contactType === type.value);
+ return existingContact ? {
+ contactType: existingContact.contactType,
+ contactName: existingContact.contactName,
+ position: existingContact.position,
+ department: existingContact.department,
+ responsibility: existingContact.responsibility,
+ email: existingContact.email,
+ } : {
+ contactType: type.value as any,
+ contactName: "",
+ position: "",
+ department: "",
+ responsibility: "",
+ email: "",
+ };
+ });
+
+ // 추가정보 데이터 설정
+ const additionalData = {
+ businessType: additionalInfo?.businessType || "",
+ industryType: additionalInfo?.industryType || "",
+ companySize: additionalInfo?.companySize || "",
+ revenue: additionalInfo?.revenue || "",
+ factoryEstablishedDate: additionalInfo?.factoryEstablishedDate
+ ? new Date(additionalInfo.factoryEstablishedDate).toISOString().split('T')[0]
+ : "",
+ preferredContractTerms: additionalInfo?.preferredContractTerms || "",
+ };
+
+ // 폼 데이터 업데이트
+ form.reset({
+ businessContacts: contactsData,
+ additionalInfo: additionalData,
+ });
+ }
+ } catch (error) {
+ console.error("Error loading existing data:", error);
+ toast.error("기존 데이터를 불러오는 중 오류가 발생했습니다.");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 다이얼로그가 열릴 때 데이터 로드
+ React.useEffect(() => {
+ loadExistingData();
+ }, [vendorId, open]);
+
+ const handleSave = async (data: FormData) => {
+ setSaving(true);
+ try {
+ // 업무담당자 정보 저장
+ const contactsResult = await saveVendorBusinessContacts(vendorId, data.businessContacts);
+ if (!contactsResult.success) {
+ throw new Error(contactsResult.error);
+ }
+
+ // 추가정보 저장
+ const additionalResult = await saveVendorAdditionalInfo(vendorId, data.additionalInfo);
+ if (!additionalResult.success) {
+ throw new Error(additionalResult.error);
+ }
+
+ toast.success("추가정보가 저장되었습니다.");
+ onSave?.();
+ onOpenChange(false);
+ } catch (error) {
+ console.error("Error saving additional info:", error);
+ toast.error(error instanceof Error ? error.message : "추가정보 저장 중 오류가 발생했습니다.");
+ } finally {
+ setSaving(false);
+ }
+ };
+
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
+ <DialogHeader>
+ <DialogTitle>추가정보 입력</DialogTitle>
+ <p className="text-sm text-muted-foreground">
+ 정규업체 등록을 위한 추가정보를 입력해주세요. <span className="text-red-500">*</span> 표시는 필수 입력 항목입니다.
+ </p>
+ </DialogHeader>
+
+ {loading ? (
+ <div className="p-8 text-center">
+ <div className="text-sm text-muted-foreground">데이터를 불러오는 중...</div>
+ </div>
+ ) : (
+
+ <Form {...form}>
+ <form onSubmit={form.handleSubmit(handleSave)}>
+ <Tabs defaultValue="contacts" className="w-full">
+ <TabsList className="grid w-full grid-cols-2">
+ <TabsTrigger value="contacts">업무담당자 정보</TabsTrigger>
+ <TabsTrigger value="additional">추가정보</TabsTrigger>
+ </TabsList>
+
+ <TabsContent value="contacts" className="space-y-4">
+ <div className="space-y-4">
+ {contactTypes.map((contactType, index) => (
+ <Card key={contactType.value}>
+ <CardHeader className="pb-3">
+ <CardTitle className="text-lg flex items-center gap-2">
+ {contactType.label} 담당자
+ <Badge variant="destructive" className="text-xs">필수</Badge>
+ </CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name={`businessContacts.${index}.contactName`}
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>담당자명 *</FormLabel>
+ <FormControl>
+ <Input placeholder="담당자명 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ <FormField
+ control={form.control}
+ name={`businessContacts.${index}.position`}
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>직급 *</FormLabel>
+ <FormControl>
+ <Input placeholder="직급 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name={`businessContacts.${index}.department`}
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>부서 *</FormLabel>
+ <FormControl>
+ <Input placeholder="부서명 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ <FormField
+ control={form.control}
+ name={`businessContacts.${index}.email`}
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>Email *</FormLabel>
+ <FormControl>
+ <Input placeholder="이메일 입력" type="email" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+ <FormField
+ control={form.control}
+ name={`businessContacts.${index}.responsibility`}
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>담당업무 *</FormLabel>
+ <FormControl>
+ <Textarea
+ placeholder="담당업무 상세 입력"
+ className="h-20"
+ {...field}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </CardContent>
+ </Card>
+ ))}
+ </div>
+ </TabsContent>
+
+ <TabsContent value="additional" className="space-y-4">
+ <Card>
+ <CardHeader>
+ <CardTitle>회사 추가정보</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="additionalInfo.businessType"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>사업유형</FormLabel>
+ <Select onValueChange={field.onChange} defaultValue={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="사업유형 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {businessTypes.map((type) => (
+ <SelectItem key={type.value} value={type.value}>
+ {type.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ <FormField
+ control={form.control}
+ name="additionalInfo.industryType"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>산업유형</FormLabel>
+ <Select onValueChange={field.onChange} defaultValue={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="산업유형 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {industryTypes.map((type) => (
+ <SelectItem key={type.value} value={type.value}>
+ {type.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="additionalInfo.companySize"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>기업규모</FormLabel>
+ <Select onValueChange={field.onChange} defaultValue={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="기업규모 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {companySizes.map((size) => (
+ <SelectItem key={size.value} value={size.value}>
+ {size.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ <FormField
+ control={form.control}
+ name="additionalInfo.revenue"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>매출액 (억원)</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="매출액 입력"
+ type="number"
+ {...field}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="additionalInfo.factoryEstablishedDate"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>공장설립일</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="YYYY-MM-DD"
+ type="date"
+ {...field}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+ <FormField
+ control={form.control}
+ name="additionalInfo.preferredContractTerms"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>선호계약조건</FormLabel>
+ <FormControl>
+ <Textarea
+ placeholder="선호하는 계약조건을 상세히 입력해주세요"
+ className="h-32"
+ {...field}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </CardContent>
+ </Card>
+ </TabsContent>
+ </Tabs>
+
+ <DialogFooter className="mt-6">
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => onOpenChange(false)}
+ disabled={saving}
+ >
+ 취소
+ </Button>
+ <Button type="submit" disabled={saving}>
+ {saving ? "저장 중..." : "저장"}
+ </Button>
+ </DialogFooter>
+ </form>
+ </Form>
+ )}
+ </DialogContent>
+ </Dialog>
+ );
+}
|
