"use client"; /** * V2Input 설정 패널 * 토스식 단계별 UX: 기본 설정 -> 타입별 설정 -> 고급 설정(접힘) */ import React, { useState, useEffect } from "react"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Settings, ChevronDown, Loader2, Type, Hash, Lock, AlignLeft, SlidersHorizontal, Palette, ListOrdered } from "lucide-react"; import { cn } from "@/lib/utils"; import { AutoGenerationType, AutoGenerationConfig } from "@/types/screen"; import { AutoGenerationUtils } from "@/lib/utils/autoGeneration"; import { getAvailableNumberingRules } from "@/lib/api/numberingRule"; import { NumberingRuleConfig } from "@/types/numbering-rule"; interface V2InputConfigPanelProps { config: Record; onChange: (config: Record) => void; menuObjid?: number; } export const V2InputConfigPanel: React.FC = ({ config, onChange, menuObjid }) => { const [numberingRules, setNumberingRules] = useState([]); const [loadingRules, setLoadingRules] = useState(false); const [parentMenus, setParentMenus] = useState([]); const [loadingMenus, setLoadingMenus] = useState(false); const [selectedMenuObjid, setSelectedMenuObjid] = useState(() => { return config.autoGeneration?.selectedMenuObjid || menuObjid; }); const [advancedOpen, setAdvancedOpen] = useState(false); const updateConfig = (field: string, value: any) => { onChange({ ...config, [field]: value }); }; useEffect(() => { const loadMenus = async () => { setLoadingMenus(true); try { const { apiClient } = await import("@/lib/api/client"); const response = await apiClient.get("/admin/menus"); if (response.data.success && response.data.data) { const allMenus = response.data.data; const level2UserMenus = allMenus.filter((menu: any) => menu.menu_type === '1' && menu.lev === 2 ); setParentMenus(level2UserMenus); } } catch (error) { console.error("부모 메뉴 로드 실패:", error); } finally { setLoadingMenus(false); } }; loadMenus(); }, []); useEffect(() => { const loadRules = async () => { if (config.autoGeneration?.type !== "numbering_rule") return; if (!selectedMenuObjid) { setNumberingRules([]); return; } setLoadingRules(true); try { const response = await getAvailableNumberingRules(selectedMenuObjid); if (response.success && response.data) { setNumberingRules(response.data); } } catch (error) { console.error("채번 규칙 목록 로드 실패:", error); setNumberingRules([]); } finally { setLoadingRules(false); } }; loadRules(); }, [selectedMenuObjid, config.autoGeneration?.type]); const inputType = config.inputType || config.type || "text"; return (
{/* ─── 1단계: 입력 타입 선택 (카드 방식) ─── */}

입력 타입

입력 필드의 종류를 선택해요

{[ { value: "text", icon: Type, label: "텍스트", desc: "일반 텍스트 입력" }, { value: "number", icon: Hash, label: "숫자", desc: "숫자만 입력" }, { value: "password", icon: Lock, label: "비밀번호", desc: "마스킹 처리" }, { value: "textarea", icon: AlignLeft, label: "여러 줄", desc: "긴 텍스트 입력" }, { value: "slider", icon: SlidersHorizontal, label: "슬라이더", desc: "범위 선택" }, { value: "color", icon: Palette, label: "색상", desc: "색상 선택기" }, { value: "numbering", icon: ListOrdered, label: "채번", desc: "자동 번호 생성" }, ].map((item) => ( ))}
{/* ─── 채번 타입 전용 안내 ─── */} {inputType === "numbering" && (

채번 규칙은 테이블 관리에서 컬럼별로 설정돼요. 화면에 배치된 컬럼의 채번 규칙이 자동으로 적용돼요.

읽기전용

채번 필드는 자동 생성되므로 읽기전용을 권장해요

updateConfig("readonly", checked)} />
)} {/* ─── 채번 타입이 아닌 경우: 기본 설정 ─── */} {inputType !== "numbering" && ( <> {/* 기본 설정 영역 */}
{/* 안내 텍스트 (placeholder) */}
안내 텍스트 updateConfig("placeholder", e.target.value)} placeholder="입력 안내" className="h-7 w-[160px] text-xs" />
{/* 입력 형식 - 텍스트 타입 전용 */} {(inputType === "text" || !config.inputType) && (
입력 형식
)} {/* 입력 마스크 */}
입력 마스크

# = 숫자, A = 문자, * = 모두

updateConfig("mask", e.target.value)} placeholder="###-####-####" className="h-7 w-[160px] text-xs" />
{/* 숫자/슬라이더: 범위 설정 */} {(inputType === "number" || inputType === "slider") && (

값 범위

updateConfig("min", e.target.value ? Number(e.target.value) : undefined)} placeholder="0" className="h-7 text-xs" />
updateConfig("max", e.target.value ? Number(e.target.value) : undefined)} placeholder="100" className="h-7 text-xs" />
updateConfig("step", e.target.value ? Number(e.target.value) : undefined)} placeholder="1" className="h-7 text-xs" />
)} {/* 여러 줄 텍스트: 줄 수 */} {inputType === "textarea" && (
줄 수 updateConfig("rows", parseInt(e.target.value) || 3)} min={2} max={20} className="h-7 w-[160px] text-xs" />
)}
{/* ─── 고급 설정: 자동 생성 (Collapsible) ─── */}
{/* 자동 생성 토글 */}

자동 생성

값이 자동으로 채워져요

{ const currentConfig = config.autoGeneration || { type: "none", enabled: false }; updateConfig("autoGeneration", { ...currentConfig, enabled: checked as boolean, }); }} />
{config.autoGeneration?.enabled && (
{/* 자동 생성 타입 */}

생성 방식

{config.autoGeneration?.type && config.autoGeneration.type !== "none" && (

{AutoGenerationUtils.getTypeDescription(config.autoGeneration.type)}

)} {/* 채번 규칙 선택 */} {config.autoGeneration?.type === "numbering_rule" && (

대상 메뉴 *

{selectedMenuObjid ? (

채번 규칙 *

{loadingRules ? (
규칙 로딩 중...
) : ( )}
) : (
먼저 대상 메뉴를 선택하세요
)}
)} {/* 랜덤/순차 옵션 */} {config.autoGeneration?.type && ["random_string", "random_number", "sequence"].includes(config.autoGeneration.type) && (
{["random_string", "random_number"].includes(config.autoGeneration.type) && (
길이 { updateConfig("autoGeneration", { ...config.autoGeneration, options: { ...config.autoGeneration?.options, length: parseInt(e.target.value) || 8, }, }); }} className="h-7 w-[120px] text-xs" />
)}
접두사 { updateConfig("autoGeneration", { ...config.autoGeneration, options: { ...config.autoGeneration?.options, prefix: e.target.value, }, }); }} placeholder="예: INV-" className="h-7 w-[120px] text-xs" />
접미사 { updateConfig("autoGeneration", { ...config.autoGeneration, options: { ...config.autoGeneration?.options, suffix: e.target.value, }, }); }} className="h-7 w-[120px] text-xs" />
미리보기
{AutoGenerationUtils.generatePreviewValue(config.autoGeneration)}
)}
)}
)}
); }; V2InputConfigPanel.displayName = "V2InputConfigPanel"; export default V2InputConfigPanel;