'use client'; import { useState, useEffect, useRef } from 'react'; import { toast } from 'sonner'; import { useItemMaster } from '@/contexts/ItemMasterContext'; import { FolderTree, ListTree, FileText, Settings, Layers, Database, Plus, Folder } from 'lucide-react'; export interface CustomTab { id: string; label: string; icon: string; isDefault: boolean; order: number; } export interface AttributeSubTab { id: string; label: string; key: string; isDefault: boolean; order: number; } export interface UseTabManagementReturn { // 메인 탭 상태 customTabs: CustomTab[]; setCustomTabs: React.Dispatch>; activeTab: string; setActiveTab: (tab: string) => void; // 속성 하위 탭 상태 attributeSubTabs: AttributeSubTab[]; setAttributeSubTabs: React.Dispatch>; activeAttributeTab: string; setActiveAttributeTab: (tab: string) => void; // 메인 탭 다이얼로그 상태 isAddTabDialogOpen: boolean; setIsAddTabDialogOpen: (open: boolean) => void; isManageTabsDialogOpen: boolean; setIsManageTabsDialogOpen: (open: boolean) => void; newTabLabel: string; setNewTabLabel: (label: string) => void; editingTabId: string | null; setEditingTabId: (id: string | null) => void; deletingTabId: string | null; setDeletingTabId: (id: string | null) => void; isDeleteTabDialogOpen: boolean; setIsDeleteTabDialogOpen: (open: boolean) => void; // 속성 하위 탭 다이얼로그 상태 isManageAttributeTabsDialogOpen: boolean; setIsManageAttributeTabsDialogOpen: (open: boolean) => void; isAddAttributeTabDialogOpen: boolean; setIsAddAttributeTabDialogOpen: (open: boolean) => void; newAttributeTabLabel: string; setNewAttributeTabLabel: (label: string) => void; editingAttributeTabId: string | null; setEditingAttributeTabId: (id: string | null) => void; deletingAttributeTabId: string | null; setDeletingAttributeTabId: (id: string | null) => void; isDeleteAttributeTabDialogOpen: boolean; setIsDeleteAttributeTabDialogOpen: (open: boolean) => void; // 핸들러 handleAddTab: () => void; handleUpdateTab: () => void; handleDeleteTab: (tabId: string) => void; confirmDeleteTab: () => void; handleAddAttributeTab: () => void; handleUpdateAttributeTab: () => void; handleDeleteAttributeTab: (tabId: string) => void; confirmDeleteAttributeTab: () => void; moveTabUp: (tabId: string) => void; moveTabDown: (tabId: string) => void; moveAttributeTabUp: (tabId: string) => void; moveAttributeTabDown: (tabId: string) => void; getTabIcon: (iconName: string) => any; handleEditTabFromManage: (tab: CustomTab) => void; } export function useTabManagement(): UseTabManagementReturn { const { itemMasterFields } = useItemMaster(); // 메인 탭 상태 const [customTabs, setCustomTabs] = useState([ { id: 'hierarchy', label: '계층구조', icon: 'FolderTree', isDefault: true, order: 1 }, { id: 'sections', label: '섹션', icon: 'Layers', isDefault: true, order: 2 }, { id: 'items', label: '항목', icon: 'ListTree', isDefault: true, order: 3 }, { id: 'attributes', label: '속성', icon: 'Settings', isDefault: true, order: 4 } ]); const [activeTab, setActiveTab] = useState('hierarchy'); // 속성 하위 탭 상태 (기본 탭: 단위, 재질, 표면처리) // TODO: 나중에 백엔드에서 기준값 로드로 대체 예정 const [attributeSubTabs, setAttributeSubTabs] = useState([ { id: 'units', label: '단위', key: 'units', isDefault: true, order: 0 }, { id: 'materials', label: '재질', key: 'materials', isDefault: true, order: 1 }, { id: 'surface', label: '표면처리', key: 'surface', isDefault: true, order: 2 }, ]); const [activeAttributeTab, setActiveAttributeTab] = useState('units'); // 메인 탭 다이얼로그 상태 const [isAddTabDialogOpen, setIsAddTabDialogOpen] = useState(false); const [isManageTabsDialogOpen, setIsManageTabsDialogOpen] = useState(false); const [newTabLabel, setNewTabLabel] = useState(''); const [editingTabId, setEditingTabId] = useState(null); const [deletingTabId, setDeletingTabId] = useState(null); const [isDeleteTabDialogOpen, setIsDeleteTabDialogOpen] = useState(false); // 속성 하위 탭 다이얼로그 상태 const [isManageAttributeTabsDialogOpen, setIsManageAttributeTabsDialogOpen] = useState(false); const [isAddAttributeTabDialogOpen, setIsAddAttributeTabDialogOpen] = useState(false); const [newAttributeTabLabel, setNewAttributeTabLabel] = useState(''); const [editingAttributeTabId, setEditingAttributeTabId] = useState(null); const [deletingAttributeTabId, setDeletingAttributeTabId] = useState(null); const [isDeleteAttributeTabDialogOpen, setIsDeleteAttributeTabDialogOpen] = useState(false); // 이전 필드 상태 추적용 ref (무한 루프 방지) const prevFieldsRef = useRef(''); // 마스터 항목이 추가/수정/삭제될 때 속성 탭 자동 동기화 useEffect(() => { // 현재 필드 상태를 문자열로 직렬화 const currentFieldsState = JSON.stringify( itemMasterFields.map(f => ({ id: f.id, name: f.field_name })).sort((a, b) => a.id - b.id) ); // 이전 상태와 동일하면 업데이트 스킵 if (prevFieldsRef.current === currentFieldsState) { return; } prevFieldsRef.current = currentFieldsState; // 현재 마스터 필드 ID 목록 const currentFieldIds = new Set(itemMasterFields.map(f => f.id.toString())); setAttributeSubTabs(prev => { const newTabs: AttributeSubTab[] = []; const updates: { key: string; label: string }[] = []; // 삭제된 마스터 항목에 해당하는 탭 제거 (숫자 key만 체크 - 마스터 항목 ID) const filteredTabs = prev.filter(tab => { // 숫자로만 이루어진 key는 마스터 항목 ID const isNumericKey = /^\d+$/.test(tab.key); if (isNumericKey && !currentFieldIds.has(tab.key)) { // 삭제된 마스터 항목의 탭 return false; } return true; }); // 새로운 마스터 항목 추가 또는 기존 항목 라벨 업데이트 itemMasterFields.forEach(field => { const existingTab = filteredTabs.find(tab => tab.key === field.id.toString()); if (!existingTab) { const maxOrder = Math.max(...filteredTabs.map(t => t.order), ...newTabs.map(t => t.order), -1); newTabs.push({ id: `attr-${field.id.toString()}`, label: field.field_name, key: field.id.toString(), isDefault: false, order: maxOrder + 1 }); } else if (existingTab.label !== field.field_name) { updates.push({ key: existingTab.key, label: field.field_name }); } }); // 탭 삭제, 추가, 업데이트 여부 확인 const hasRemovals = filteredTabs.length !== prev.length; const hasAdditions = newTabs.length > 0; const hasUpdates = updates.length > 0; // 변경사항 없으면 이전 상태 그대로 반환 if (!hasRemovals && !hasAdditions && !hasUpdates) { return prev; } let result = filteredTabs.map(tab => { const update = updates.find(u => u.key === tab.key); return update ? { ...tab, label: update.label } : tab; }); result = [...result, ...newTabs]; // 중복 제거 return result.filter((tab, index, self) => index === self.findIndex(t => t.key === tab.key) ); }); // 현재 활성 탭이 삭제된 마스터 항목인 경우 기본 탭으로 전환 const isNumericKey = /^\d+$/.test(activeAttributeTab); if (isNumericKey && !currentFieldIds.has(activeAttributeTab)) { setActiveAttributeTab('units'); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [itemMasterFields]); // 메인 탭 핸들러 const handleAddTab = () => { if (!newTabLabel.trim()) { toast.error('탭 이름을 입력해주세요'); return; } const newTab: CustomTab = { id: Date.now().toString(), label: newTabLabel, icon: 'FileText', isDefault: false, order: customTabs.length + 1 }; setCustomTabs(prev => [...prev, newTab]); setNewTabLabel(''); setIsAddTabDialogOpen(false); toast.success('탭이 추가되었습니다'); }; const handleUpdateTab = () => { if (!newTabLabel.trim() || !editingTabId) { toast.error('탭 이름을 입력해주세요'); return; } setCustomTabs(prev => prev.map(tab => tab.id === editingTabId ? { ...tab, label: newTabLabel } : tab )); setEditingTabId(null); setNewTabLabel(''); setIsAddTabDialogOpen(false); setIsManageTabsDialogOpen(true); toast.success('탭이 수정되었습니다'); }; const handleDeleteTab = (tabId: string) => { const tab = customTabs.find(t => t.id === tabId); if (!tab || tab.isDefault) { toast.error('기본 탭은 삭제할 수 없습니다'); return; } setDeletingTabId(tabId); setIsDeleteTabDialogOpen(true); }; const confirmDeleteTab = () => { if (!deletingTabId) return; setCustomTabs(prev => prev.filter(t => t.id !== deletingTabId)); if (activeTab === deletingTabId) { setActiveTab('hierarchy'); } setIsDeleteTabDialogOpen(false); setDeletingTabId(null); toast.success('탭이 삭제되었습니다'); }; // 속성 하위 탭 핸들러 const handleAddAttributeTab = () => { if (!newAttributeTabLabel.trim()) { toast.error('탭 이름을 입력해주세요'); return; } const newTab: AttributeSubTab = { id: `attr-${Date.now()}`, label: newAttributeTabLabel, key: `custom-${Date.now()}`, isDefault: false, order: attributeSubTabs.length }; setAttributeSubTabs(prev => [...prev, newTab]); setNewAttributeTabLabel(''); setIsAddAttributeTabDialogOpen(false); toast.success('속성 탭이 추가되었습니다'); }; const handleUpdateAttributeTab = () => { if (!newAttributeTabLabel.trim() || !editingAttributeTabId) { toast.error('탭 이름을 입력해주세요'); return; } setAttributeSubTabs(prev => prev.map(tab => tab.id === editingAttributeTabId ? { ...tab, label: newAttributeTabLabel } : tab )); setEditingAttributeTabId(null); setNewAttributeTabLabel(''); setIsAddAttributeTabDialogOpen(false); setIsManageAttributeTabsDialogOpen(true); toast.success('속성 탭이 수정되었습니다'); }; const handleDeleteAttributeTab = (tabId: string) => { const tab = attributeSubTabs.find(t => t.id === tabId); if (!tab || tab.isDefault) { toast.error('기본 속성 탭은 삭제할 수 없습니다'); return; } setDeletingAttributeTabId(tabId); setIsDeleteAttributeTabDialogOpen(true); }; const confirmDeleteAttributeTab = () => { if (!deletingAttributeTabId) return; setAttributeSubTabs(prev => prev.filter(t => t.id !== deletingAttributeTabId)); if (activeAttributeTab === deletingAttributeTabId) { const firstTab = attributeSubTabs.find(t => t.id !== deletingAttributeTabId); if (firstTab) { setActiveAttributeTab(firstTab.key); } } setIsDeleteAttributeTabDialogOpen(false); setDeletingAttributeTabId(null); toast.success('속성 탭이 삭제되었습니다'); }; // 탭 순서 변경 핸들러 const moveTabUp = (tabId: string) => { const tabIndex = customTabs.findIndex(t => t.id === tabId); if (tabIndex <= 0) return; const newTabs = [...customTabs]; const temp = newTabs[tabIndex - 1].order; newTabs[tabIndex - 1].order = newTabs[tabIndex].order; newTabs[tabIndex].order = temp; setCustomTabs(newTabs.sort((a, b) => a.order - b.order)); toast.success('탭 순서가 변경되었습니다'); }; const moveTabDown = (tabId: string) => { const tabIndex = customTabs.findIndex(t => t.id === tabId); if (tabIndex >= customTabs.length - 1) return; const newTabs = [...customTabs]; const temp = newTabs[tabIndex + 1].order; newTabs[tabIndex + 1].order = newTabs[tabIndex].order; newTabs[tabIndex].order = temp; setCustomTabs(newTabs.sort((a, b) => a.order - b.order)); toast.success('탭 순서가 변경되었습니다'); }; const moveAttributeTabUp = (tabId: string) => { const tabIndex = attributeSubTabs.findIndex(t => t.id === tabId); if (tabIndex <= 0) return; const newTabs = [...attributeSubTabs]; const temp = newTabs[tabIndex - 1].order; newTabs[tabIndex - 1].order = newTabs[tabIndex].order; newTabs[tabIndex].order = temp; setAttributeSubTabs(newTabs.sort((a, b) => a.order - b.order)); }; const moveAttributeTabDown = (tabId: string) => { const tabIndex = attributeSubTabs.findIndex(t => t.id === tabId); if (tabIndex >= attributeSubTabs.length - 1) return; const newTabs = [...attributeSubTabs]; const temp = newTabs[tabIndex + 1].order; newTabs[tabIndex + 1].order = newTabs[tabIndex].order; newTabs[tabIndex].order = temp; setAttributeSubTabs(newTabs.sort((a, b) => a.order - b.order)); }; // 아이콘 헬퍼 const getTabIcon = (iconName: string) => { const icons: Record = { FolderTree, ListTree, FileText, Settings, Layers, Database, Plus, Folder }; return icons[iconName] || FileText; }; // 탭 관리에서 수정 시작 const handleEditTabFromManage = (tab: CustomTab) => { if (tab.isDefault) { toast.error('기본 탭은 수정할 수 없습니다'); return; } setEditingTabId(tab.id); setNewTabLabel(tab.label); setIsManageTabsDialogOpen(false); setIsAddTabDialogOpen(true); }; return { // 메인 탭 상태 customTabs, setCustomTabs, activeTab, setActiveTab, // 속성 하위 탭 상태 attributeSubTabs, setAttributeSubTabs, activeAttributeTab, setActiveAttributeTab, // 메인 탭 다이얼로그 상태 isAddTabDialogOpen, setIsAddTabDialogOpen, isManageTabsDialogOpen, setIsManageTabsDialogOpen, newTabLabel, setNewTabLabel, editingTabId, setEditingTabId, deletingTabId, setDeletingTabId, isDeleteTabDialogOpen, setIsDeleteTabDialogOpen, // 속성 하위 탭 다이얼로그 상태 isManageAttributeTabsDialogOpen, setIsManageAttributeTabsDialogOpen, isAddAttributeTabDialogOpen, setIsAddAttributeTabDialogOpen, newAttributeTabLabel, setNewAttributeTabLabel, editingAttributeTabId, setEditingAttributeTabId, deletingAttributeTabId, setDeletingAttributeTabId, isDeleteAttributeTabDialogOpen, setIsDeleteAttributeTabDialogOpen, // 핸들러 handleAddTab, handleUpdateTab, handleDeleteTab, confirmDeleteTab, handleAddAttributeTab, handleUpdateAttributeTab, handleDeleteAttributeTab, confirmDeleteAttributeTab, moveTabUp, moveTabDown, moveAttributeTabUp, moveAttributeTabDown, getTabIcon, handleEditTabFromManage, }; }