From 5c9b39eb011763a7491b3e8542de9f6d4976dd65 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Mon, 23 Jun 2025 09:02:07 +0000
Subject: (최겸) 기술영업 벤더 개발
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../additional-info/tech-vendor-info-form.tsx      | 513 +++++++++++++++++++++
 1 file changed, 513 insertions(+)
 create mode 100644 components/additional-info/tech-vendor-info-form.tsx

(limited to 'components/additional-info/tech-vendor-info-form.tsx')

diff --git a/components/additional-info/tech-vendor-info-form.tsx b/components/additional-info/tech-vendor-info-form.tsx
new file mode 100644
index 00000000..8e6f7eaf
--- /dev/null
+++ b/components/additional-info/tech-vendor-info-form.tsx
@@ -0,0 +1,513 @@
+"use client"
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { useForm } from "react-hook-form"
+import { useSession } from "next-auth/react"
+
+import { Button } from "@/components/ui/button"
+import {
+  Form,
+  FormControl,
+  FormField,
+  FormItem,
+  FormLabel,
+  FormMessage,
+} from "@/components/ui/form"
+import { Input } from "@/components/ui/input"
+import { toast } from "@/hooks/use-toast"
+import { Download, Loader2, Mail, Phone, User } from "lucide-react"
+import { Badge } from "@/components/ui/badge"
+
+// 기술영업 벤더 관련 임포트
+import { getTechVendorDetailById, modifyTechVendor } from "@/lib/tech-vendors/service"
+import { updateTechVendorSchema, type UpdateTechVendorSchema } from "@/lib/tech-vendors/validations"
+
+import {
+  Card,
+  CardContent,
+  CardDescription,
+  CardHeader,
+  CardTitle,
+} from "@/components/ui/card"
+
+// 타입 정의
+interface TechVendorContact {
+  id: number
+  contactName: string
+  contactPosition: string | null
+  contactEmail: string
+  contactPhone: string | null
+  isPrimary: boolean
+}
+
+interface TechVendorAttachment {
+  id: number
+  fileName: string
+  filePath: string
+  attachmentType: string
+  createdAt: Date
+  updatedAt: Date
+}
+
+export function TechVendorInfoForm() {
+  const { data: session } = useSession()
+  const techCompanyId = session?.user?.techCompanyId
+
+  // 기술영업 벤더 데이터 상태
+  const [vendor, setVendor] = React.useState<any>(null)
+  const [contacts, setContacts] = React.useState<TechVendorContact[]>([])
+  const [attachments, setAttachments] = React.useState<TechVendorAttachment[]>([])
+  const [isLoading, setIsLoading] = React.useState(true)
+  const [isSubmitting, setIsSubmitting] = React.useState(false)
+  const [isDownloading, setIsDownloading] = React.useState(false)
+
+  // React Hook Form (기술영업 벤더용 스키마 사용)
+  const form = useForm<UpdateTechVendorSchema>({
+    resolver: zodResolver(updateTechVendorSchema),
+    defaultValues: {
+      vendorName: "",
+      vendorCode: "",
+      address: "",
+      email: "",
+      phone: "",
+      country: "",
+      website: "",
+      techVendorType: "",
+      status: "ACTIVE",
+    },
+    mode: "onChange",
+  })
+
+  const isFormValid = form.formState.isValid
+
+  // 기술영업 벤더 정보 가져오기
+  React.useEffect(() => {
+    async function fetchTechVendorData() {
+      if (!techCompanyId) return
+
+      try {
+        setIsLoading(true)
+        // 기술영업 벤더 상세 정보 가져오기 (연락처, 첨부파일 포함)
+        const vendorData = await getTechVendorDetailById(Number(techCompanyId))
+
+        if (!vendorData) {
+          toast({
+            variant: "destructive",
+            title: "오류",
+            description: "기술영업 벤더 정보를 찾을 수 없습니다.",
+          })
+          return
+        }
+
+        setVendor(vendorData)
+        setContacts(vendorData.contacts || [])
+        setAttachments(vendorData.attachments || [])
+
+        // 폼 기본값 설정
+        const formValues = {
+          vendorName: vendorData.vendorName || "",
+          vendorCode: vendorData.vendorCode || "",
+          address: vendorData.address || "",
+          email: vendorData.email || "",
+          phone: vendorData.phone || "",
+          country: vendorData.country || "",
+          website: vendorData.website || "",
+          techVendorType: vendorData.techVendorType || "",
+          status: vendorData.status || "ACTIVE",
+        }
+
+        form.reset(formValues)
+      } catch (error) {
+        console.error("Error fetching tech vendor data:", error)
+        toast({
+          variant: "destructive",
+          title: "데이터 로드 오류",
+          description: "기술영업 벤더 정보를 불러오는 중 오류가 발생했습니다.",
+        })
+      } finally {
+        setIsLoading(false)
+      }
+    }
+
+    fetchTechVendorData()
+  }, [techCompanyId, form])
+
+  const handleDownloadFile = async (file: TechVendorAttachment) => {
+    try {
+      setIsDownloading(true)
+      
+      const downloadUrl = `/api/tech-vendors/attachments/download?id=${file.id}&vendorId=${Number(techCompanyId)}`
+      
+      const downloadLink = document.createElement('a')
+      downloadLink.href = downloadUrl
+      downloadLink.download = file.fileName
+      downloadLink.target = '_blank'
+      document.body.appendChild(downloadLink)
+      downloadLink.click()
+      
+      setTimeout(() => {
+        document.body.removeChild(downloadLink)
+      }, 100)
+      
+      toast({
+        title: "다운로드 시작",
+        description: "파일 다운로드가 시작되었습니다.",
+      })
+    } catch (error) {
+      console.error("Error downloading file:", error)
+      toast({
+        variant: "destructive",
+        title: "다운로드 오류",
+        description: "파일 다운로드 중 오류가 발생했습니다.",
+      })
+    } finally {
+      setIsDownloading(false)
+    }
+  }
+
+  const handleDownloadAllFiles = async () => {
+    try {
+      setIsDownloading(true)
+
+      const downloadUrl = `/api/tech-vendors/attachments/download-all?vendorId=${Number(techCompanyId)}`
+      
+      const downloadLink = document.createElement('a')
+      downloadLink.href = downloadUrl
+      downloadLink.download = `tech-vendor-${techCompanyId}-files.zip`
+      downloadLink.target = '_blank'
+      document.body.appendChild(downloadLink)
+      downloadLink.click()
+      
+      setTimeout(() => {
+        document.body.removeChild(downloadLink)
+      }, 100)
+      
+      toast({
+        title: "다운로드 시작",
+        description: "전체 파일 다운로드가 시작되었습니다.",
+      })
+    } catch (error) {
+      console.error("Error downloading files:", error)
+      toast({
+        variant: "destructive",
+        title: "다운로드 오류",
+        description: "파일 다운로드 중 오류가 발생했습니다.",
+      })
+    } finally {
+      setIsDownloading(false)
+    }
+  }
+
+  async function onSubmit(values: UpdateTechVendorSchema) {
+    if (!techCompanyId) return
+    
+    setIsSubmitting(true)
+    
+    try {
+      const { error } = await modifyTechVendor({
+        ...values,
+        id: String(techCompanyId),
+      })
+
+      if (error) {
+        throw new Error(error)
+      }
+
+      toast({
+        title: "업데이트 완료",
+        description: "기술영업 벤더 정보가 성공적으로 업데이트되었습니다.",
+      })
+    } catch (error) {
+      console.error("Error updating tech vendor:", error)
+      toast({
+        variant: "destructive",
+        title: "업데이트 오류",
+        description: "기술영업 벤더 정보 업데이트 중 오류가 발생했습니다.",
+      })
+    } finally {
+      setIsSubmitting(false)
+    }
+  }
+
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center p-8">
+        <Loader2 className="h-8 w-8 animate-spin" />
+        <span className="ml-2">기술영업 벤더 정보를 불러오는 중...</span>
+      </div>
+    )
+  }
+
+  if (!vendor) {
+    return (
+      <div className="flex items-center justify-center p-8">
+        <span>기술영업 벤더 정보를 찾을 수 없습니다.</span>
+      </div>
+    )
+  }
+
+  return (
+    <div className="space-y-6 p-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h2 className="text-2xl font-bold">기술영업 벤더 정보</h2>
+          <p className="text-gray-600">기술영업 벤더 정보를 확인하고 업데이트할 수 있습니다.</p>
+        </div>
+        {attachments.length > 0 && (
+          <Button
+            variant="outline"
+            onClick={handleDownloadAllFiles}
+            disabled={isDownloading}
+          >
+            <Download className="h-4 w-4 mr-2" />
+            {isDownloading ? "다운로드 중..." : "전체 파일 다운로드"}
+          </Button>
+        )}
+      </div>
+
+      <Form {...form}>
+        <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
+          <Card>
+            <CardHeader>
+              <CardTitle>기본 정보</CardTitle>
+            </CardHeader>
+            <CardContent className="space-y-4">
+              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                <FormField
+                  control={form.control}
+                  name="vendorName"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>벤더명</FormLabel>
+                      <FormControl>
+                        <Input {...field} disabled />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+                <FormField
+                  control={form.control}
+                  name="vendorCode"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>벤더 코드</FormLabel>
+                      <FormControl>
+                        <Input {...field} disabled />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+              </div>
+
+              {/* 사업자등록번호 */}
+              {vendor.taxId && (
+                <div>
+                  <label className="text-sm font-medium text-gray-600">사업자등록번호</label>
+                  <p className="mt-1">{vendor.taxId}</p>
+                </div>
+              )}
+
+              {/* 공급품목 */}
+              {vendor.items && (
+                <div>
+                  <label className="text-sm font-medium text-gray-600">공급품목</label>
+                  <p className="mt-1 text-sm leading-relaxed">{vendor.items}</p>
+                </div>
+              )}
+
+              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                <FormField
+                  control={form.control}
+                  name="email"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>이메일</FormLabel>
+                      <FormControl>
+                        <Input {...field} type="email" />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+                <FormField
+                  control={form.control}
+                  name="phone"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>전화번호</FormLabel>
+                      <FormControl>
+                        <Input {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+              </div>
+
+              <FormField
+                control={form.control}
+                name="address"
+                render={({ field }) => (
+                  <FormItem>
+                    <FormLabel>주소</FormLabel>
+                    <FormControl>
+                      <Input {...field} />
+                    </FormControl>
+                    <FormMessage />
+                  </FormItem>
+                )}
+              />
+
+              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                <FormField
+                  control={form.control}
+                  name="country"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>국가</FormLabel>
+                      <FormControl>
+                        <Input {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+                <FormField
+                  control={form.control}
+                  name="website"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>웹사이트</FormLabel>
+                      <FormControl>
+                        <Input {...field} />
+                      </FormControl>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
+              </div>
+            </CardContent>
+          </Card>
+
+
+          {/* 연락처 정보 */}
+          {contacts.length > 0 && (
+            <Card>
+              <CardHeader>
+                <CardTitle>연락처 정보</CardTitle>
+                <CardDescription>
+                  등록된 연락처 정보입니다.
+                </CardDescription>
+              </CardHeader>
+              <CardContent>
+                <div className="space-y-4">
+                  {contacts.map((contact) => (
+                    <div
+                      key={contact.id}
+                      className="flex items-center justify-between p-4 border rounded-lg"
+                    >
+                      <div className="flex items-center space-x-4">
+                        <div className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-100">
+                          <User className="h-5 w-5 text-blue-600" />
+                        </div>
+                        <div>
+                          <div className="flex items-center space-x-2">
+                            <h4 className="font-medium">{contact.contactName}</h4>
+                            {contact.isPrimary && (
+                              <Badge variant="secondary">주 담당자</Badge>
+                            )}
+                          </div>
+                          {contact.contactPosition && (
+                            <p className="text-sm text-gray-600">{contact.contactPosition}</p>
+                          )}
+                        </div>
+                      </div>
+                      <div className="flex space-x-2">
+                        {contact.contactEmail && (
+                          <Button
+                            variant="ghost"
+                            size="sm"
+                            onClick={() => window.open(`mailto:${contact.contactEmail}`)}
+                          >
+                            <Mail className="h-4 w-4" />
+                          </Button>
+                        )}
+                        {contact.contactPhone && (
+                          <Button
+                            variant="ghost"
+                            size="sm"
+                            onClick={() => window.open(`tel:${contact.contactPhone}`)}
+                          >
+                            <Phone className="h-4 w-4" />
+                          </Button>
+                        )}
+                      </div>
+                    </div>
+                  ))}
+                </div>
+              </CardContent>
+            </Card>
+          )}
+
+          {/* 첨부파일 정보 */}
+          {attachments.length > 0 && (
+            <Card>
+              <CardHeader>
+                <CardTitle>첨부파일</CardTitle>
+                <CardDescription>
+                  업로드된 파일들을 확인하고 다운로드할 수 있습니다.
+                </CardDescription>
+              </CardHeader>
+              <CardContent>
+                <div className="space-y-2">
+                  {attachments.map((file) => (
+                    <div
+                      key={file.id}
+                      className="flex items-center justify-between p-3 border rounded-lg hover:bg-gray-50"
+                    >
+                      <div className="flex-1">
+                        <h4 className="font-medium">{file.fileName}</h4>
+                        <div className="flex items-center space-x-2 text-sm text-gray-600">
+                          <span>{file.attachmentType}</span>
+                          <span>•</span>
+                          <span>{new Date(file.createdAt).toLocaleDateString()}</span>
+                        </div>
+                      </div>
+                      <Button
+                        variant="ghost"
+                        size="sm"
+                        onClick={() => handleDownloadFile(file)}
+                        disabled={isDownloading}
+                      >
+                        <Download className="h-4 w-4" />
+                      </Button>
+                    </div>
+                  ))}
+                </div>
+              </CardContent>
+            </Card>
+          )}
+
+          <div className="flex justify-end space-x-4">
+            <Button
+              type="submit"
+              disabled={isSubmitting || !isFormValid}
+            >
+              {isSubmitting ? (
+                <>
+                  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+                  업데이트 중...
+                </>
+              ) : (
+                "정보 업데이트"
+              )}
+            </Button>
+          </div>
+        </form>
+      </Form>
+    </div>
+  )
+} 
\ No newline at end of file
-- 
cgit v1.2.3

