'use client'; import { useState, useEffect, useMemo } from 'react'; import { PageLayout } from '@/components/organisms/PageLayout'; import { PageHeader } from '@/components/organisms/PageHeader'; import { useItemMaster } from '@/contexts/ItemMasterContext'; import type { SectionTemplate, BOMItem, TemplateField } from '@/contexts/ItemMasterContext'; import { MasterFieldTab, HierarchyTab, SectionsTab } from './ItemMasterDataManagement/tabs'; import { LoadingSpinner } from '@/components/ui/loading-spinner'; import { ServerErrorPage } from '@/components/common/ServerErrorPage'; // 2025-12-24: Phase 2 UI 컴포넌트 분리 import { AttributeTabContent, ItemMasterDialogs } from './ItemMasterDataManagement/components'; import { Database, FileText, } from 'lucide-react'; 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 { Badge } from '@/components/ui/badge'; import { toast } from 'sonner'; // 커스텀 훅 import import { usePageManagement, useSectionManagement, useFieldManagement, useMasterFieldManagement, useTemplateManagement, useAttributeManagement, useTabManagement, // 2025-12-24: 신규 훅 추가 useInitialDataLoading, useImportManagement, useReorderManagement, useDeleteManagement, } from './ItemMasterDataManagement/hooks'; const ITEM_TYPE_OPTIONS = [ { value: 'FG', label: '제품 (FG)' }, { value: 'PT', label: '부품 (PT)' }, { value: 'SM', label: '부자재 (SM)' }, { value: 'RM', label: '원자재 (RM)' }, { value: 'CS', label: '소모품 (CS)' } ]; const INPUT_TYPE_OPTIONS = [ { value: 'textbox', label: '텍스트박스' }, { value: 'dropdown', label: '드롭다운' }, { value: 'checkbox', label: '체크박스' }, { value: 'number', label: '숫자' }, { value: 'date', label: '날짜' }, { value: 'textarea', label: '텍스트영역' } ]; // 입력 타입 라벨 변환 헬퍼 함수 (중복 코드 제거) const getInputTypeLabel = (inputType: string | undefined): string => { const labels: Record = { textbox: '텍스트박스', number: '숫자', dropdown: '드롭다운', checkbox: '체크박스', date: '날짜', textarea: '텍스트영역', }; return labels[inputType || ''] || '텍스트박스'; }; export function ItemMasterDataManagement() { const { itemPages, loadItemPages, updateItemPage, deleteItemPage, updateSection, deleteSection, reorderFields, itemMasterFields, loadItemMasterFields, sectionTemplates, loadSectionTemplates, resetAllData, // 2025-11-26 추가: 독립 엔티티 관리 independentSections, loadIndependentSections, loadIndependentFields, refreshIndependentSections, refreshIndependentFields, linkSectionToPage, linkFieldToSection, unlinkFieldFromSection, getSectionUsage, getFieldUsage, cloneSection, reorderSections, // 2025-11-27 추가: BOM 항목 API 함수 addBOMItem, updateBOMItem, deleteBOMItem, } = useItemMaster(); // ===== 커스텀 훅 초기화 ===== const pageManagement = usePageManagement(); const sectionManagement = useSectionManagement(); const fieldManagement = useFieldManagement(); const masterFieldManagement = useMasterFieldManagement(); const templateManagement = useTemplateManagement(); const attributeManagement = useAttributeManagement(); const tabManagement = useTabManagement(); // 2025-12-24: 신규 훅 (먼저 tabManagement, attributeManagement에서 setter 추출 필요) const { 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, } = tabManagement; const { unitOptions, setUnitOptions, materialOptions, setMaterialOptions, surfaceTreatmentOptions, setSurfaceTreatmentOptions, customAttributeOptions, setCustomAttributeOptions, isOptionDialogOpen, setIsOptionDialogOpen, editingOptionType, setEditingOptionType, newOptionValue, setNewOptionValue, newOptionLabel, setNewOptionLabel, newOptionColumnValues, setNewOptionColumnValues, newOptionInputType, setNewOptionInputType, newOptionRequired, setNewOptionRequired, newOptionOptions, setNewOptionOptions, newOptionPlaceholder, setNewOptionPlaceholder, newOptionDefaultValue, setNewOptionDefaultValue, isColumnManageDialogOpen, setIsColumnManageDialogOpen, managingColumnType, setManagingColumnType, attributeColumns, setAttributeColumns, newColumnName, setNewColumnName, newColumnKey, setNewColumnKey, newColumnType, setNewColumnType, newColumnRequired, setNewColumnRequired, handleAddOption, handleDeleteOption, handleAddColumn: _handleAddColumn, handleDeleteColumn: _handleDeleteColumn, } = attributeManagement; // 2025-12-24: 신규 훅 초기화 const { isInitialLoading, error } = useInitialDataLoading({ setCustomTabs, setUnitOptions, }); const importManagement = useImportManagement(); const { isImportSectionDialogOpen, setIsImportSectionDialogOpen, isImportFieldDialogOpen, setIsImportFieldDialogOpen, selectedImportSectionId, setSelectedImportSectionId, selectedImportFieldId, setSelectedImportFieldId, importFieldTargetSectionId, setImportFieldTargetSectionId, handleImportSection: handleImportSectionFromHook, handleImportField: handleImportFieldFromHook, handleCloneSection, } = importManagement; const reorderManagement = useReorderManagement(); const { moveSection: moveSectionFromHook, moveField: moveFieldFromHook } = reorderManagement; const deleteManagement = useDeleteManagement({ itemPages }); const { handleDeletePage: handleDeletePageWithTracking, handleDeleteSection: _handleDeleteSectionWithTracking, handleUnlinkField: handleUnlinkFieldWithTracking, handleResetAllData: handleResetAllDataFromHook, } = deleteManagement; // 훅에서 필요한 값들 구조분해 const { selectedPageId, setSelectedPageId, selectedPage, editingPageId, setEditingPageId, editingPageName, setEditingPageName, isPageDialogOpen, setIsPageDialogOpen, newPageName, setNewPageName, newPageItemType, setNewPageItemType, editingPathPageId, setEditingPathPageId, editingAbsolutePath, setEditingAbsolutePath, handleAddPage, handleDuplicatePage, } = pageManagement; const { editingSectionId, setEditingSectionId, editingSectionTitle, setEditingSectionTitle, isSectionDialogOpen, setIsSectionDialogOpen, newSectionTitle, setNewSectionTitle, newSectionDescription, setNewSectionDescription, newSectionType, setNewSectionType, sectionInputMode, setSectionInputMode, selectedSectionTemplateId, setSelectedSectionTemplateId, handleAddSection, handleLinkTemplate, handleEditSectionTitle, handleSaveSectionTitle, handleUnlinkSection, } = sectionManagement; const { isFieldDialogOpen, setIsFieldDialogOpen, selectedSectionForField, setSelectedSectionForField, editingFieldId, setEditingFieldId, fieldInputMode, setFieldInputMode, showMasterFieldList, setShowMasterFieldList, selectedMasterFieldId, setSelectedMasterFieldId, newFieldName, setNewFieldName, newFieldKey, setNewFieldKey, newFieldInputType, setNewFieldInputType, newFieldRequired, setNewFieldRequired, newFieldOptions, setNewFieldOptions, newFieldDescription, setNewFieldDescription, textboxColumns, setTextboxColumns, isColumnDialogOpen, setIsColumnDialogOpen, editingColumnId, setEditingColumnId, columnName, setColumnName, columnKey, setColumnKey, newFieldConditionEnabled, setNewFieldConditionEnabled, newFieldConditionTargetType, setNewFieldConditionTargetType, newFieldConditionFields, setNewFieldConditionFields, newFieldConditionSections, setNewFieldConditionSections, tempConditionValue, setTempConditionValue, handleAddField, handleEditField, } = fieldManagement; const { isMasterFieldDialogOpen, setIsMasterFieldDialogOpen, editingMasterFieldId, setEditingMasterFieldId, newMasterFieldName, setNewMasterFieldName, newMasterFieldKey, setNewMasterFieldKey, newMasterFieldInputType, setNewMasterFieldInputType, newMasterFieldRequired, setNewMasterFieldRequired, newMasterFieldCategory, setNewMasterFieldCategory, newMasterFieldDescription, setNewMasterFieldDescription, newMasterFieldOptions, setNewMasterFieldOptions, newMasterFieldAttributeType, setNewMasterFieldAttributeType, newMasterFieldMultiColumn, setNewMasterFieldMultiColumn, newMasterFieldColumnCount, setNewMasterFieldColumnCount, newMasterFieldColumnNames, setNewMasterFieldColumnNames, handleAddMasterField, handleEditMasterField, handleUpdateMasterField, handleDeleteMasterField, } = masterFieldManagement; const { isSectionTemplateDialogOpen, setIsSectionTemplateDialogOpen, editingSectionTemplateId, setEditingSectionTemplateId, newSectionTemplateTitle, setNewSectionTemplateTitle, newSectionTemplateDescription, setNewSectionTemplateDescription, newSectionTemplateCategory, setNewSectionTemplateCategory, newSectionTemplateType, setNewSectionTemplateType, isLoadTemplateDialogOpen, setIsLoadTemplateDialogOpen, selectedTemplateId, setSelectedTemplateId, isTemplateFieldDialogOpen, setIsTemplateFieldDialogOpen, currentTemplateId: _currentTemplateId, setCurrentTemplateId, editingTemplateFieldId, setEditingTemplateFieldId, templateFieldName, setTemplateFieldName, templateFieldKey, setTemplateFieldKey, templateFieldInputType, setTemplateFieldInputType, templateFieldRequired, setTemplateFieldRequired, templateFieldOptions, setTemplateFieldOptions, templateFieldDescription, setTemplateFieldDescription, templateFieldMultiColumn, setTemplateFieldMultiColumn, templateFieldColumnCount, setTemplateFieldColumnCount, templateFieldColumnNames, setTemplateFieldColumnNames, templateFieldInputMode, setTemplateFieldInputMode, templateFieldShowMasterFieldList, setTemplateFieldShowMasterFieldList, templateFieldSelectedMasterFieldId, setTemplateFieldSelectedMasterFieldId, handleAddSectionTemplate, handleEditSectionTemplate, handleUpdateSectionTemplate, handleDeleteSectionTemplate, handleLoadTemplate, handleAddTemplateField, handleEditTemplateField, handleDeleteTemplateField, handleAddBOMItemToTemplate, handleUpdateBOMItemInTemplate, handleDeleteBOMItemFromTemplate, } = templateManagement; // 모든 페이지의 섹션을 하나의 배열로 평탄화 const _itemSections = itemPages.flatMap(page => page.sections.map(section => ({ ...section, parentPageId: page.id })) ); // 2025-11-26: itemPages의 모든 섹션 + 독립 섹션(independentSections)을 SectionTemplate 형식으로 변환 // 이렇게 하면 계층구조 탭과 섹션 탭이 같은 데이터 소스를 사용하여 자동 동기화됨 // 독립 섹션: 페이지에서 연결 해제된 섹션 (page_id = null) const sectionsAsTemplates: SectionTemplate[] = useMemo(() => { // 1. itemPages에 연결된 섹션들 const linkedSections = itemPages.flatMap(page => page.sections.map(section => ({ id: section.id, tenant_id: section.tenant_id || 0, template_name: section.title, section_type: section.section_type, description: section.description || null, default_fields: null, // ItemField → TemplateField 변환 // 2025-12-16: field_key 전체 표시 (백엔드 형식: {ID}_{사용자입력}) fields: section.fields?.map(field => { const rawKey = field.field_key || field.field_name.toLowerCase().replace(/\s+/g, '_'); return { id: field.id.toString(), name: field.field_name, fieldKey: rawKey, property: { inputType: field.field_type, // 2025-11-27: is_required와 properties.required 둘 다 체크 required: field.is_required || field.properties?.required, options: field.options?.map((opt: { label: string; value: string }) => opt.label || opt.value), }, description: field.placeholder || undefined, } as TemplateField; }), bomItems: section.bom_items, created_by: section.created_by || null, updated_by: section.updated_by || null, created_at: section.created_at, updated_at: section.updated_at, })) ); // 2. 독립 섹션들 (page_id = null, 연결 해제된 섹션) // 2025-12-16: field_key 전체 표시 (백엔드 형식: {ID}_{사용자입력}) const unlinkedSections = independentSections.map(section => ({ id: section.id, tenant_id: section.tenant_id || 0, template_name: section.title, section_type: section.section_type, description: section.description || null, default_fields: null, fields: section.fields?.map(field => { const rawKey = field.field_key || field.field_name.toLowerCase().replace(/\s+/g, '_'); return { id: field.id.toString(), name: field.field_name, fieldKey: rawKey, property: { inputType: field.field_type, // 2025-11-27: is_required와 properties.required 둘 다 체크 required: field.is_required || field.properties?.required, options: field.options?.map((opt: { label: string; value: string }) => opt.label || opt.value), }, description: field.placeholder || undefined, } as TemplateField; }), bomItems: section.bom_items, created_by: section.created_by || null, updated_by: section.updated_by || null, created_at: section.created_at, updated_at: section.updated_at, })); // 3. 중복 제거 (같은 섹션이 여러 페이지에 연결되었거나, 연결 섹션과 독립 섹션에 동시 존재하는 경우) // 2025-12-01: linkedSections를 나중에 추가하여 우선시 (Map에서 나중 값이 덮어씀) // itemPages의 섹션이 최신 상태이므로 이 데이터가 우선되어야 실시간 업데이트 반영됨 const allSections = [...unlinkedSections, ...linkedSections]; const uniqueSections = Array.from( new Map(allSections.map(s => [s.id, s])).values() ); return uniqueSections; }, [itemPages, independentSections]); // 모바일 체크 const [isMobile, setIsMobile] = useState(false); useEffect(() => { const checkMobile = () => setIsMobile(window.innerWidth < 768); checkMobile(); window.addEventListener('resize', checkMobile); return () => window.removeEventListener('resize', checkMobile); }, []); // 필드, 마스터필드, 템플릿 관련 상태는 위의 훅에서 관리됩니다. // BOM 관리 상태 (훅에 없음) const [_bomItems, setBomItems] = useState([]); // 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( setUnitOptions, setMaterialOptions, setSurfaceTreatmentOptions, setCustomAttributeOptions, setAttributeColumns, setBomItems, setCustomTabs, setAttributeSubTabs, ); // ===== 래퍼 함수들 (훅 함수에 selectedPage 바인딩 및 타입 호환성) ===== const handleAddSectionWrapper = () => handleAddSection(selectedPage); const handleLinkTemplateWrapper = (template: SectionTemplate) => handleLinkTemplate(template, selectedPage); const handleSaveSectionTitleWrapper = () => handleSaveSectionTitle(selectedPage); const handleAddFieldWrapper = () => handleAddField(selectedPage); const handleLoadTemplateWrapper = () => handleLoadTemplate(selectedPage); // setter 래퍼들 (Dispatch 타입 호환성) // eslint-disable-next-line @typescript-eslint/no-explicit-any const setNewSectionTypeWrapper: React.Dispatch> = setNewSectionType as any; // eslint-disable-next-line @typescript-eslint/no-explicit-any const setNewPageItemTypeWrapper: React.Dispatch> = setNewPageItemType as any; // 초기 로딩 중 UI if (isInitialLoading) { return (
); } // 에러 발생 시 UI if (error) { return ( window.location.reload()} showContactInfo={true} /> ); } return (
{customTabs.sort((a, b) => a.order - b.order).map(tab => { const Icon = getTabIcon(tab.icon); return ( {tab.label} ); })} {/* setIsManageTabsDialogOpen(true)}*/} {/*>*/} {/* */} {/* 탭 관리*/} {/**/} {/* 전체 초기화 버튼 숨김 처리 - 디자인에 없는 기능 */} {/* */}
{/* 속성 탭 (단위/재질/표면처리 통합) */} 속성 관리 단위, 재질, 표면처리 등의 속성을 관리합니다 {/* 속성 하위 탭 (칩 형태) */}
{attributeSubTabs.sort((a, b) => a.order - b.order).map(tab => ( ))}
{/* 단위 관리 */} {activeAttributeTab === 'units' && (

단위 목록

{unitOptions.map((option) => { const columns = attributeColumns['units'] || []; const hasColumns = columns.length > 0 && option.columnValues; return (
{option.label} {option.inputType && ( {getInputTypeLabel(option.inputType)} )} {option.required && ( 필수 )}
값(Value): {option.value}
{option.placeholder && (
플레이스홀더: {option.placeholder}
)} {option.defaultValue && (
기본값: {option.defaultValue}
)} {option.inputType === 'dropdown' && option.options && (
옵션:
{option.options.map((opt, idx) => ( {opt} ))}
)}
{hasColumns && (

추가 칼럼

{columns.map((column) => (
{column.name}: {option.columnValues?.[column.key] || '-'}
))}
)}
); })}
)} {/* 재질 관리 */} {activeAttributeTab === 'materials' && (

재질 목록

{materialOptions.map((option) => { const columns = attributeColumns['materials'] || []; const hasColumns = columns.length > 0 && option.columnValues; return (
{option.label} {option.inputType && ( {getInputTypeLabel(option.inputType)} )} {option.required && ( 필수 )}
값(Value): {option.value}
{option.placeholder && (
플레이스홀더: {option.placeholder}
)} {option.defaultValue && (
기본값: {option.defaultValue}
)} {option.inputType === 'dropdown' && option.options && (
옵션:
{option.options.map((opt, idx) => ( {opt} ))}
)}
{hasColumns && (

추가 칼럼

{columns.map((column) => (
{column.name}: {option.columnValues?.[column.key] || '-'}
))}
)}
); })}
)} {/* 표면처리 관리 */} {activeAttributeTab === 'surface' && (

표면처리 목록

{surfaceTreatmentOptions.map((option) => { const columns = attributeColumns['surface'] || []; const hasColumns = columns.length > 0 && option.columnValues; const inputTypeLabel = getInputTypeLabel(option.inputType); return (
{option.label} {option.inputType && ( {inputTypeLabel} )} {option.required && ( 필수 )}
값(Value): {option.value}
{option.placeholder && (
플레이스홀더: {option.placeholder}
)} {option.defaultValue && (
기본값: {option.defaultValue}
)} {option.inputType === 'dropdown' && option.options && (
옵션:
{option.options.map((opt, idx) => ( {opt} ))}
)}
{hasColumns && (

추가 칼럼

{columns.map((column) => (
{column.name}: {option.columnValues?.[column.key] || '-'}
))}
)}
); })}
)} {/* 사용자 정의 속성 탭 및 마스터 항목 탭 */} {!['units', 'materials', 'surface'].includes(activeAttributeTab) && (() => { const currentTabKey = activeAttributeTab; // 마스터 항목인지 확인 const masterField = itemMasterFields.find(f => f.id.toString() === currentTabKey); // 마스터 항목이면 해당 항목의 속성값들을 표시 // Note: properties is Record | null, convert to array for display const propertiesArray = masterField?.properties ? Object.entries(masterField.properties).map(([key, value]) => ({ key, ...value as object })) : []; if (masterField && propertiesArray.length > 0) { return (

{masterField.field_name} 속성 목록

항목 탭에서 추가한 "{masterField.field_name}" 항목의 속성값들입니다

{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} {propertiesArray.map((property: any) => { const inputTypeLabel = getInputTypeLabel(property.type); return (
{property.label} {inputTypeLabel} {property.required && ( 필수 )}
키(Key): {property.key}
{property.placeholder && (
플레이스홀더: {property.placeholder}
)} {property.defaultValue && (
기본값: {property.defaultValue}
)} {property.type === 'dropdown' && property.options && (
옵션:
{property.options.map((opt: string, idx: number) => ( {opt} ))}
)}
); })}

마스터 항목 속성 관리

이 속성들은 항목 탭에서 "{masterField.field_name}" 항목을 편집하여 추가/수정/삭제할 수 있습니다.

); } // 사용자 정의 속성 탭 (기존 로직) const currentOptions = customAttributeOptions[currentTabKey] || []; return (

{attributeSubTabs.find(t => t.key === activeAttributeTab)?.label || '사용자 정의'} 목록

{currentOptions.length > 0 ? (
{currentOptions.map((option) => { const columns = attributeColumns[currentTabKey] || []; const hasColumns = columns.length > 0 && option.columnValues; const inputTypeLabel = getInputTypeLabel(option.inputType); return (
{option.label} {inputTypeLabel} {option.required && ( 필수 )}
값(Value): {option.value}
{option.placeholder && (
플레이스홀더: {option.placeholder}
)} {option.defaultValue && (
기본값: {option.defaultValue}
)} {option.inputType === 'dropdown' && option.options && (
옵션:
{option.options.map((opt, idx) => ( {opt} ))}
)}
{hasColumns && (

추가 칼럼

{columns.map((column) => (
{column.name}: {option.columnValues?.[column.key] || '-'}
))}
)}
); })}
) : (

아직 추가된 항목이 없습니다

위 "추가" 버튼을 클릭하여 새로운 속성을 추가할 수 있습니다

)}
); })()}
{/* 항목 탭 */} {/* 섹션관리 탭 */} ({ value: opt.value, label: opt.label }))} onCloneSection={handleCloneSection} setIsImportFieldDialogOpen={setIsImportFieldDialogOpen} setImportFieldTargetSectionId={setImportFieldTargetSectionId} /> {/* 계층구조 탭 */} ({ value: opt.value, label: opt.label }))} editingPageId={editingPageId} setEditingPageId={setEditingPageId} editingPageName={editingPageName} setEditingPageName={setEditingPageName} selectedPageId={selectedPageId} setSelectedPageId={setSelectedPageId} editingPathPageId={editingPathPageId} setEditingPathPageId={setEditingPathPageId} editingAbsolutePath={editingAbsolutePath} setEditingAbsolutePath={setEditingAbsolutePath} editingSectionId={editingSectionId} setEditingSectionId={setEditingSectionId} editingSectionTitle={editingSectionTitle} setEditingSectionTitle={setEditingSectionTitle} hasUnsavedChanges={false} pendingChanges={{ pages: [], sections: [], fields: [], masterFields: [], attributes: [], sectionTemplates: [] }} selectedSectionForField={selectedSectionForField} setSelectedSectionForField={setSelectedSectionForField} newSectionType={newSectionType} setNewSectionType={setNewSectionTypeWrapper} updateItemPage={updateItemPage} trackChange={() => {}} deleteItemPage={handleDeletePageWithTracking} duplicatePage={handleDuplicatePage} setIsPageDialogOpen={setIsPageDialogOpen} setIsSectionDialogOpen={setIsSectionDialogOpen} setIsFieldDialogOpen={setIsFieldDialogOpen} handleEditSectionTitle={handleEditSectionTitle} handleSaveSectionTitle={handleSaveSectionTitleWrapper} moveSection={moveSection} unlinkSection={handleUnlinkSection} updateSection={updateSection} deleteField={handleUnlinkFieldWithTracking} handleEditField={handleEditField} moveField={moveField} setIsImportSectionDialogOpen={setIsImportSectionDialogOpen} setIsImportFieldDialogOpen={setIsImportFieldDialogOpen} setImportFieldTargetSectionId={setImportFieldTargetSectionId} // 2025-11-27 추가: BOM 항목 API 함수 addBOMItem={addBOMItem} updateBOMItem={updateBOMItem} deleteBOMItem={deleteBOMItem} /> {/* 사용자 정의 탭들 */} {customTabs.filter(tab => !tab.isDefault).map(tab => ( {tab.label} 사용자 정의 탭입니다. 여기에 필요한 콘텐츠를 추가할 수 있습니다.

