import { useForm, Controller } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { X, Layers } from 'lucide-react'; import { showToast } from '@/lib/notify'; import { Typography } from '@/components/ui/typography'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { type NegotiationCard, type CardTab, generateCardCode } from '../types'; import type { CardInput } from '../hooks/useCards'; const schema = z.object({ isWildcard: z.boolean(), code: z.string().trim().min(1, '카드번호는 필수 기입 사항입니다.'), title: z.string().trim().min(1, '카드이름은 필수 기입 사항입니다.'), scriptPreview: z.string().trim().min(1, '스크립트를 작성해 주십시오.'), status: z.enum(['ACTIVE', 'INACTIVE']), triggerCondition: z.string(), memo: z.string(), }); type FormValues = z.infer; type CardFormModalProps = { open: boolean; mode: 'create' | 'edit'; card: NegotiationCard | null; // edit 모드 초기값 출처 activeTab: CardTab; // create 시 기본 카드 종류 결정 onCreate: (input: CardInput) => Promise; onUpdate: (id: string, input: CardInput) => Promise; onClose: () => void; }; function buildDefaults( mode: 'create' | 'edit', card: NegotiationCard | null, activeTab: CardTab, ): FormValues { if (mode === 'edit' && card) { return { isWildcard: card.isWildcard, code: card.code, title: card.title, scriptPreview: card.scriptPreview, status: card.status, triggerCondition: card.triggerCondition || '', memo: card.memo || '', }; } const wild = activeTab === 'WILD'; return { isWildcard: wild, code: generateCardCode(wild), title: '', scriptPreview: '', status: 'ACTIVE', triggerCondition: '', memo: '', }; } // 협상카드/와일드카드 등록·수정 모달. react-hook-form + zod로 검증. // 페이지는 key={mode + card?.id}로 리마운트시켜 초기값을 주입한다. export function CardFormModal({ open, mode, card, activeTab, onCreate, onUpdate, onClose, }: CardFormModalProps) { const { register, control, handleSubmit, watch, setValue, formState: { errors }, } = useForm({ resolver: zodResolver(schema), defaultValues: buildDefaults(mode, card, activeTab), }); const isWildcard = watch('isWildcard'); if (!open) return null; const onValid = async (v: FormValues) => { const input: CardInput = { title: v.title, code: v.code, scriptPreview: v.scriptPreview, status: v.status, isWildcard: v.isWildcard, triggerCondition: v.triggerCondition, memo: v.memo, }; const kind = v.isWildcard ? '와일드카드' : '협상카드'; try { if (mode === 'create') { await onCreate(input); showToast(`${kind}가 추가되었습니다.`, 'success'); } else if (card) { await onUpdate(card.id, input); showToast('정보가 수정되었습니다.', 'success'); } onClose(); } catch (err) { showToast(err instanceof Error ? err.message : `${kind} 저장 실패`, 'error'); } }; return (
{mode === 'create' ? isWildcard ? '신규 와일드카드 등록' : '신규 협상카드 등록' : isWildcard ? '와일드카드 정보 수정' : '협상카드 정보 수정'}
{/* Card Type Selection */}
카드 종류 ( )} />
{/* Code */}
카드번호 {errors.code &&

{errors.code.message}

}
{/* Status */}
협상 적용 여부 {isWildcard ? ( (
{field.value === 'ACTIVE' ? '적용 승인' : '적용 대기'}
)} /> ) : (
상시 항상 적용
)}
{/* Title */}
카드이름 {errors.title &&

{errors.title.message}

}
{/* Script */}
스크립트
치환 변수 사용: {'{target_price}'}, {'{partner_name}'}
에디터