"use client"; /** * V2 타임라인 스케줄러 설정 패널 * 토스식 단계별 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 { Button } from "@/components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Settings, ChevronDown, Check, ChevronsUpDown, Database, Users } from "lucide-react"; import { cn } from "@/lib/utils"; import { tableTypeApi } from "@/lib/api/screen"; import type { TimelineSchedulerConfig, ScheduleType, SourceDataConfig, ResourceFieldMapping } from "@/lib/registry/components/v2-timeline-scheduler/types"; import { zoomLevelOptions, scheduleTypeOptions } from "@/lib/registry/components/v2-timeline-scheduler/config"; interface V2TimelineSchedulerConfigPanelProps { config: TimelineSchedulerConfig; onChange: (config: Partial) => void; } interface TableInfo { tableName: string; displayName: string; } interface ColumnInfo { columnName: string; displayName: string; } export const V2TimelineSchedulerConfigPanel: React.FC = ({ config, onChange, }) => { const [tables, setTables] = useState([]); const [sourceColumns, setSourceColumns] = useState([]); const [resourceColumns, setResourceColumns] = useState([]); const [loading, setLoading] = useState(false); const [sourceTableOpen, setSourceTableOpen] = useState(false); const [resourceTableOpen, setResourceTableOpen] = useState(false); const [displayOpen, setDisplayOpen] = useState(false); useEffect(() => { const loadTables = async () => { setLoading(true); try { const tableList = await tableTypeApi.getTables(); if (Array.isArray(tableList)) { setTables( tableList.map((t: any) => ({ tableName: t.table_name || t.tableName, displayName: t.display_name || t.displayName || t.table_name || t.tableName, })) ); } } catch (err) { console.error("테이블 목록 로드 오류:", err); } finally { setLoading(false); } }; loadTables(); }, []); useEffect(() => { const loadSourceColumns = async () => { if (!config.sourceConfig?.tableName) { setSourceColumns([]); return; } try { const columns = await tableTypeApi.getColumns(config.sourceConfig.tableName); if (Array.isArray(columns)) { setSourceColumns( columns.map((col: any) => ({ columnName: col.column_name || col.columnName, displayName: col.display_name || col.displayName || col.column_name || col.columnName, })) ); } } catch (err) { console.error("소스 컬럼 로드 오류:", err); setSourceColumns([]); } }; loadSourceColumns(); }, [config.sourceConfig?.tableName]); useEffect(() => { const loadResourceColumns = async () => { if (!config.resourceTable) { setResourceColumns([]); return; } try { const columns = await tableTypeApi.getColumns(config.resourceTable); if (Array.isArray(columns)) { setResourceColumns( columns.map((col: any) => ({ columnName: col.column_name || col.columnName, displayName: col.display_name || col.displayName || col.column_name || col.columnName, })) ); } } catch (err) { console.error("리소스 컬럼 로드 오류:", err); setResourceColumns([]); } }; loadResourceColumns(); }, [config.resourceTable]); const updateConfig = (updates: Partial) => { onChange({ ...config, ...updates }); }; const updateSourceConfig = (updates: Partial) => { updateConfig({ sourceConfig: { ...config.sourceConfig, ...updates, }, }); }; const updateResourceFieldMapping = (field: string, value: string) => { updateConfig({ resourceFieldMapping: { ...config.resourceFieldMapping, id: config.resourceFieldMapping?.id || "id", name: config.resourceFieldMapping?.name || "name", [field]: value, }, }); }; return (
{/* ─── 1단계: 스케줄 생성 설정 ─── */}

스케줄 생성 설정

스케줄 자동 생성 시 참조할 원본 데이터를 설정해요 (저장: schedule_mng)

{/* 스케줄 타입 */}
스케줄 타입
{/* 소스 테이블 Combobox */}
소스 테이블 (수주/작업요청 등) { return value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0; }} > 테이블을 찾을 수 없습니다. {tables.map((table) => ( { updateSourceConfig({ tableName: table.tableName }); setSourceTableOpen(false); }} className="text-xs" >
{table.displayName} {table.tableName}
))}
{/* 소스 필드 매핑 (테이블 선택 시) */} {config.sourceConfig?.tableName && (

필드 매핑

기준일 (마감일/납기일) *

스케줄 종료일로 사용돼요

수량 필드
그룹 필드 (품목코드)
그룹명 필드 (품목명)
)}
{/* ─── 2단계: 리소스 설정 ─── */}

리소스 설정 (설비/작업자)

타임라인 Y축에 표시할 리소스를 설정해요

{/* 리소스 테이블 Combobox */}
리소스 테이블 { return value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0; }} > 테이블을 찾을 수 없습니다. {tables.map((table) => ( { updateConfig({ resourceTable: table.tableName }); setResourceTableOpen(false); }} className="text-xs" >
{table.displayName} {table.tableName}
))}
{/* 리소스 필드 매핑 */} {config.resourceTable && (

리소스 필드

ID 필드
이름 필드
)}
{/* ─── 3단계: 표시 설정 (Collapsible) ─── */}
{/* 줌 레벨 */}
기본 줌 레벨
{/* 높이 */}
높이 (px) updateConfig({ height: parseInt(e.target.value) || 500 })} className="h-7 w-[100px] text-xs" />
{/* 행 높이 */}
행 높이 (px) updateConfig({ rowHeight: parseInt(e.target.value) || 50 })} className="h-7 w-[100px] text-xs" />
{/* Switch 토글들 */}

편집 가능

스케줄을 직접 수정할 수 있어요

updateConfig({ editable: v })} />

드래그 이동

스케줄을 드래그해서 날짜를 변경해요

updateConfig({ draggable: v })} />

리사이즈

스케줄의 기간을 늘이거나 줄여요

updateConfig({ resizable: v })} />

오늘 표시선

오늘 날짜에 빨간 세로선을 표시해요

updateConfig({ showTodayLine: v })} />

진행률 표시

각 스케줄의 진행률 바를 보여줘요

updateConfig({ showProgress: v })} />

툴바 표시

상단의 네비게이션/줌 컨트롤을 보여줘요

updateConfig({ showToolbar: v })} />
); }; V2TimelineSchedulerConfigPanel.displayName = "V2TimelineSchedulerConfigPanel"; export default V2TimelineSchedulerConfigPanel;