diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
| commit | ef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch) | |
| tree | 345251a3ed0f4429716fa5edaa31024d8f4cb560 /components/signup/join-form.tsx | |
| parent | 9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff) | |
~20250428 작업사항
Diffstat (limited to 'components/signup/join-form.tsx')
| -rw-r--r-- | components/signup/join-form.tsx | 323 |
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 |
