From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Tue, 25 Mar 2025 15:55:45 +0900
Subject: initial commit

---
 components/signup/join-form.tsx | 1010 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 1010 insertions(+)
 create mode 100644 components/signup/join-form.tsx

(limited to 'components/signup/join-form.tsx')

diff --git a/components/signup/join-form.tsx b/components/signup/join-form.tsx
new file mode 100644
index 00000000..06aee3b5
--- /dev/null
+++ b/components/signup/join-form.tsx
@@ -0,0 +1,1010 @@
+"use client"
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { useForm, useFieldArray } from "react-hook-form"
+import { useRouter, useSearchParams, useParams } from "next/navigation"
+
+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 { Button } from "@/components/ui/button"
+import { Separator } from "@/components/ui/separator"
+import {
+  Form,
+  FormControl,
+  FormDescription,
+  FormField,
+  FormItem,
+  FormLabel,
+  FormMessage,
+} from "@/components/ui/form"
+import { Input } from "@/components/ui/input"
+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 { Check, ChevronsUpDown, Loader2, Plus, X } from "lucide-react"
+import { cn } from "@/lib/utils"
+import { useTranslation } from "@/i18n/client"
+
+import { createVendor } from "@/lib/vendors/service"
+import { createVendorSchema, CreateVendorSchema } from "@/lib/vendors/validations"
+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 { Badge } from "@/components/ui/badge"
+import { ScrollArea } from "@/components/ui/scroll-area"
+import prettyBytes from "pretty-bytes"
+
+i18nIsoCountries.registerLocale(enLocale)
+i18nIsoCountries.registerLocale(koLocale)
+
+const locale = "ko"
+const countryMap = i18nIsoCountries.getNames(locale, { select: "official" })
+const countryArray = Object.entries(countryMap).map(([code, label]) => ({
+  code,
+  label,
+}))
+
+// Example agencies + rating scales
+const creditAgencies = [
+  { value: "NICE", label: "NICE평가정보" },
+  { value: "KIS", label: "KIS (한국신용평가)" },
+  { value: "KED", label: "KED (한국기업데이터)" },
+  { value: "SCI", label: "SCI평가정보" },
+]
+const creditRatingScaleMap: Record<string, string[]> = {
+  NICE: ["AAA", "AA", "A", "BBB", "BB", "B", "C", "D"],
+  KIS: ["AAA", "AA+", "AA", "A+", "A", "BBB+", "BBB", "BB", "B", "C"],
+  KED: ["AAA", "AA", "A", "BBB", "BB", "B", "CCC", "CC", "C", "D"],
+  SCI: ["AAA", "AA+", "AA", "AA-", "A+", "A", "A-", "BBB+", "BBB-", "B"],
+}
+
+const MAX_FILE_SIZE = 3e9
+
+export function JoinForm() {
+  const params = useParams()
+  const lng = (params.lng as string) || "ko"
+  const { t } = useTranslation(lng, "translation")
+
+  const router = useRouter()
+  const searchParams = useSearchParams()
+  const defaultTaxId = searchParams.get("taxID") ?? ""
+
+  // File states
+  const [selectedFiles, setSelectedFiles] = React.useState<File[]>([])
+  const [creditRatingFile, setCreditRatingFile] = React.useState<File[]>([])
+  const [cashFlowRatingFile, setCashFlowRatingFile] = React.useState<File[]>([])
+
+  const [isSubmitting, setIsSubmitting] = React.useState(false)
+
+  // React Hook Form
+  const form = useForm<CreateVendorSchema>({
+    resolver: zodResolver(createVendorSchema),
+    defaultValues: {
+      vendorName: "",
+      taxId: defaultTaxId,
+      address: "",
+      email: "",
+      phone: "",
+      country: "",
+      representativeName: "",
+      representativeBirth: "",
+      representativeEmail: "",
+      representativePhone: "",
+      corporateRegistrationNumber: "",
+      creditAgency: "",
+      creditRating: "",
+      cashFlowRating: "",
+      attachedFiles: undefined,
+      creditRatingAttachment: undefined,
+      cashFlowRatingAttachment: undefined,
+      // contacts (no isPrimary)
+      contacts: [
+        {
+          contactName: "",
+          contactPosition: "",
+          contactEmail: "",
+          contactPhone: "",
+        },
+      ],
+    },
+    mode: "onChange",
+  })
+  const isFormValid = form.formState.isValid
+
+
+
+  // Field array for contacts
+  const { fields: contactFields, append: addContact, remove: removeContact } =
+    useFieldArray({
+      control: form.control,
+      name: "contacts",
+    })
+
+  // Dropzone handlers (same as before)...
+  const handleDropAccepted = (acceptedFiles: File[]) => {
+    const newFiles = [...selectedFiles, ...acceptedFiles]
+    setSelectedFiles(newFiles)
+    form.setValue("attachedFiles", newFiles, { shouldValidate: true })
+  }
+  const handleDropRejected = (fileRejections: any[]) => {
+    fileRejections.forEach((rej) => {
+      toast({
+        variant: "destructive",
+        title: "File Error",
+        description: `${rej.file.name}: ${rej.errors[0]?.message || "Upload failed"}`,
+      })
+    })
+  }
+  const removeFile = (index: number) => {
+    const updated = [...selectedFiles]
+    updated.splice(index, 1)
+    setSelectedFiles(updated)
+    form.setValue("attachedFiles", updated, { shouldValidate: true })
+  }
+
+  const handleCreditDropAccepted = (acceptedFiles: File[]) => {
+    const newFiles = [...creditRatingFile, ...acceptedFiles]
+    setCreditRatingFile(newFiles)
+    form.setValue("creditRatingAttachment", newFiles, { shouldValidate: true })
+  }
+  const handleCreditDropRejected = (fileRejections: any[]) => {
+    fileRejections.forEach((rej) => {
+      toast({
+        variant: "destructive",
+        title: "File Error",
+        description: `${rej.file.name}: ${rej.errors[0]?.message || "Upload failed"}`,
+      })
+    })
+  }
+  const removeCreditFile = (index: number) => {
+    const updated = [...creditRatingFile]
+    updated.splice(index, 1)
+    setCreditRatingFile(updated)
+    form.setValue("creditRatingAttachment", updated, { shouldValidate: true })
+  }
+
+  const handleCashFlowDropAccepted = (acceptedFiles: File[]) => {
+    const newFiles = [...cashFlowRatingFile, ...acceptedFiles]
+    setCashFlowRatingFile(newFiles)
+    form.setValue("cashFlowRatingAttachment", newFiles, { shouldValidate: true })
+  }
+  const handleCashFlowDropRejected = (fileRejections: any[]) => {
+    fileRejections.forEach((rej) => {
+      toast({
+        variant: "destructive",
+        title: "File Error",
+        description: `${rej.file.name}: ${rej.errors[0]?.message || "Upload failed"}`,
+      })
+    })
+  }
+  const removeCashFlowFile = (index: number) => {
+    const updated = [...cashFlowRatingFile]
+    updated.splice(index, 1)
+    setCashFlowRatingFile(updated)
+    form.setValue("cashFlowRatingAttachment", updated, { shouldValidate: true })
+  }
+
+  // Submit
+  async function onSubmit(values: CreateVendorSchema) {
+    setIsSubmitting(true)
+    try {
+      const mainFiles = values.attachedFiles
+        ? Array.from(values.attachedFiles as FileList)
+        : []
+      const creditRatingFiles = values.creditRatingAttachment
+        ? Array.from(values.creditRatingAttachment as FileList)
+        : []
+      const cashFlowRatingFiles = values.cashFlowRatingAttachment
+        ? Array.from(values.cashFlowRatingAttachment as FileList)
+        : []
+
+      const vendorData = {
+        vendorName: values.vendorName,
+        vendorCode: values.vendorCode,
+        website: values.website,
+        taxId: values.taxId,
+        address: values.address,
+        email: values.email,
+        phone: values.phone,
+        country: values.country,
+        status: "PENDING_REVIEW" as const,
+        representativeName: values.representativeName || "",
+        representativeBirth: values.representativeBirth || "",
+        representativeEmail: values.representativeEmail || "",
+        representativePhone: values.representativePhone || "",
+        corporateRegistrationNumber: values.corporateRegistrationNumber || "",
+        creditAgency: values.creditAgency || "",
+        creditRating: values.creditRating || "",
+        cashFlowRating: values.cashFlowRating || "",
+      }
+
+      const result = await createVendor({
+        vendorData,
+        files: mainFiles,
+        creditRatingFiles,
+        cashFlowRatingFiles,
+        contacts: values.contacts,
+      })
+
+      if (!result.error) {
+        toast({
+          title: "등록 완료",
+          description: "회사 등록이 완료되었습니다. (status=PENDING_REVIEW)",
+        })
+        router.push("/")
+      } else {
+        toast({
+          variant: "destructive",
+          title: "오류",
+          description: result.error || "등록에 실패했습니다.",
+        })
+      }
+    } catch (error: any) {
+      console.error(error)
+      toast({
+        variant: "destructive",
+        title: "서버 에러",
+        description: error.message || "에러가 발생했습니다.",
+      })
+    } finally {
+      setIsSubmitting(false)
+    }
+  }
+
+  // Render
+  return (
+    <div className="container py-6">
+      <section className="overflow-hidden rounded-md border bg-background shadow-sm">
+        <div className="p-6 md:p-10 space-y-6">
+          <div className="space-y-2">
+            <h3 className="text-xl font-semibold">
+              {defaultTaxId}{" "}
+              {t("joinForm.title", {
+                defaultValue: "Vendor Administrator Creation",
+              })}
+            </h3>
+            <p className="text-sm text-muted-foreground">
+              {t("joinForm.description", {
+                defaultValue:
+                  "Please provide basic company information and attach any required documents (e.g., business registration). We will review and approve as soon as possible.",
+              })}
+            </p>
+          </div>
+
+          <Separator />
+
+          <Form {...form}>
+            <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
+              {/* ─────────────────────────────────────────
+                  Basic Info
+              ───────────────────────────────────────── */}
+              <div className="rounded-md border p-4 space-y-4">
+                <h4 className="text-md font-semibold">기본 정보</h4>
+                <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                  {/* vendorName is required in the schema → show * */}
+                  <FormField
+                    control={form.control}
+                    name="vendorName"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                          업체명
+                        </FormLabel>
+                        <FormControl>
+                          <Input {...field} disabled={isSubmitting} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  {/* Address (optional, no * here) */}
+                  <FormField
+                    control={form.control}
+                    name="address"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>주소</FormLabel>
+                        <FormControl>
+                          <Input {...field} disabled={isSubmitting} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={form.control}
+                    name="phone"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>대표 전화</FormLabel>
+                        <FormControl>
+                          <Input {...field} disabled={isSubmitting} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  {/* email is required → show * */}
+                  <FormField
+                    control={form.control}
+                    name="email"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                          대표 이메일
+                        </FormLabel>
+                        <FormControl>
+                          <Input {...field} disabled={isSubmitting} />
+                        </FormControl>
+                        <FormDescription>
+                          회사 대표 이메일(관리자 로그인에 사용될 수 있음)
+                        </FormDescription>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  {/* website optional */}
+                  <FormField
+                    control={form.control}
+                    name="website"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>웹사이트</FormLabel>
+                        <FormControl>
+                          <Input {...field} disabled={isSubmitting} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={form.control}
+                    name="country"
+                    render={({ field }) => {
+                      const selectedCountry = countryArray.find(
+                        (c) => c.code === field.value
+                      )
+                      return (
+                        <FormItem>
+                          <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                            Country
+                          </FormLabel>
+                          <Popover>
+                            <PopoverTrigger asChild>
+                              <FormControl>
+                                <Button
+                                  variant="outline"
+                                  role="combobox"
+                                  className={cn(
+                                    "w-full justify-between",
+                                    !field.value && "text-muted-foreground"
+                                  )}
+                                  disabled={isSubmitting}
+                                >
+                                  {selectedCountry
+                                    ? selectedCountry.label
+                                    : "Select a country"}
+                                  <ChevronsUpDown className="ml-2 opacity-50" />
+                                </Button>
+                              </FormControl>
+                            </PopoverTrigger>
+                            <PopoverContent className="w-full p-0">
+                              <Command>
+                                <CommandInput placeholder="Search country..." />
+                                <CommandList>
+                                  <CommandEmpty>No country found.</CommandEmpty>
+                                  <CommandGroup>
+                                    {countryArray.map((country) => (
+                                      <CommandItem
+                                        key={country.code}
+                                        value={country.label}
+                                        onSelect={() =>
+                                          field.onChange(country.code)
+                                        }
+                                      >
+                                        <Check
+                                          className={cn(
+                                            "mr-2",
+                                            country.code === field.value
+                                              ? "opacity-100"
+                                              : "opacity-0"
+                                          )}
+                                        />
+                                        {country.label}
+                                      </CommandItem>
+                                    ))}
+                                  </CommandGroup>
+                                </CommandList>
+                              </Command>
+                            </PopoverContent>
+                          </Popover>
+                          <FormMessage />
+                        </FormItem>
+                      )
+                    }}
+                  />
+                </div>
+              </div>
+
+              {/* ─────────────────────────────────────────
+                  담당자 정보 (contacts)
+              ───────────────────────────────────────── */}
+              <div className="rounded-md border p-4 space-y-4">
+                <div className="flex items-center justify-between">
+                  <h4 className="text-md font-semibold">담당자 정보 (최소 1명)</h4>
+                  <Button
+                    type="button"
+                    variant="outline"
+                    onClick={() =>
+                      addContact({
+                        contactName: "",
+                        contactPosition: "",
+                        contactEmail: "",
+                        contactPhone: "",
+                      })
+                    }
+                    disabled={isSubmitting}
+                  >
+                    <Plus className="mr-1 h-4 w-4" />
+                    Add Contact
+                  </Button>
+                </div>
+
+                <div className="space-y-2">
+                  {contactFields.map((contact, index) => (
+                    <div
+                      key={contact.id}
+                      className="bg-muted/10 rounded-md p-4 space-y-4"
+                    >
+                      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
+                        {/* contactName → required */}
+                        <FormField
+                          control={form.control}
+                          name={`contacts.${index}.contactName`}
+                          render={({ field }) => (
+                            <FormItem>
+                              <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                                담당자명
+                              </FormLabel>
+                              <FormControl>
+                                <Input {...field} disabled={isSubmitting} />
+                              </FormControl>
+                              <FormMessage />
+                            </FormItem>
+                          )}
+                        />
+
+                        {/* contactPosition → optional */}
+                        <FormField
+                          control={form.control}
+                          name={`contacts.${index}.contactPosition`}
+                          render={({ field }) => (
+                            <FormItem>
+                              <FormLabel>직급 / 부서</FormLabel>
+                              <FormControl>
+                                <Input {...field} disabled={isSubmitting} />
+                              </FormControl>
+                              <FormMessage />
+                            </FormItem>
+                          )}
+                        />
+
+                        {/* contactEmail → required */}
+                        <FormField
+                          control={form.control}
+                          name={`contacts.${index}.contactEmail`}
+                          render={({ field }) => (
+                            <FormItem>
+                              <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                                이메일
+                              </FormLabel>
+                              <FormControl>
+                                <Input {...field} disabled={isSubmitting} />
+                              </FormControl>
+                              <FormMessage />
+                            </FormItem>
+                          )}
+                        />
+
+                        {/* contactPhone → optional */}
+                        <FormField
+                          control={form.control}
+                          name={`contacts.${index}.contactPhone`}
+                          render={({ field }) => (
+                            <FormItem>
+                              <FormLabel>전화번호</FormLabel>
+                              <FormControl>
+                                <Input {...field} disabled={isSubmitting} />
+                              </FormControl>
+                              <FormMessage />
+                            </FormItem>
+                          )}
+                        />
+                      </div>
+
+                      {/* Remove contact button row */}
+                      {contactFields.length > 1 && (
+                        <div className="flex justify-end">
+                          <Button
+                            variant="destructive"
+                            onClick={() => removeContact(index)}
+                            disabled={isSubmitting}
+                          >
+                            <X className="mr-1 h-4 w-4" />
+                            Remove
+                          </Button>
+                        </div>
+                      )}
+                    </div>
+                  ))}
+                </div>
+              </div>
+
+              {/* ─────────────────────────────────────────
+                  한국 사업자 (country === "KR")
+              ───────────────────────────────────────── */}
+              {form.watch("country") === "KR" && (
+                <div className="rounded-md border p-4 space-y-4">
+                  <h4 className="text-md font-semibold">한국 사업자 정보</h4>
+
+                  {/* 대표자 등... all optional or whichever you want * for */}
+                  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                    <FormField
+                      control={form.control}
+                      name="representativeName"
+                      render={({ field }) => (
+                        <FormItem>
+                          <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                            대표자 이름
+                          </FormLabel>
+                          <FormControl>
+                            <Input {...field} disabled={isSubmitting} />
+                          </FormControl>
+                          <FormMessage />
+                        </FormItem>
+                      )}
+                    />
+                    <FormField
+                      control={form.control}
+                      name="representativeBirth"
+                      render={({ field }) => (
+                        <FormItem>
+                          <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                            대표자 생년월일
+                          </FormLabel>
+                          <FormControl>
+                            <Input
+                              placeholder="YYYY-MM-DD"
+                              {...field}
+                              disabled={isSubmitting}
+                            />
+                          </FormControl>
+                          <FormMessage />
+                        </FormItem>
+                      )}
+                    />
+                    <FormField
+                      control={form.control}
+                      name="representativeEmail"
+                      render={({ field }) => (
+                        <FormItem>
+                          <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                            대표자 이메일
+                          </FormLabel>
+                          <FormControl>
+                            <Input {...field} disabled={isSubmitting} />
+                          </FormControl>
+                          <FormMessage />
+                        </FormItem>
+                      )}
+                    />
+                    <FormField
+                      control={form.control}
+                      name="representativePhone"
+                      render={({ field }) => (
+                        <FormItem>
+                          <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                            대표자 전화번호
+                          </FormLabel>
+                          <FormControl>
+                            <Input {...field} disabled={isSubmitting} />
+                          </FormControl>
+                          <FormMessage />
+                        </FormItem>
+                      )}
+                    />
+                    <FormField
+                      control={form.control}
+                      name="corporateRegistrationNumber"
+                      render={({ field }) => (
+                        <FormItem>
+                          <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                            법인등록번호
+                          </FormLabel>
+                          <FormControl>
+                            <Input {...field} disabled={isSubmitting} />
+                          </FormControl>
+                          <FormMessage />
+                        </FormItem>
+                      )}
+                    />
+                  </div>
+
+                  <Separator />
+
+                  {/* 신용/현금 흐름 */}
+                  <div className="space-y-2">
+                    <FormField
+                      control={form.control}
+                      name="creditAgency"
+                      render={({ field }) => {
+                        const agencyValue = field.value
+                        return (
+                          <FormItem>
+                            <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                              평가사
+                            </FormLabel>
+                            <Select
+                              onValueChange={field.onChange}
+                              defaultValue={agencyValue}
+                            >
+                              <FormControl>
+                                <SelectTrigger disabled={isSubmitting}>
+                                  <SelectValue placeholder="평가사 선택" />
+                                </SelectTrigger>
+                              </FormControl>
+                              <SelectContent>
+                                {creditAgencies.map((agency) => (
+                                  <SelectItem
+                                    key={agency.value}
+                                    value={agency.value}
+                                  >
+                                    {agency.label}
+                                  </SelectItem>
+                                ))}
+                              </SelectContent>
+                            </Select>
+                            <FormDescription>
+                              신용평가 및 현금흐름등급에 사용할 평가사
+                            </FormDescription>
+                            <FormMessage />
+                          </FormItem>
+                        )
+                      }}
+                    />
+                    {form.watch("creditAgency") && (
+                      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                        {/* 신용평가등급 */}
+                        <FormField
+                          control={form.control}
+                          name="creditRating"
+                          render={({ field }) => {
+                            const selectedAgency = form.watch("creditAgency")
+                            const ratingScale =
+                              creditRatingScaleMap[
+                              selectedAgency as keyof typeof creditRatingScaleMap
+                              ] || []
+                            return (
+                              <FormItem>
+                                <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                                  신용평가등급
+                                </FormLabel>
+                                <Select
+                                  onValueChange={field.onChange}
+                                  defaultValue={field.value}
+                                >
+                                  <FormControl>
+                                    <SelectTrigger disabled={isSubmitting}>
+                                      <SelectValue placeholder="등급 선택" />
+                                    </SelectTrigger>
+                                  </FormControl>
+                                  <SelectContent>
+                                    {ratingScale.map((r) => (
+                                      <SelectItem key={r} value={r}>
+                                        {r}
+                                      </SelectItem>
+                                    ))}
+                                  </SelectContent>
+                                </Select>
+                                <FormMessage />
+                              </FormItem>
+                            )
+                          }}
+                        />
+                        {/* 현금흐름등급 */}
+                        <FormField
+                          control={form.control}
+                          name="cashFlowRating"
+                          render={({ field }) => {
+                            const selectedAgency = form.watch("creditAgency")
+                            const ratingScale =
+                              creditRatingScaleMap[
+                              selectedAgency as keyof typeof creditRatingScaleMap
+                              ] || []
+                            return (
+                              <FormItem>
+                                <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                                  현금흐름등급
+                                </FormLabel>
+                                <Select
+                                  onValueChange={field.onChange}
+                                  defaultValue={field.value}
+                                >
+                                  <FormControl>
+                                    <SelectTrigger disabled={isSubmitting}>
+                                      <SelectValue placeholder="등급 선택" />
+                                    </SelectTrigger>
+                                  </FormControl>
+                                  <SelectContent>
+                                    {ratingScale.map((r) => (
+                                      <SelectItem key={r} value={r}>
+                                        {r}
+                                      </SelectItem>
+                                    ))}
+                                  </SelectContent>
+                                </Select>
+                                <FormMessage />
+                              </FormItem>
+                            )
+                          }}
+                        />
+                      </div>
+                    )}
+                  </div>
+
+                  {/* Credit/CashFlow Attachments */}
+                  {form.watch("creditAgency") && (
+                    <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                      <FormField
+                        control={form.control}
+                        name="creditRatingAttachment"
+                        render={() => (
+                          <FormItem>
+                            <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                              신용평가등급 첨부</FormLabel>
+                            <Dropzone
+                              maxSize={MAX_FILE_SIZE}
+                              multiple
+                              onDropAccepted={handleCreditDropAccepted}
+                              onDropRejected={handleCreditDropRejected}
+                              disabled={isSubmitting}
+                            >
+                              {({ maxSize }) => (
+                                <DropzoneZone className="flex justify-center">
+                                  <DropzoneInput />
+                                  <div className="flex items-center gap-4">
+                                    <DropzoneUploadIcon />
+                                    <div className="grid gap-1">
+                                      <DropzoneTitle>드래그 또는 클릭</DropzoneTitle>
+                                      <DropzoneDescription>
+                                        최대: {maxSize ? prettyBytes(maxSize) : "무제한"}
+                                      </DropzoneDescription>
+                                    </div>
+                                  </div>
+                                </DropzoneZone>
+                              )}
+                            </Dropzone>
+                            {creditRatingFile.length > 0 && (
+                              <div className="mt-2">
+                                <ScrollArea className="max-h-32">
+                                  <FileList className="gap-2">
+                                    {creditRatingFile.map((file, i) => (
+                                      <FileListItem key={file.name + i}>
+                                        <FileListHeader>
+                                          <FileListIcon />
+                                          <FileListInfo>
+                                            <FileListName>{file.name}</FileListName>
+                                            <FileListDescription>
+                                              {prettyBytes(file.size)}
+                                            </FileListDescription>
+                                          </FileListInfo>
+                                          <FileListAction
+                                            onClick={() => removeCreditFile(i)}
+                                          >
+                                            <X className="h-4 w-4" />
+                                          </FileListAction>
+                                        </FileListHeader>
+                                      </FileListItem>
+                                    ))}
+                                  </FileList>
+                                </ScrollArea>
+                              </div>
+                            )}
+                          </FormItem>
+                        )}
+                      />
+                      {/* Cash Flow Attachment */}
+                      <FormField
+                        control={form.control}
+                        name="cashFlowRatingAttachment"
+                        render={() => (
+                          <FormItem>
+                            <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                              현금흐름등급 첨부</FormLabel>
+                            <Dropzone
+                              maxSize={MAX_FILE_SIZE}
+                              multiple
+                              onDropAccepted={handleCashFlowDropAccepted}
+                              onDropRejected={handleCashFlowDropRejected}
+                              disabled={isSubmitting}
+                            >
+                              {({ maxSize }) => (
+                                <DropzoneZone className="flex justify-center">
+                                  <DropzoneInput />
+                                  <div className="flex items-center gap-4">
+                                    <DropzoneUploadIcon />
+                                    <div className="grid gap-1">
+                                      <DropzoneTitle>드래그 또는 클릭</DropzoneTitle>
+                                      <DropzoneDescription>
+                                        최대: {maxSize ? prettyBytes(maxSize) : "무제한"}
+                                      </DropzoneDescription>
+                                    </div>
+                                  </div>
+                                </DropzoneZone>
+                              )}
+                            </Dropzone>
+                            {cashFlowRatingFile.length > 0 && (
+                              <div className="mt-2">
+                                <ScrollArea className="max-h-32">
+                                  <FileList className="gap-2">
+                                    {cashFlowRatingFile.map((file, i) => (
+                                      <FileListItem key={file.name + i}>
+                                        <FileListHeader>
+                                          <FileListIcon />
+                                          <FileListInfo>
+                                            <FileListName>{file.name}</FileListName>
+                                            <FileListDescription>
+                                              {prettyBytes(file.size)}
+                                            </FileListDescription>
+                                          </FileListInfo>
+                                          <FileListAction
+                                            onClick={() => removeCashFlowFile(i)}
+                                          >
+                                            <X className="h-4 w-4" />
+                                          </FileListAction>
+                                        </FileListHeader>
+                                      </FileListItem>
+                                    ))}
+                                  </FileList>
+                                </ScrollArea>
+                              </div>
+                            )}
+                          </FormItem>
+                        )}
+                      />
+                    </div>
+                  )}
+                </div>
+              )}
+
+              {/* ─────────────────────────────────────────
+                  첨부파일 (사업자등록증 등)
+              ───────────────────────────────────────── */}
+              <div className="rounded-md border p-4 space-y-4">
+                <h4 className="text-md font-semibold">기타 첨부파일</h4>
+                <FormField
+                  control={form.control}
+                  name="attachedFiles"
+                  render={() => (
+                    <FormItem>
+                      <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+                        첨부 파일
+                      </FormLabel>
+                      <Dropzone
+                        maxSize={MAX_FILE_SIZE}
+                        multiple
+                        onDropAccepted={handleDropAccepted}
+                        onDropRejected={handleDropRejected}
+                        disabled={isSubmitting}
+                      >
+                        {({ maxSize }) => (
+                          <DropzoneZone className="flex justify-center">
+                            <DropzoneInput />
+                            <div className="flex items-center gap-4">
+                              <DropzoneUploadIcon />
+                              <div className="grid gap-1">
+                                <DropzoneTitle>파일 업로드</DropzoneTitle>
+                                <DropzoneDescription>
+                                  드래그 또는 클릭
+                                  {maxSize
+                                    ? ` (최대: ${prettyBytes(maxSize)})`
+                                    : null}
+                                </DropzoneDescription>
+                              </div>
+                            </div>
+                          </DropzoneZone>
+                        )}
+                      </Dropzone>
+                      {selectedFiles.length > 0 && (
+                        <div className="mt-2">
+                          <ScrollArea className="max-h-32">
+                            <FileList className="gap-2">
+                              {selectedFiles.map((file, i) => (
+                                <FileListItem key={file.name + i}>
+                                  <FileListHeader>
+                                    <FileListIcon />
+                                    <FileListInfo>
+                                      <FileListName>{file.name}</FileListName>
+                                      <FileListDescription>
+                                        {prettyBytes(file.size)}
+                                      </FileListDescription>
+                                    </FileListInfo>
+                                    <FileListAction onClick={() => removeFile(i)}>
+                                      <X className="h-4 w-4" />
+                                    </FileListAction>
+                                  </FileListHeader>
+                                </FileListItem>
+                              ))}
+                            </FileList>
+                          </ScrollArea>
+                        </div>
+                      )}
+                    </FormItem>
+                  )}
+                />
+              </div>
+
+              {/* ─────────────────────────────────────────
+                  Submit
+              ───────────────────────────────────────── */}
+              <div className="flex justify-end">
+                <Button type="submit" disabled={!isFormValid || isSubmitting}>
+                  {isSubmitting ? (
+                    <>
+                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+                      등록 중...
+                    </>
+                  ) : (
+                    "Submit"
+                  )}
+                </Button>
+              </div>
+            </form>
+          </Form>
+        </div>
+      </section>
+    </div>
+  )
+}
\ No newline at end of file
-- 
cgit v1.2.3

