summaryrefslogtreecommitdiff
path: root/components/signup/join-form.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/signup/join-form.tsx')
-rw-r--r--components/signup/join-form.tsx323
1 files changed, 270 insertions, 53 deletions
diff --git a/components/signup/join-form.tsx b/components/signup/join-form.tsx
index 6f9ad891..e53e779f 100644
--- a/components/signup/join-form.tsx
+++ b/components/signup/join-form.tsx
@@ -39,7 +39,7 @@ 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 { createVendor, getVendorTypes } from "@/lib/vendors/service"
import { createVendorSchema, CreateVendorSchema } from "@/lib/vendors/validations"
import {
Select,
@@ -81,6 +81,63 @@ const countryArray = Object.entries(countryMap).map(([code, label]) => ({
label,
}))
+// Sort countries to put Korea first, then alphabetically
+const sortedCountryArray = [...countryArray].sort((a, b) => {
+ // Put Korea (KR) at the top
+ if (a.code === "KR") return -1;
+ if (b.code === "KR") return 1;
+
+ // Otherwise sort alphabetically
+ return a.label.localeCompare(b.label);
+});
+
+// Add English names for Korean locale
+const enhancedCountryArray = sortedCountryArray.map(country => ({
+ ...country,
+ label: locale === "ko" && country.code === "KR"
+ ? "대한민국 (South Korea)"
+ : country.label
+}));
+
+// Comprehensive list of country dial codes
+const countryDialCodes: { [key: string]: string } = {
+ AF: "+93", AL: "+355", DZ: "+213", AS: "+1-684", AD: "+376", AO: "+244",
+ AI: "+1-264", AG: "+1-268", AR: "+54", AM: "+374", AW: "+297", AU: "+61",
+ AT: "+43", AZ: "+994", BS: "+1-242", BH: "+973", BD: "+880", BB: "+1-246",
+ BY: "+375", BE: "+32", BZ: "+501", BJ: "+229", BM: "+1-441", BT: "+975",
+ BO: "+591", BA: "+387", BW: "+267", BR: "+55", BN: "+673", BG: "+359",
+ BF: "+226", BI: "+257", KH: "+855", CM: "+237", CA: "+1", CV: "+238",
+ KY: "+1-345", CF: "+236", TD: "+235", CL: "+56", CN: "+86", CO: "+57",
+ KM: "+269", CG: "+242", CD: "+243", CR: "+506", CI: "+225", HR: "+385",
+ CU: "+53", CY: "+357", CZ: "+420", DK: "+45", DJ: "+253", DM: "+1-767",
+ DO: "+1-809", EC: "+593", EG: "+20", SV: "+503", GQ: "+240", ER: "+291",
+ EE: "+372", ET: "+251", FJ: "+679", FI: "+358", FR: "+33", GA: "+241",
+ GM: "+220", GE: "+995", DE: "+49", GH: "+233", GR: "+30", GD: "+1-473",
+ GT: "+502", GN: "+224", GW: "+245", GY: "+592", HT: "+509", HN: "+504",
+ HK: "+852", HU: "+36", IS: "+354", IN: "+91", ID: "+62", IR: "+98",
+ IQ: "+964", IE: "+353", IL: "+972", IT: "+39", JM: "+1-876", JP: "+81",
+ JO: "+962", KZ: "+7", KE: "+254", KI: "+686", KR: "+82", KW: "+965",
+ KG: "+996", LA: "+856", LV: "+371", LB: "+961", LS: "+266", LR: "+231",
+ LY: "+218", LI: "+423", LT: "+370", LU: "+352", MK: "+389", MG: "+261",
+ MW: "+265", MY: "+60", MV: "+960", ML: "+223", MT: "+356", MH: "+692",
+ MR: "+222", MU: "+230", MX: "+52", FM: "+691", MD: "+373", MC: "+377",
+ MN: "+976", ME: "+382", MA: "+212", MZ: "+258", MM: "+95", NA: "+264",
+ NR: "+674", NP: "+977", NL: "+31", NZ: "+64", NI: "+505", NE: "+227",
+ NG: "+234", NU: "+683", KP: "+850", NO: "+47", OM: "+968", PK: "+92",
+ PW: "+680", PS: "+970", PA: "+507", PG: "+675", PY: "+595", PE: "+51",
+ PH: "+63", PL: "+48", PT: "+351", PR: "+1-787", QA: "+974", RO: "+40",
+ RU: "+7", RW: "+250", KN: "+1-869", LC: "+1-758", VC: "+1-784", WS: "+685",
+ SM: "+378", ST: "+239", SA: "+966", SN: "+221", RS: "+381", SC: "+248",
+ SL: "+232", SG: "+65", SK: "+421", SI: "+386", SB: "+677", SO: "+252",
+ ZA: "+27", SS: "+211", ES: "+34", LK: "+94", SD: "+249", SR: "+597",
+ SZ: "+268", SE: "+46", CH: "+41", SY: "+963", TW: "+886", TJ: "+992",
+ TZ: "+255", TH: "+66", TL: "+670", TG: "+228", TK: "+690", TO: "+676",
+ TT: "+1-868", TN: "+216", TR: "+90", TM: "+993", TV: "+688", UG: "+256",
+ UA: "+380", AE: "+971", GB: "+44", US: "+1", UY: "+598", UZ: "+998",
+ VU: "+678", VA: "+39-06", VE: "+58", VN: "+84", YE: "+967", ZM: "+260",
+ ZW: "+263"
+};
+
const MAX_FILE_SIZE = 3e9
export function JoinForm() {
@@ -92,16 +149,54 @@ export function JoinForm() {
const searchParams = useSearchParams() || new URLSearchParams();
const defaultTaxId = searchParams.get("taxID") ?? ""
+ // Define VendorType interface
+ interface VendorType {
+ id: number;
+ code: string;
+ nameKo: string;
+ nameEn: string;
+ }
+
+ // Vendor Types state with proper typing
+ const [vendorTypes, setVendorTypes] = React.useState<VendorType[]>([])
+ const [isLoadingVendorTypes, setIsLoadingVendorTypes] = React.useState(true)
+
// File states
const [selectedFiles, setSelectedFiles] = React.useState<File[]>([])
const [isSubmitting, setIsSubmitting] = React.useState(false)
+ // Fetch vendor types on component mount
+ React.useEffect(() => {
+ async function loadVendorTypes() {
+ setIsLoadingVendorTypes(true)
+ try {
+ const result = await getVendorTypes()
+ if (result.data) {
+ setVendorTypes(result.data)
+ }
+ } catch (error) {
+ console.error("Failed to load vendor types:", error)
+ toast({
+ variant: "destructive",
+ title: "Error",
+ description: "Failed to load vendor types",
+ })
+ } finally {
+ setIsLoadingVendorTypes(false)
+ }
+ }
+
+ loadVendorTypes()
+ }, [])
+
// React Hook Form
const form = useForm<CreateVendorSchema>({
resolver: zodResolver(createVendorSchema),
defaultValues: {
vendorName: "",
+ vendorTypeId: undefined,
+ items: "",
taxId: defaultTaxId,
address: "",
email: "",
@@ -126,7 +221,9 @@ export function JoinForm() {
mode: "onChange",
})
const isFormValid = form.formState.isValid
-
+ console.log("Form errors:", form.formState.errors);
+ console.log("Form values:", form.getValues());
+ console.log("Form valid:", form.formState.isValid);
// Field array for contacts
@@ -168,6 +265,8 @@ export function JoinForm() {
const vendorData = {
vendorName: values.vendorName,
+ vendorTypeId: values.vendorTypeId,
+ items: values.items,
vendorCode: values.vendorCode,
website: values.website,
taxId: values.taxId,
@@ -194,7 +293,7 @@ export function JoinForm() {
title: "등록 완료",
description: "회사 등록이 완료되었습니다. (status=PENDING_REVIEW)",
})
- router.push("/")
+ router.push("/ko/partners")
} else {
toast({
variant: "destructive",
@@ -214,6 +313,12 @@ export function JoinForm() {
}
}
+ // Get country code for phone number placeholder
+ const getPhonePlaceholder = (countryCode: string) => {
+ if (!countryCode || !countryDialCodes[countryCode]) return "전화번호";
+ return `${countryDialCodes[countryCode]} 전화번호`;
+ };
+
// Render
return (
<div className="container py-6">
@@ -244,79 +349,123 @@ export function JoinForm() {
<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 * */}
+ {/* Vendor Type - New Field */}
<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>
- )}
+ name="vendorTypeId"
+ render={({ field }) => {
+ const selectedType = vendorTypes.find(type => type.id === field.value);
+ const displayName = lng === "ko" ?
+ (selectedType?.nameKo || "") :
+ (selectedType?.nameEn || "");
+
+ return (
+ <FormItem>
+ <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+ 업체유형
+ </FormLabel>
+ <Popover>
+ <PopoverTrigger asChild>
+ <FormControl>
+ <Button
+ variant="outline"
+ role="combobox"
+ className={cn(
+ "w-full justify-between",
+ !field.value && "text-muted-foreground"
+ )}
+ disabled={isSubmitting || isLoadingVendorTypes}
+ >
+ {isLoadingVendorTypes
+ ? "Loading..."
+ : displayName || "업체유형 선택"}
+ <ChevronsUpDown className="ml-2 opacity-50" />
+ </Button>
+ </FormControl>
+ </PopoverTrigger>
+ <PopoverContent className="w-full p-0">
+ <Command>
+ <CommandInput placeholder="업체유형 검색..." />
+ <CommandList>
+ <CommandEmpty>No vendor type found.</CommandEmpty>
+ <CommandGroup>
+ {vendorTypes.map((type) => (
+ <CommandItem
+ key={type.id}
+ value={lng === "ko" ? type.nameKo : type.nameEn}
+ onSelect={() => field.onChange(type.id)}
+ >
+ <Check
+ className={cn(
+ "mr-2",
+ type.id === field.value
+ ? "opacity-100"
+ : "opacity-0"
+ )}
+ />
+ {lng === "ko" ? type.nameKo : type.nameEn}
+ </CommandItem>
+ ))}
+ </CommandGroup>
+ </CommandList>
+ </Command>
+ </PopoverContent>
+ </Popover>
+ <FormMessage />
+ </FormItem>
+ );
+ }}
/>
+ {/* vendorName */}
<FormField
control={form.control}
- name="phone"
+ name="vendorName"
render={({ field }) => (
<FormItem>
- <FormLabel>대표 전화</FormLabel>
+ <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+ 업체명
+ </FormLabel>
<FormControl>
<Input {...field} disabled={isSubmitting} />
</FormControl>
+ <FormDescription>
+ {form.watch("country") === "KR"
+ ? "사업자 등록증에 표기된 정확한 회사명을 입력하세요."
+ : "해외 업체의 경우 영문 회사명을 입력하세요."}
+ </FormDescription>
<FormMessage />
</FormItem>
)}
/>
- {/* email is required → show * */}
+ {/* Items - New Field */}
<FormField
control={form.control}
- name="email"
+ name="items"
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 */}
+ {/* Address */}
<FormField
control={form.control}
- name="website"
+ name="address"
render={({ field }) => (
<FormItem>
- <FormLabel>웹사이트</FormLabel>
+ <FormLabel>주소</FormLabel>
<FormControl>
<Input {...field} disabled={isSubmitting} />
</FormControl>
@@ -325,17 +474,18 @@ export function JoinForm() {
)}
/>
+ {/* Country - Updated with enhanced list */}
<FormField
control={form.control}
name="country"
render={({ field }) => {
- const selectedCountry = countryArray.find(
+ const selectedCountry = enhancedCountryArray.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>
@@ -351,18 +501,18 @@ export function JoinForm() {
>
{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..." />
+ <CommandInput placeholder="국가 검색..." />
<CommandList>
<CommandEmpty>No country found.</CommandEmpty>
<CommandGroup>
- {countryArray.map((country) => (
+ {enhancedCountryArray.map((country) => (
<CommandItem
key={country.code}
value={country.label}
@@ -391,6 +541,62 @@ export function JoinForm() {
)
}}
/>
+
+ {/* Phone - Updated with country code hint */}
+ <FormField
+ control={form.control}
+ name="phone"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+ 대표 전화
+ </FormLabel>
+ <FormControl>
+ <Input
+ {...field}
+ placeholder={getPhonePlaceholder(form.watch("country"))}
+ disabled={isSubmitting}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* Email - Updated with company domain guidance */}
+ <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>
+ 회사 도메인 이메일을 사용하세요. (naver.com, gmail.com, daum.net 등의 개인 이메일은 지양해주세요)
+ </FormDescription>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* Website */}
+ <FormField
+ control={form.control}
+ name="website"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>웹사이트</FormLabel>
+ <FormControl>
+ <Input {...field} disabled={isSubmitting} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
</div>
</div>
@@ -425,7 +631,7 @@ export function JoinForm() {
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 */}
+ {/* contactName - All required now */}
<FormField
control={form.control}
name={`contacts.${index}.contactName`}
@@ -442,13 +648,15 @@ export function JoinForm() {
)}
/>
- {/* contactPosition → optional */}
+ {/* contactPosition - Now required */}
<FormField
control={form.control}
name={`contacts.${index}.contactPosition`}
render={({ field }) => (
<FormItem>
- <FormLabel>직급 / 부서</FormLabel>
+ <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+ 직급 / 부서
+ </FormLabel>
<FormControl>
<Input {...field} disabled={isSubmitting} />
</FormControl>
@@ -457,7 +665,7 @@ export function JoinForm() {
)}
/>
- {/* contactEmail → required */}
+ {/* contactEmail */}
<FormField
control={form.control}
name={`contacts.${index}.contactEmail`}
@@ -474,15 +682,21 @@ export function JoinForm() {
)}
/>
- {/* contactPhone → optional */}
+ {/* contactPhone - Now required */}
<FormField
control={form.control}
name={`contacts.${index}.contactPhone`}
render={({ field }) => (
<FormItem>
- <FormLabel>전화번호</FormLabel>
+ <FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
+ 전화번호
+ </FormLabel>
<FormControl>
- <Input {...field} disabled={isSubmitting} />
+ <Input
+ {...field}
+ placeholder={getPhonePlaceholder(form.watch("country"))}
+ disabled={isSubmitting}
+ />
</FormControl>
<FormMessage />
</FormItem>
@@ -515,7 +729,7 @@ export function JoinForm() {
<div className="rounded-md border p-4 space-y-4">
<h4 className="text-md font-semibold">한국 사업자 정보</h4>
- {/* 대표자 등... all optional or whichever you want * for */}
+ {/* 대표자 등... all now required for Korean companies */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormField
control={form.control}
@@ -613,6 +827,9 @@ export function JoinForm() {
<FormLabel className="after:content-['*'] after:ml-0.5 after:text-red-500">
첨부 파일
</FormLabel>
+ <FormDescription>
+ 사업자등록증, ISO 9001 인증서, 회사 브로셔, 기본 소개자료 등을 첨부해주세요.
+ </FormDescription>
<Dropzone
maxSize={MAX_FILE_SIZE}
multiple