Files
sam-react-prod/src/components/items/ItemMasterDataManagement/hooks/useTabManagement.ts

441 lines
14 KiB
TypeScript
Raw Normal View History

'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<React.SetStateAction<CustomTab[]>>;
activeTab: string;
setActiveTab: (tab: string) => void;
// 속성 하위 탭 상태
attributeSubTabs: AttributeSubTab[];
setAttributeSubTabs: React.Dispatch<React.SetStateAction<AttributeSubTab[]>>;
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<CustomTab[]>([
{ 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');
// 속성 하위 탭 상태
const [attributeSubTabs, setAttributeSubTabs] = useState<AttributeSubTab[]>([]);
const [activeAttributeTab, setActiveAttributeTab] = useState('units');
// 메인 탭 다이얼로그 상태
const [isAddTabDialogOpen, setIsAddTabDialogOpen] = useState(false);
const [isManageTabsDialogOpen, setIsManageTabsDialogOpen] = useState(false);
const [newTabLabel, setNewTabLabel] = useState('');
const [editingTabId, setEditingTabId] = useState<string | null>(null);
const [deletingTabId, setDeletingTabId] = useState<string | null>(null);
const [isDeleteTabDialogOpen, setIsDeleteTabDialogOpen] = useState(false);
// 속성 하위 탭 다이얼로그 상태
const [isManageAttributeTabsDialogOpen, setIsManageAttributeTabsDialogOpen] = useState(false);
const [isAddAttributeTabDialogOpen, setIsAddAttributeTabDialogOpen] = useState(false);
const [newAttributeTabLabel, setNewAttributeTabLabel] = useState('');
const [editingAttributeTabId, setEditingAttributeTabId] = useState<string | null>(null);
const [deletingAttributeTabId, setDeletingAttributeTabId] = useState<string | null>(null);
const [isDeleteAttributeTabDialogOpen, setIsDeleteAttributeTabDialogOpen] = useState(false);
// 이전 필드 상태 추적용 ref (무한 루프 방지)
const prevFieldsRef = useRef<string>('');
// 마스터 항목이 추가/수정될 때 속성 탭 자동 생성
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;
setAttributeSubTabs(prev => {
const newTabs: AttributeSubTab[] = [];
const updates: { key: string; label: string }[] = [];
itemMasterFields.forEach(field => {
const existingTab = prev.find(tab => tab.key === field.id.toString());
if (!existingTab) {
const maxOrder = Math.max(...prev.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 });
}
});
// 변경사항 없으면 이전 상태 그대로 반환
if (newTabs.length === 0 && updates.length === 0) {
return prev;
}
let result = prev.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)
);
});
// 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<string, any> = {
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,
};
}