{tab.label} 탭의 콘텐츠가 비어있습니다

이 탭에 필요한 기능을 추가하여 사용하세요

))}
{}} /> {/* 항목 추가/수정 다이얼로그 - 데스크톱 */} {!isMobile && ( s.id === selectedSectionForField) || null} selectedPage={selectedPage || null} itemMasterFields={itemMasterFields} handleAddField={handleAddFieldWrapper} setIsColumnDialogOpen={setIsColumnDialogOpen} setEditingColumnId={setEditingColumnId} setColumnName={setColumnName} setColumnKey={setColumnKey} /> )} {/* 항목 추가/수정 다이얼로그 - 모바일 (바텀시트) */} {isMobile && ( s.id === selectedSectionForField) || null} selectedPage={selectedPage || null} itemMasterFields={itemMasterFields} handleAddField={handleAddFieldWrapper} setIsColumnDialogOpen={setIsColumnDialogOpen} setEditingColumnId={setEditingColumnId} setColumnName={setColumnName} setColumnKey={setColumnKey} /> )} {/* 텍스트박스 컬럼 추가/수정 다이얼로그 */} {/* 섹션 불러오기 다이얼로그 */} {/* 필드 불러오기 다이얼로그 - 2025-11-27: 탭 통합 (항목+독립필드 → 필드) */} s.id === importFieldTargetSectionId)?.title : undefined } />
); }