/** * FormField - 통합 폼 필드 컴포넌트 */ import { ReactNode } from "react"; import { Label } from "../ui/label"; import { Input } from "../ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { Textarea } from "../ui/textarea"; import { AlertCircle } from "lucide-react"; import { PhoneInput } from "../ui/phone-input"; import { BusinessNumberInput } from "../ui/business-number-input"; import { PersonalNumberInput } from "../ui/personal-number-input"; import { NumberInput } from "../ui/number-input"; import { CurrencyInput } from "../ui/currency-input"; import { QuantityInput } from "../ui/quantity-input"; export type FormFieldType = | 'text' | 'number' | 'date' | 'select' | 'textarea' | 'custom' | 'password' // 새 입력 타입 | 'phone' // 전화번호 (자동 하이픈) | 'businessNumber' // 사업자번호 (000-00-00000) | 'personalNumber' // 주민번호 (000000-0000000) | 'currency' // 금액 (천단위 콤마, ₩) | 'quantity'; // 수량 (정수, 최소 0) export interface SelectOption { value: string; label: string; disabled?: boolean; } export interface FormFieldProps { label: string; required?: boolean; type?: FormFieldType; value?: string | number; onChange?: (value: string) => void; /** number 타입 전용 onChange (currency, quantity 타입에서 사용) */ onChangeNumber?: (value: number | undefined) => void; placeholder?: string; disabled?: boolean; error?: string; helpText?: string; options?: SelectOption[]; selectPlaceholder?: string; children?: ReactNode; className?: string; inputClassName?: string; rows?: number; min?: number; max?: number; step?: number; htmlFor?: string; // 새 입력 타입 전용 옵션 /** 사업자번호 유효성 검사 표시 */ showValidation?: boolean; /** 주민번호 뒷자리 마스킹 */ maskBack?: boolean; /** 소수점 허용 (NumberInput) */ allowDecimal?: boolean; /** 소수점 자릿수 (NumberInput) */ decimalPlaces?: number; /** 천단위 콤마 (NumberInput) */ useComma?: boolean; /** 접미사 (원, 개, % 등) */ suffix?: string; /** 수량 +/- 버튼 표시 */ showButtons?: boolean; } export function FormField({ label, required = false, type = 'text', value, onChange, onChangeNumber, placeholder, disabled = false, error, helpText, options = [], selectPlaceholder = "선택하세요", children, className = "", inputClassName = "", rows = 3, min, max, step, htmlFor, // 새 입력 타입 전용 옵션 showValidation, maskBack, allowDecimal, decimalPlaces, useComma, suffix, showButtons, }: FormFieldProps) { const renderInput = () => { switch (type) { case 'select': return ( {options.map((option) => ( {option.label} ))} ); case 'textarea': return ( onChange?.(e.target.value)} placeholder={placeholder} disabled={disabled} rows={rows} className={`${error ? 'border-red-500' : ''} ${inputClassName}`} /> ); case 'custom': return children; case 'number': return ( onChange?.(e.target.value)} placeholder={placeholder} disabled={disabled} min={min} max={max} step={step} className={`${error ? 'border-red-500' : ''} ${inputClassName}`} /> ); case 'date': return ( onChange?.(e.target.value)} disabled={disabled} className={`${error ? 'border-red-500' : ''} ${inputClassName}`} /> ); case 'password': return ( onChange?.(e.target.value)} placeholder={placeholder} disabled={disabled} className={`${error ? 'border-red-500' : ''} ${inputClassName}`} /> ); case 'phone': return ( onChange?.(v)} placeholder={placeholder} disabled={disabled} error={!!error} className={inputClassName} /> ); case 'businessNumber': return ( onChange?.(v)} placeholder={placeholder} disabled={disabled} error={!!error} showValidation={showValidation} className={inputClassName} /> ); case 'personalNumber': return ( onChange?.(v)} placeholder={placeholder} disabled={disabled} error={!!error} maskBack={maskBack} className={inputClassName} /> ); case 'currency': return ( onChangeNumber?.(v)} placeholder={placeholder} disabled={disabled} error={!!error} className={inputClassName} /> ); case 'quantity': return ( onChangeNumber?.(v)} placeholder={placeholder} disabled={disabled} error={!!error} min={min} max={max} step={step} showButtons={showButtons} suffix={suffix} className={inputClassName} /> ); case 'text': default: return ( onChange?.(e.target.value)} placeholder={placeholder} disabled={disabled} className={`${error ? 'border-red-500' : ''} ${inputClassName}`} /> ); } }; return ( {label} {required && *} {renderInput()} {error && ( {error} )} {helpText && !error && ( {helpText} )} ); }
{helpText}