"use client"; /** * V2Input 설정 패널 * 통합 입력 컴포넌트의 세부 설정을 관리합니다. */ 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 { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; 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; allComponents?: any[]; } export const V2InputConfigPanel: React.FC = ({ config, onChange, menuObjid, allComponents = [] }) => { // 채번 규칙 목록 상태 const [numberingRules, setNumberingRules] = useState([]); const [loadingRules, setLoadingRules] = useState(false); // 부모 메뉴 목록 상태 (채번규칙 사용을 위한 선택) const [parentMenus, setParentMenus] = useState([]); const [loadingMenus, setLoadingMenus] = useState(false); // 선택된 메뉴 OBJID const [selectedMenuObjid, setSelectedMenuObjid] = useState(() => { return config.autoGeneration?.selectedMenuObjid || menuObjid; }); // 설정 업데이트 핸들러 const updateConfig = (field: string, value: any) => { onChange({ ...config, [field]: value }); }; // 부모 메뉴 목록 로드 (사용자 메뉴의 레벨 2만) 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; // 사용자 메뉴(menu_type='1')의 레벨 2만 필터링 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]); return (
{/* 입력 타입 */}
{/* 채번 타입 전용 설정 */} {config.inputType === "numbering" && (

채번 타입 안내

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

{/* 채번 필드는 기본적으로 읽기전용 */}
{ updateConfig("readonly", checked); }} />

채번 필드는 자동으로 생성되므로 읽기전용을 권장합니다

)} {/* 채번 타입이 아닌 경우에만 추가 설정 표시 */} {config.inputType !== "numbering" && ( <> {/* 형식 (텍스트/숫자용) */} {(config.inputType === "text" || !config.inputType) && (
)} {/* 플레이스홀더 */}
updateConfig("placeholder", e.target.value)} placeholder="입력 안내 텍스트" className="h-8 text-xs" />
{/* 숫자/슬라이더 전용 설정 */} {(config.inputType === "number" || config.inputType === "slider") && ( <>
updateConfig("min", e.target.value ? Number(e.target.value) : undefined)} placeholder="0" className="h-8 text-xs" />
updateConfig("max", e.target.value ? Number(e.target.value) : undefined)} placeholder="100" className="h-8 text-xs" />
updateConfig("step", e.target.value ? Number(e.target.value) : undefined)} placeholder="1" className="h-8 text-xs" />
)} {/* 여러 줄 텍스트 전용 설정 */} {config.inputType === "textarea" && (
updateConfig("rows", parseInt(e.target.value) || 3)} min={2} max={20} className="h-8 text-xs" />
)} {/* 마스크 입력 (선택) */}
updateConfig("mask", e.target.value)} placeholder="예: ###-####-####" className="h-8 text-xs" />

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

{/* 자동생성 기능 */}
{ 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 ? (
) : (
먼저 대상 메뉴를 선택하세요
)} )} {/* 자동생성 옵션 (랜덤/순차용) */} {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-8 text-xs" />
)} {/* 접두사 */}
{ updateConfig("autoGeneration", { ...config.autoGeneration, options: { ...config.autoGeneration?.options, prefix: e.target.value, }, }); }} placeholder="예: INV-" className="h-8 text-xs" />
{/* 접미사 */}
{ updateConfig("autoGeneration", { ...config.autoGeneration, options: { ...config.autoGeneration?.options, suffix: e.target.value, }, }); }} className="h-8 text-xs" />
{/* 미리보기 */}
{AutoGenerationUtils.generatePreviewValue(config.autoGeneration)}
)}
)}
)} {/* 데이터 바인딩 설정 */}
); }; V2InputConfigPanel.displayName = "V2InputConfigPanel"; /** * 데이터 바인딩 설정 섹션 * 같은 화면의 v2-table-list 컴포넌트를 자동 감지하여 드롭다운으로 표시 */ function DataBindingSection({ config, onChange, allComponents, }: { config: Record; onChange: (config: Record) => void; allComponents: any[]; }) { const [tableColumns, setTableColumns] = useState([]); const [loadingColumns, setLoadingColumns] = useState(false); // 같은 화면의 v2-table-list 컴포넌트만 필터링 const tableListComponents = React.useMemo(() => { return allComponents.filter((comp) => { const type = comp.componentType || comp.widgetType || comp.componentConfig?.type || (comp.url && comp.url.split("/").pop()); return type === "v2-table-list"; }); }, [allComponents]); // 선택된 테이블 컴포넌트의 테이블명 추출 const selectedTableComponent = React.useMemo(() => { if (!config.dataBinding?.sourceComponentId) return null; return tableListComponents.find((comp) => comp.id === config.dataBinding.sourceComponentId); }, [tableListComponents, config.dataBinding?.sourceComponentId]); const selectedTableName = React.useMemo(() => { if (!selectedTableComponent) return null; return ( selectedTableComponent.componentConfig?.selectedTable || selectedTableComponent.selectedTable || null ); }, [selectedTableComponent]); // 선택된 테이블의 컬럼 목록 로드 useEffect(() => { if (!selectedTableName) { setTableColumns([]); return; } const loadColumns = async () => { setLoadingColumns(true); try { const { tableTypeApi } = await import("@/lib/api/screen"); const response = await tableTypeApi.getTableTypeColumns(selectedTableName); if (response.success && response.data) { const cols = response.data.map((col: any) => col.column_name).filter(Boolean); setTableColumns(cols); } } catch { // 컬럼 정보를 못 가져오면 테이블 컴포넌트의 columns에서 추출 const configColumns = selectedTableComponent?.componentConfig?.columns; if (Array.isArray(configColumns)) { setTableColumns(configColumns.map((c: any) => c.columnName).filter(Boolean)); } } finally { setLoadingColumns(false); } }; loadColumns(); }, [selectedTableName, selectedTableComponent]); const updateConfig = (field: string, value: any) => { onChange({ ...config, [field]: value }); }; return (
{ if (checked) { const firstTable = tableListComponents[0]; updateConfig("dataBinding", { sourceComponentId: firstTable?.id || "", sourceColumn: "", }); } else { updateConfig("dataBinding", undefined); } }} />
{config.dataBinding && (

테이블에서 행 선택 시 해당 컬럼 값이 자동으로 채워집니다

{/* 소스 테이블 컴포넌트 선택 */}
{tableListComponents.length === 0 ? (

이 화면에 v2-table-list 컴포넌트가 없습니다

) : ( )}
{/* 소스 컬럼 선택 */} {config.dataBinding?.sourceComponentId && (
{loadingColumns ? (

컬럼 로딩 중...

) : tableColumns.length === 0 ? ( <> { updateConfig("dataBinding", { ...config.dataBinding, sourceColumn: e.target.value, }); }} placeholder="컬럼명 직접 입력" className="h-7 text-xs" />

컬럼 정보를 불러올 수 없어 직접 입력

) : ( )}
)}
)}
); } export default V2InputConfigPanel;