diff --git a/src/components/items/ItemMasterDataManagement.tsx b/src/components/items/ItemMasterDataManagement.tsx index fbf59be0..b984357c 100644 --- a/src/components/items/ItemMasterDataManagement.tsx +++ b/src/components/items/ItemMasterDataManagement.tsx @@ -18,6 +18,23 @@ import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +// 다이얼로그 컴포넌트 import +import { TabManagementDialogs } from './ItemMasterDataManagement/dialogs/TabManagementDialogs'; +import { OptionDialog } from './ItemMasterDataManagement/dialogs/OptionDialog'; +import { ColumnManageDialog } from './ItemMasterDataManagement/dialogs/ColumnManageDialog'; +import { PathEditDialog } from './ItemMasterDataManagement/dialogs/PathEditDialog'; +import { PageDialog } from './ItemMasterDataManagement/dialogs/PageDialog'; +import { SectionDialog } from './ItemMasterDataManagement/dialogs/SectionDialog'; +import { FieldDialog } from './ItemMasterDataManagement/dialogs/FieldDialog'; +import { FieldDrawer } from './ItemMasterDataManagement/dialogs/FieldDrawer'; +import { ColumnDialog } from './ItemMasterDataManagement/dialogs/ColumnDialog'; +import { MasterFieldDialog } from './ItemMasterDataManagement/dialogs/MasterFieldDialog'; +import { SectionTemplateDialog } from './ItemMasterDataManagement/dialogs/SectionTemplateDialog'; +import { TemplateFieldDialog } from './ItemMasterDataManagement/dialogs/TemplateFieldDialog'; +import { LoadTemplateDialog } from './ItemMasterDataManagement/dialogs/LoadTemplateDialog'; +import { ImportSectionDialog } from './ItemMasterDataManagement/dialogs/ImportSectionDialog'; +import { ImportFieldDialog } from './ItemMasterDataManagement/dialogs/ImportFieldDialog'; + // 커스텀 훅 import import { usePageManagement, @@ -54,30 +71,30 @@ const INPUT_TYPE_OPTIONS = [ export function ItemMasterDataManagement() { const { itemPages, - loadItemPages, + loadItemPages: _loadItemPages, updateItemPage, - deleteItemPage, + deleteItemPage: _deleteItemPage, updateSection, - deleteSection, - reorderFields, + deleteSection: _deleteSection, + reorderFields: _reorderFields, itemMasterFields, - loadItemMasterFields, + loadItemMasterFields: _loadItemMasterFields, sectionTemplates, - loadSectionTemplates, - resetAllData, + loadSectionTemplates: _loadSectionTemplates, + resetAllData: _resetAllData, // 2025-11-26 추가: 독립 엔티티 관리 independentSections, - loadIndependentSections, - loadIndependentFields, + loadIndependentSections: _loadIndependentSections, + loadIndependentFields: _loadIndependentFields, refreshIndependentSections, refreshIndependentFields, - linkSectionToPage, - linkFieldToSection, - unlinkFieldFromSection, + linkSectionToPage: _linkSectionToPage, + linkFieldToSection: _linkFieldToSection, + unlinkFieldFromSection: _unlinkFieldFromSection, getSectionUsage, getFieldUsage, - cloneSection, - reorderSections, + cloneSection: _cloneSection, + reorderSections: _reorderSections, // 2025-11-27 추가: BOM 항목 API 함수 addBOMItem, updateBOMItem, @@ -368,10 +385,10 @@ export function ItemMasterDataManagement() { // 2025-12-24: 신규 훅에서 가져온 핸들러 래퍼 const handleImportSection = async () => handleImportSectionFromHook(selectedPageId); - const handleImportField = async () => handleImportFieldFromHook(selectedPage); - const moveSection = async (dragIndex: number, hoverIndex: number) => moveSectionFromHook(selectedPage, dragIndex, hoverIndex); - const moveField = async (sectionId: number, dragFieldId: number, hoverFieldId: number) => moveFieldFromHook(selectedPage, sectionId, dragFieldId, hoverFieldId); - const handleResetAllData = () => handleResetAllDataFromHook( + const handleImportField = async () => handleImportFieldFromHook(selectedPage ?? null); + const moveSection = async (dragIndex: number, hoverIndex: number) => moveSectionFromHook(selectedPage ?? null, dragIndex, hoverIndex); + const moveField = async (sectionId: number, dragFieldId: number, hoverFieldId: number) => moveFieldFromHook(selectedPage ?? null, sectionId, dragFieldId, hoverFieldId); + const _handleResetAllData = () => handleResetAllDataFromHook( setUnitOptions, setMaterialOptions, setSurfaceTreatmentOptions, diff --git a/src/components/items/ItemMasterDataManagement/components/AttributeTabContent.tsx b/src/components/items/ItemMasterDataManagement/components/AttributeTabContent.tsx index ec048588..eae33293 100644 --- a/src/components/items/ItemMasterDataManagement/components/AttributeTabContent.tsx +++ b/src/components/items/ItemMasterDataManagement/components/AttributeTabContent.tsx @@ -5,34 +5,14 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com import { Badge } from '@/components/ui/badge'; import { Plus, Trash2, Settings, Package } from 'lucide-react'; import type { ItemMasterField } from '@/contexts/ItemMasterContext'; +import type { MasterOption, OptionColumn } from '../types'; +import type { AttributeSubTab } from '../hooks/useTabManagement'; -// 타입 정의 -export interface UnitOption { - id: string; - value: string; - label: string; - inputType?: string; - required?: boolean; - placeholder?: string; - defaultValue?: string; - options?: string[]; - columnValues?: Record; -} +// UnitOption은 MasterOption으로 대체 +export type UnitOption = MasterOption; -export interface AttributeSubTab { - id: string; - key: string; - label: string; - order: number; -} - -export interface AttributeColumn { - id: string; - name: string; - key: string; - type: string; - required: boolean; -} +// AttributeColumn은 OptionColumn으로 대체 +export type AttributeColumn = OptionColumn; // 입력 타입 라벨 변환 헬퍼 함수 const getInputTypeLabel = (inputType: string | undefined): string => { @@ -68,7 +48,7 @@ interface AttributeTabContentProps { setManagingColumnType: (type: string) => void; setNewColumnName: (name: string) => void; setNewColumnKey: (key: string) => void; - setNewColumnType: (type: string) => void; + setNewColumnType: (type: 'text' | 'number') => void; setNewColumnRequired: (required: boolean) => void; handleDeleteOption: (type: string, id: string) => void; } diff --git a/src/components/items/ItemMasterDataManagement/components/index.ts b/src/components/items/ItemMasterDataManagement/components/index.ts index 981acb35..a7eae9ee 100644 --- a/src/components/items/ItemMasterDataManagement/components/index.ts +++ b/src/components/items/ItemMasterDataManagement/components/index.ts @@ -3,5 +3,4 @@ export { DraggableField } from './DraggableField'; // 2025-12-24: Phase 2 UI 컴포넌트 분리 export { AttributeTabContent } from './AttributeTabContent'; -export { ItemMasterDialogs } from './ItemMasterDialogs'; -export type { ItemMasterDialogsProps } from './ItemMasterDialogs'; \ No newline at end of file +// ItemMasterDialogs는 props가 너무 많아 사용하지 않음 (2025-12-24 결정) \ No newline at end of file diff --git a/src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts b/src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts index 24546b95..48ebcecd 100644 --- a/src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts +++ b/src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts @@ -5,7 +5,12 @@ import { useItemMaster } from '@/contexts/ItemMasterContext'; import { toast } from 'sonner'; import type { ItemPage, BOMItem } from '@/contexts/ItemMasterContext'; import type { CustomTab, AttributeSubTab } from './useTabManagement'; -import type { UnitOption, MaterialOption, SurfaceTreatmentOption } from './useAttributeManagement'; +import type { MasterOption, OptionColumn } from '../types'; + +// 타입 alias (기존 호환성) +type UnitOption = MasterOption; +type MaterialOption = MasterOption; +type SurfaceTreatmentOption = MasterOption; export interface UseDeleteManagementReturn { handleDeletePage: (pageId: number) => void; @@ -16,7 +21,7 @@ export interface UseDeleteManagementReturn { setMaterialOptions: React.Dispatch>, setSurfaceTreatmentOptions: React.Dispatch>, setCustomAttributeOptions: React.Dispatch>>, - setAttributeColumns: React.Dispatch>>, + setAttributeColumns: React.Dispatch>>, setBomItems: React.Dispatch>, setCustomTabs: React.Dispatch>, setAttributeSubTabs: React.Dispatch>, @@ -70,7 +75,7 @@ export function useDeleteManagement({ itemPages }: UseDeleteManagementProps): Us setMaterialOptions: React.Dispatch>, setSurfaceTreatmentOptions: React.Dispatch>, setCustomAttributeOptions: React.Dispatch>>, - setAttributeColumns: React.Dispatch>>, + setAttributeColumns: React.Dispatch>>, setBomItems: React.Dispatch>, setCustomTabs: React.Dispatch>, setAttributeSubTabs: React.Dispatch>, diff --git a/src/components/items/ItemMasterDataManagement/hooks/useInitialDataLoading.ts b/src/components/items/ItemMasterDataManagement/hooks/useInitialDataLoading.ts index 7340cfd7..1ea67c82 100644 --- a/src/components/items/ItemMasterDataManagement/hooks/useInitialDataLoading.ts +++ b/src/components/items/ItemMasterDataManagement/hooks/useInitialDataLoading.ts @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect, useCallback } from 'react'; +import { useState, useEffect, useCallback, useRef } from 'react'; import { useItemMaster } from '@/contexts/ItemMasterContext'; import { itemMasterApi } from '@/lib/api/item-master'; import { getErrorMessage, ApiError } from '@/lib/api/error-handler'; @@ -15,7 +15,10 @@ import { } from '@/lib/api/transformers'; import { toast } from 'sonner'; import type { CustomTab } from './useTabManagement'; -import type { UnitOption } from './useAttributeManagement'; +import type { MasterOption } from '../types'; + +// 타입 alias +type UnitOption = MasterOption; export interface UseInitialDataLoadingReturn { isInitialLoading: boolean; @@ -43,6 +46,9 @@ export function useInitialDataLoading({ const [isInitialLoading, setIsInitialLoading] = useState(true); const [error, setError] = useState(null); + // 초기 로딩이 이미 실행되었는지 추적하는 ref + const hasInitialLoadRun = useRef(false); + const loadInitialData = useCallback(async () => { try { setIsInitialLoading(true); @@ -138,9 +144,15 @@ export function useInitialDataLoading({ setUnitOptions, ]); + // 초기 로딩은 한 번만 실행 (의존성 배열의 함수들이 불안정해도 무한 루프 방지) useEffect(() => { + if (hasInitialLoadRun.current) { + return; + } + hasInitialLoadRun.current = true; loadInitialData(); - }, [loadInitialData]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return { isInitialLoading,