'use client'; import { useState, useEffect, useMemo } from 'react'; import dynamic from 'next/dynamic'; 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 { DetailPageSkeleton } from '@/components/ui/skeleton'; import { ServerErrorPage } from '@/components/common/ServerErrorPage'; // 2025-12-24: Phase 2 UI 컴포넌트 분리 import { AttributeTabContent } 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'; // 다이얼로그 컴포넌트 - lazy load (사용자 클릭 시에만 로드) const TabManagementDialogs = dynamic( () => import('./ItemMasterDataManagement/dialogs/TabManagementDialogs').then(mod => ({ default: mod.TabManagementDialogs })), ); const OptionDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/OptionDialog').then(mod => ({ default: mod.OptionDialog })), ); const ColumnManageDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/ColumnManageDialog').then(mod => ({ default: mod.ColumnManageDialog })), ); const PathEditDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/PathEditDialog').then(mod => ({ default: mod.PathEditDialog })), ); const PageDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/PageDialog').then(mod => ({ default: mod.PageDialog })), ); const SectionDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/SectionDialog').then(mod => ({ default: mod.SectionDialog })), ); const FieldDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/FieldDialog').then(mod => ({ default: mod.FieldDialog })), ); const FieldDrawer = dynamic( () => import('./ItemMasterDataManagement/dialogs/FieldDrawer').then(mod => ({ default: mod.FieldDrawer })), ); const ColumnDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/ColumnDialog').then(mod => ({ default: mod.ColumnDialog })), ); const MasterFieldDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/MasterFieldDialog').then(mod => ({ default: mod.MasterFieldDialog })), ); const SectionTemplateDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/SectionTemplateDialog').then(mod => ({ default: mod.SectionTemplateDialog })), ); const TemplateFieldDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/TemplateFieldDialog').then(mod => ({ default: mod.TemplateFieldDialog })), ); const LoadTemplateDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/LoadTemplateDialog').then(mod => ({ default: mod.LoadTemplateDialog })), ); const ImportSectionDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/ImportSectionDialog').then(mod => ({ default: mod.ImportSectionDialog })), ); const ImportFieldDialog = dynamic( () => import('./ItemMasterDataManagement/dialogs/ImportFieldDialog').then(mod => ({ default: mod.ImportFieldDialog })), ); // 커스텀 훅 import import { usePageManagement, useSectionManagement, useFieldManagement, useMasterFieldManagement, useTemplateManagement, useAttributeManagement, useTabManagement, // 2025-12-24: 신규 훅 추가 useInitialDataLoading, useImportManagement, useReorderManagement, useDeleteManagement, } from './ItemMasterDataManagement/hooks'; // 에러 알림 Context import { ErrorAlertProvider } from './ItemMasterDataManagement/contexts'; 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: '텍스트영역' } ]; // Wrapper 컴포넌트: ErrorAlertProvider를 먼저 제공 export function ItemMasterDataManagement() { return ( ); } // 실제 로직을 담는 내부 컴포넌트 function ItemMasterDataManagementContent() { const { itemPages, loadItemPages: _loadItemPages, updateItemPage, deleteItemPage: _deleteItemPage, updateSection, deleteSection: _deleteSection, reorderFields: _reorderFields, itemMasterFields, loadItemMasterFields: _loadItemMasterFields, sectionTemplates, loadSectionTemplates: _loadSectionTemplates, resetAllData: _resetAllData, // 2025-11-26 추가: 독립 엔티티 관리 independentSections, loadIndependentSections: _loadIndependentSections, loadIndependentFields: _loadIndependentFields, refreshIndependentSections, refreshIndependentFields, linkSectionToPage: _linkSectionToPage, linkFieldToSection: _linkFieldToSection, unlinkFieldFromSection: _unlinkFieldFromSection, getSectionUsage, getFieldUsage, cloneSection: _cloneSection, reorderSections: _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, } = 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, 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, 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; // 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 ?? 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, 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; // SectionTemplateDialog 카테고리 래퍼 (string | string[] → string[]) const handleSetNewSectionTemplateCategory = (category: string | string[]) => { if (Array.isArray(category)) { setNewSectionTemplateCategory(category); } else { setNewSectionTemplateCategory(category ? [category] : []); } }; // LoadTemplateDialog 선택 ID 래퍼 (string | number | null → string | null) const handleSetSelectedTemplateId = (id: string | number | null) => { if (id === null) { setSelectedTemplateId(null); } else { setSelectedTemplateId(String(id)); } }; // TemplateFieldDialog 선택 ID 래퍼 (string | number | null → string) const handleSetTemplateFieldSelectedMasterFieldId = (id: string | number | null) => { setTemplateFieldSelectedMasterFieldId(id === null ? '' : String(id)); }; // 초기 로딩 중 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)}*/} {/*>*/} {/* */} {/* 탭 관리*/} {/**/} {/* 전체 초기화 버튼 숨김 처리 - 디자인에 없는 기능 */} {/* 전체 초기화 */} {/* 속성 탭 (단위/재질/표면처리 통합) - 2025-12-24: AttributeTabContent로 분리 */} {/* 항목 탭 */} {/* 섹션관리 탭 */} ({ 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 && ( void} selectedSectionForField={selectedPage?.sections.find(s => s.id === selectedSectionForField) || null} selectedPage={selectedPage || null} itemMasterFields={itemMasterFields} handleAddField={handleAddFieldWrapper} setIsColumnDialogOpen={setIsColumnDialogOpen} setEditingColumnId={setEditingColumnId} setColumnName={setColumnName} setColumnKey={setColumnKey} /> )} {/* 항목 추가/수정 다이얼로그 - 모바일 (바텀시트) */} {isMobile && ( void} selectedSectionForField={selectedPage?.sections.find(s => 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 } /> ); }
{tab.label} 탭의 콘텐츠가 비어있습니다
이 탭에 필요한 기능을 추가하여 사용하세요