"use client"; import { useState } from "react"; import { cn } from "@/lib/utils"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { ChevronLeft, ChevronRight, Plus, Trash2 } from "lucide-react"; import type { PopSearchConfig, SearchInputType, DatePresetOption, } from "./types"; import { SEARCH_INPUT_TYPE_LABELS, DATE_PRESET_LABELS } from "./types"; // ======================================== // 기본값 // ======================================== const DEFAULT_CONFIG: PopSearchConfig = { inputType: "text", fieldName: "", placeholder: "검색어 입력", debounceMs: 500, triggerOnEnter: true, labelPosition: "top", labelText: "", labelVisible: true, }; // ======================================== // 설정 패널 메인 // ======================================== interface ConfigPanelProps { config: PopSearchConfig | undefined; onUpdate: (config: PopSearchConfig) => void; } export function PopSearchConfigPanel({ config, onUpdate }: ConfigPanelProps) { const [step, setStep] = useState(0); const cfg = { ...DEFAULT_CONFIG, ...(config || {}) }; const update = (partial: Partial) => { onUpdate({ ...cfg, ...partial }); }; const STEPS = ["기본 설정", "상세 설정"]; return (
{/* Stepper 헤더 */}
{STEPS.map((s, i) => ( ))}
{/* STEP 1: 기본 설정 */} {step === 0 && ( )} {/* STEP 2: 타입별 상세 설정 */} {step === 1 && ( )} {/* 이전/다음 버튼 */}
); } // ======================================== // STEP 1: 기본 설정 // ======================================== interface StepProps { cfg: PopSearchConfig; update: (partial: Partial) => void; } function StepBasicSettings({ cfg, update }: StepProps) { return (
{/* 입력 타입 */}
{/* 필드명 */}
update({ fieldName: e.target.value })} placeholder="예: supplier_code" className="h-8 text-xs" />

filter_changed 이벤트에서 이 이름으로 값이 전달됩니다

{/* 플레이스홀더 */}
update({ placeholder: e.target.value })} placeholder="입력 힌트 텍스트" className="h-8 text-xs" />
{/* 라벨 표시 */}
update({ labelVisible: Boolean(checked) }) } />
{/* 라벨 텍스트 + 위치 (라벨 표시 ON일 때만) */} {cfg.labelVisible !== false && ( <>
update({ labelText: e.target.value })} placeholder="예: 거래처명" className="h-8 text-xs" />
)}
); } // ======================================== // STEP 2: 타입별 상세 설정 // ======================================== function StepDetailSettings({ cfg, update }: StepProps) { switch (cfg.inputType) { case "text": case "number": return ; case "select": return ; case "date-preset": return ; case "modal-table": case "modal-card": case "modal-icon-grid": return ; case "toggle": return (

토글은 추가 설정이 없습니다. ON/OFF 값이 바로 전달됩니다.

); default: return (

{cfg.inputType} 타입의 상세 설정은 후속 구현 예정입니다.

); } } // ======================================== // text/number 상세 설정 // ======================================== function TextDetailSettings({ cfg, update }: StepProps) { return (
{/* 디바운스 */}
update({ debounceMs: Math.max(0, Number(e.target.value)) }) } min={0} max={5000} step={100} className="h-8 text-xs" />

입력 후 대기 시간. 0이면 즉시 발행 (권장: 300~500)

{/* Enter 발행 */}
update({ triggerOnEnter: Boolean(checked) }) } />
); } // ======================================== // select 상세 설정: 정적 옵션 편집 // ======================================== function SelectDetailSettings({ cfg, update }: StepProps) { const options = cfg.options || []; const addOption = () => { update({ options: [ ...options, { value: `opt_${options.length + 1}`, label: `옵션 ${options.length + 1}` }, ], }); }; const removeOption = (index: number) => { update({ options: options.filter((_, i) => i !== index) }); }; const updateOption = (index: number, field: "value" | "label", val: string) => { const next = options.map((opt, i) => i === index ? { ...opt, [field]: val } : opt ); update({ options: next }); }; return (
{options.length === 0 && (

옵션이 없습니다. 아래 버튼으로 추가하세요.

)} {options.map((opt, i) => (
updateOption(i, "value", e.target.value)} placeholder="값" className="h-7 flex-1 text-[10px]" /> updateOption(i, "label", e.target.value)} placeholder="라벨" className="h-7 flex-1 text-[10px]" />
))}
); } // ======================================== // date-preset 상세 설정: 프리셋 선택 // ======================================== function DatePresetDetailSettings({ cfg, update }: StepProps) { const ALL_PRESETS: DatePresetOption[] = [ "today", "this-week", "this-month", "custom", ]; const activePresets = cfg.datePresets || ["today", "this-week", "this-month"]; const togglePreset = (preset: DatePresetOption) => { const next = activePresets.includes(preset) ? activePresets.filter((p) => p !== preset) : [...activePresets, preset]; update({ datePresets: next.length > 0 ? next : ["today"] }); }; return (
{ALL_PRESETS.map((preset) => (
togglePreset(preset)} />
))} {activePresets.includes("custom") && (

"직접" 선택 시 날짜 입력 UI가 표시됩니다 (후속 구현)

)}
); } // ======================================== // modal-* 상세 설정 // ======================================== function ModalDetailSettings({ cfg, update }: StepProps) { const mc = cfg.modalConfig || { modalCanvasId: "", displayField: "", valueField: "" }; const updateModal = (partial: Partial) => { update({ modalConfig: { ...mc, ...partial } }); }; return (

모달 캔버스 연동은 모달 시스템 구현 후 활성화됩니다. 현재는 설정값만 저장됩니다.

{/* 모달 캔버스 ID */}
updateModal({ modalCanvasId: e.target.value })} placeholder="예: modal-supplier" className="h-8 text-xs" />
{/* 표시 필드 */}
updateModal({ displayField: e.target.value })} placeholder="예: supplier_name" className="h-8 text-xs" />

선택 후 입력란에 표시할 필드명

{/* 값 필드 */}
updateModal({ valueField: e.target.value })} placeholder="예: supplier_code" className="h-8 text-xs" />

필터 값으로 사용할 필드명

); }