- 회계 모듈 탭 UI 복원 (대손/은행거래/청구/입금/예상경비/상품권/매입/매출/세금계산서/거래처원장/거래처/출금) - HR 모듈 탭 복원 (근태/급여/휴가) - 대시보드 type2/3/4 페이지 개선 - CEO 대시보드 섹션 로딩 최적화 - 품목 마스터데이터 관리 탭 기능 강화 - 생산 작업자화면/작업지시 개선 - 품질 검사 생성/상세 화면 보완 - 건설 견적/현장관리 상세 개선 - UniversalListPage 기능 확장 - E2E 잔여 버그 핸드오프 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1037 lines
45 KiB
TypeScript
1037 lines
45 KiB
TypeScript
'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 (
|
|
<ErrorAlertProvider>
|
|
<ItemMasterDataManagementContent />
|
|
</ErrorAlertProvider>
|
|
);
|
|
}
|
|
|
|
// 실제 로직을 담는 내부 컴포넌트
|
|
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<BOMItem[]>([]);
|
|
|
|
// 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<SetStateAction> 타입 호환성)
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const setNewSectionTypeWrapper: React.Dispatch<React.SetStateAction<'fields' | 'bom'>> = setNewSectionType as any;
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const setNewPageItemTypeWrapper: React.Dispatch<React.SetStateAction<'FG' | 'PT' | 'SM' | 'RM' | 'CS'>> = 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 <DetailPageSkeleton />;
|
|
}
|
|
|
|
// 에러 발생 시 UI
|
|
if (error) {
|
|
return (
|
|
<ServerErrorPage
|
|
title="데이터를 불러올 수 없습니다"
|
|
message={error}
|
|
onRetry={() => window.location.reload()}
|
|
showContactInfo={true}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<PageLayout>
|
|
<PageHeader
|
|
title="품목기준관리"
|
|
description="품목관리에서 사용되는 기준 정보를 설정하고 관리합니다"
|
|
icon={Database}
|
|
/>
|
|
|
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="min-w-0 overflow-x-hidden">
|
|
<div
|
|
className="mb-4 min-w-0 overflow-x-auto [&::-webkit-scrollbar]:hidden"
|
|
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
|
|
>
|
|
<TabsList>
|
|
{customTabs.sort((a, b) => a.order - b.order).map(tab => {
|
|
const Icon = getTabIcon(tab.icon);
|
|
return (
|
|
<TabsTrigger key={tab.id} value={tab.id}>
|
|
<Icon className="w-4 h-4 mr-2" />
|
|
{tab.label}
|
|
</TabsTrigger>
|
|
);
|
|
})}
|
|
</TabsList>
|
|
{/*<Button*/}
|
|
{/* size="sm"*/}
|
|
{/* variant="outline"*/}
|
|
{/* onClick={() => setIsManageTabsDialogOpen(true)}*/}
|
|
{/*>*/}
|
|
{/* <Settings className="h-4 w-4 mr-1" />*/}
|
|
{/* 탭 관리*/}
|
|
{/*</Button>*/}
|
|
{/* 전체 초기화 버튼 숨김 처리 - 디자인에 없는 기능 */}
|
|
{/* <Button
|
|
size="sm"
|
|
variant="destructive"
|
|
onClick={handleResetAllData}
|
|
>
|
|
<Trash2 className="h-4 w-4 mr-1" />
|
|
전체 초기화
|
|
</Button> */}
|
|
</div>
|
|
|
|
{/* 속성 탭 (단위/재질/표면처리 통합) - 2025-12-24: AttributeTabContent로 분리 */}
|
|
<TabsContent value="attributes" className="space-y-4">
|
|
<AttributeTabContent
|
|
activeAttributeTab={activeAttributeTab}
|
|
setActiveAttributeTab={setActiveAttributeTab}
|
|
attributeSubTabs={attributeSubTabs}
|
|
unitOptions={unitOptions}
|
|
materialOptions={materialOptions}
|
|
surfaceTreatmentOptions={surfaceTreatmentOptions}
|
|
customAttributeOptions={customAttributeOptions}
|
|
attributeColumns={attributeColumns}
|
|
itemMasterFields={itemMasterFields}
|
|
setIsManageAttributeTabsDialogOpen={setIsManageAttributeTabsDialogOpen}
|
|
setIsOptionDialogOpen={setIsOptionDialogOpen}
|
|
setEditingOptionType={setEditingOptionType}
|
|
setNewOptionValue={setNewOptionValue}
|
|
setNewOptionLabel={setNewOptionLabel}
|
|
setNewOptionColumnValues={setNewOptionColumnValues}
|
|
setIsColumnManageDialogOpen={setIsColumnManageDialogOpen}
|
|
setManagingColumnType={setManagingColumnType}
|
|
setNewColumnName={setNewColumnName}
|
|
setNewColumnKey={setNewColumnKey}
|
|
setNewColumnType={setNewColumnType}
|
|
setNewColumnRequired={setNewColumnRequired}
|
|
handleDeleteOption={handleDeleteOption}
|
|
/>
|
|
</TabsContent>
|
|
|
|
{/* 항목 탭 */}
|
|
<TabsContent value="items" className="space-y-4">
|
|
<MasterFieldTab
|
|
itemMasterFields={itemMasterFields}
|
|
setIsMasterFieldDialogOpen={setIsMasterFieldDialogOpen}
|
|
handleEditMasterField={handleEditMasterField}
|
|
handleDeleteMasterField={handleDeleteMasterField}
|
|
hasUnsavedChanges={false}
|
|
pendingChanges={{ masterFields: [] }}
|
|
/>
|
|
</TabsContent>
|
|
|
|
{/* 섹션관리 탭 */}
|
|
<TabsContent value="sections" className="space-y-4">
|
|
<SectionsTab
|
|
sectionTemplates={sectionsAsTemplates}
|
|
setIsSectionTemplateDialogOpen={setIsSectionTemplateDialogOpen}
|
|
setCurrentTemplateId={setCurrentTemplateId}
|
|
setIsTemplateFieldDialogOpen={setIsTemplateFieldDialogOpen}
|
|
handleEditSectionTemplate={handleEditSectionTemplate}
|
|
handleDeleteSectionTemplate={handleDeleteSectionTemplate}
|
|
handleEditTemplateField={handleEditTemplateField}
|
|
handleDeleteTemplateField={handleDeleteTemplateField}
|
|
handleAddBOMItemToTemplate={handleAddBOMItemToTemplate}
|
|
handleUpdateBOMItemInTemplate={handleUpdateBOMItemInTemplate}
|
|
handleDeleteBOMItemFromTemplate={handleDeleteBOMItemFromTemplate}
|
|
ITEM_TYPE_OPTIONS={ITEM_TYPE_OPTIONS}
|
|
INPUT_TYPE_OPTIONS={INPUT_TYPE_OPTIONS}
|
|
unitOptions={unitOptions.map(opt => ({ value: opt.value, label: opt.label }))}
|
|
onCloneSection={handleCloneSection}
|
|
setIsImportFieldDialogOpen={setIsImportFieldDialogOpen}
|
|
setImportFieldTargetSectionId={setImportFieldTargetSectionId}
|
|
/>
|
|
</TabsContent>
|
|
|
|
{/* 계층구조 탭 */}
|
|
<TabsContent value="hierarchy" className="space-y-4">
|
|
<HierarchyTab
|
|
itemPages={itemPages}
|
|
selectedPage={selectedPage}
|
|
ITEM_TYPE_OPTIONS={ITEM_TYPE_OPTIONS}
|
|
unitOptions={unitOptions.map(opt => ({ 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}
|
|
/>
|
|
</TabsContent>
|
|
|
|
{/* 사용자 정의 탭들 */}
|
|
{customTabs.filter(tab => !tab.isDefault).map(tab => (
|
|
<TabsContent key={tab.id} value={tab.id}>
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>{tab.label}</CardTitle>
|
|
<CardDescription>사용자 정의 탭입니다. 여기에 필요한 콘텐츠를 추가할 수 있습니다.</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-center py-12">
|
|
<FileText className="w-16 h-16 mx-auto text-gray-300 mb-4" />
|
|
<p className="text-muted-foreground mb-2">{tab.label} 탭의 콘텐츠가 비어있습니다</p>
|
|
<p className="text-sm text-muted-foreground">
|
|
이 탭에 필요한 기능을 추가하여 사용하세요
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
))}
|
|
</Tabs>
|
|
|
|
<TabManagementDialogs
|
|
isManageTabsDialogOpen={isManageTabsDialogOpen}
|
|
setIsManageTabsDialogOpen={setIsManageTabsDialogOpen}
|
|
customTabs={customTabs}
|
|
moveTabUp={moveTabUp}
|
|
moveTabDown={moveTabDown}
|
|
handleEditTabFromManage={handleEditTabFromManage}
|
|
handleDeleteTab={handleDeleteTab}
|
|
getTabIcon={getTabIcon}
|
|
setIsAddTabDialogOpen={setIsAddTabDialogOpen}
|
|
isDeleteTabDialogOpen={isDeleteTabDialogOpen}
|
|
setIsDeleteTabDialogOpen={setIsDeleteTabDialogOpen}
|
|
deletingTabId={deletingTabId}
|
|
setDeletingTabId={setDeletingTabId}
|
|
confirmDeleteTab={confirmDeleteTab}
|
|
isAddTabDialogOpen={isAddTabDialogOpen}
|
|
editingTabId={editingTabId}
|
|
setEditingTabId={setEditingTabId}
|
|
newTabLabel={newTabLabel}
|
|
setNewTabLabel={setNewTabLabel}
|
|
handleUpdateTab={handleUpdateTab}
|
|
handleAddTab={handleAddTab}
|
|
isManageAttributeTabsDialogOpen={isManageAttributeTabsDialogOpen}
|
|
setIsManageAttributeTabsDialogOpen={setIsManageAttributeTabsDialogOpen}
|
|
attributeSubTabs={attributeSubTabs}
|
|
moveAttributeTabUp={moveAttributeTabUp}
|
|
moveAttributeTabDown={moveAttributeTabDown}
|
|
handleDeleteAttributeTab={handleDeleteAttributeTab}
|
|
isDeleteAttributeTabDialogOpen={isDeleteAttributeTabDialogOpen}
|
|
setIsDeleteAttributeTabDialogOpen={setIsDeleteAttributeTabDialogOpen}
|
|
deletingAttributeTabId={deletingAttributeTabId}
|
|
setDeletingAttributeTabId={setDeletingAttributeTabId}
|
|
confirmDeleteAttributeTab={confirmDeleteAttributeTab}
|
|
isAddAttributeTabDialogOpen={isAddAttributeTabDialogOpen}
|
|
setIsAddAttributeTabDialogOpen={setIsAddAttributeTabDialogOpen}
|
|
editingAttributeTabId={editingAttributeTabId}
|
|
setEditingAttributeTabId={setEditingAttributeTabId}
|
|
newAttributeTabLabel={newAttributeTabLabel}
|
|
setNewAttributeTabLabel={setNewAttributeTabLabel}
|
|
handleUpdateAttributeTab={handleUpdateAttributeTab}
|
|
handleAddAttributeTab={handleAddAttributeTab}
|
|
/>
|
|
|
|
<OptionDialog
|
|
isOpen={isOptionDialogOpen}
|
|
setIsOpen={setIsOptionDialogOpen}
|
|
newOptionValue={newOptionValue}
|
|
setNewOptionValue={setNewOptionValue}
|
|
newOptionLabel={newOptionLabel}
|
|
setNewOptionLabel={setNewOptionLabel}
|
|
newOptionColumnValues={newOptionColumnValues}
|
|
setNewOptionColumnValues={setNewOptionColumnValues}
|
|
newOptionInputType={newOptionInputType}
|
|
setNewOptionInputType={setNewOptionInputType}
|
|
newOptionRequired={newOptionRequired}
|
|
setNewOptionRequired={setNewOptionRequired}
|
|
newOptionOptions={newOptionOptions}
|
|
setNewOptionOptions={setNewOptionOptions}
|
|
newOptionPlaceholder={newOptionPlaceholder}
|
|
setNewOptionPlaceholder={setNewOptionPlaceholder}
|
|
newOptionDefaultValue={newOptionDefaultValue}
|
|
setNewOptionDefaultValue={setNewOptionDefaultValue}
|
|
editingOptionType={editingOptionType}
|
|
attributeSubTabs={attributeSubTabs}
|
|
attributeColumns={attributeColumns}
|
|
handleAddOption={handleAddOption}
|
|
/>
|
|
|
|
<ColumnManageDialog
|
|
isOpen={isColumnManageDialogOpen}
|
|
setIsOpen={setIsColumnManageDialogOpen}
|
|
managingColumnType={managingColumnType}
|
|
attributeSubTabs={attributeSubTabs}
|
|
attributeColumns={attributeColumns}
|
|
setAttributeColumns={setAttributeColumns}
|
|
newColumnName={newColumnName}
|
|
setNewColumnName={setNewColumnName}
|
|
newColumnKey={newColumnKey}
|
|
setNewColumnKey={setNewColumnKey}
|
|
newColumnType={newColumnType}
|
|
setNewColumnType={setNewColumnType}
|
|
newColumnRequired={newColumnRequired}
|
|
setNewColumnRequired={setNewColumnRequired}
|
|
/>
|
|
|
|
|
|
<PathEditDialog
|
|
editingPathPageId={editingPathPageId}
|
|
setEditingPathPageId={setEditingPathPageId}
|
|
editingAbsolutePath={editingAbsolutePath}
|
|
setEditingAbsolutePath={setEditingAbsolutePath}
|
|
updateItemPage={updateItemPage}
|
|
trackChange={() => {}}
|
|
/>
|
|
|
|
<PageDialog
|
|
isPageDialogOpen={isPageDialogOpen}
|
|
setIsPageDialogOpen={setIsPageDialogOpen}
|
|
newPageName={newPageName}
|
|
setNewPageName={setNewPageName}
|
|
newPageItemType={newPageItemType}
|
|
setNewPageItemType={setNewPageItemTypeWrapper}
|
|
handleAddPage={handleAddPage}
|
|
/>
|
|
|
|
<SectionDialog
|
|
isSectionDialogOpen={isSectionDialogOpen}
|
|
setIsSectionDialogOpen={setIsSectionDialogOpen}
|
|
newSectionType={newSectionType}
|
|
setNewSectionType={setNewSectionType}
|
|
newSectionTitle={newSectionTitle}
|
|
setNewSectionTitle={setNewSectionTitle}
|
|
newSectionDescription={newSectionDescription}
|
|
setNewSectionDescription={setNewSectionDescription}
|
|
handleAddSection={handleAddSectionWrapper}
|
|
sectionInputMode={sectionInputMode}
|
|
setSectionInputMode={setSectionInputMode}
|
|
sectionTemplates={sectionsAsTemplates}
|
|
selectedTemplateId={selectedSectionTemplateId}
|
|
setSelectedTemplateId={setSelectedSectionTemplateId}
|
|
handleLinkTemplate={handleLinkTemplateWrapper}
|
|
/>
|
|
|
|
{/* 항목 추가/수정 다이얼로그 - 데스크톱 */}
|
|
{!isMobile && (
|
|
<FieldDialog
|
|
isOpen={isFieldDialogOpen}
|
|
onOpenChange={setIsFieldDialogOpen}
|
|
editingFieldId={editingFieldId}
|
|
setEditingFieldId={setEditingFieldId}
|
|
fieldInputMode={fieldInputMode}
|
|
setFieldInputMode={setFieldInputMode}
|
|
showMasterFieldList={showMasterFieldList}
|
|
setShowMasterFieldList={setShowMasterFieldList}
|
|
selectedMasterFieldId={selectedMasterFieldId}
|
|
setSelectedMasterFieldId={setSelectedMasterFieldId}
|
|
textboxColumns={textboxColumns}
|
|
setTextboxColumns={setTextboxColumns}
|
|
newFieldConditionEnabled={newFieldConditionEnabled}
|
|
setNewFieldConditionEnabled={setNewFieldConditionEnabled}
|
|
newFieldConditionTargetType={newFieldConditionTargetType}
|
|
setNewFieldConditionTargetType={setNewFieldConditionTargetType}
|
|
newFieldConditionFields={newFieldConditionFields}
|
|
setNewFieldConditionFields={setNewFieldConditionFields}
|
|
newFieldConditionSections={newFieldConditionSections}
|
|
setNewFieldConditionSections={setNewFieldConditionSections}
|
|
tempConditionValue={tempConditionValue}
|
|
setTempConditionValue={setTempConditionValue}
|
|
newFieldName={newFieldName}
|
|
setNewFieldName={setNewFieldName}
|
|
newFieldKey={newFieldKey}
|
|
setNewFieldKey={setNewFieldKey}
|
|
newFieldInputType={newFieldInputType}
|
|
setNewFieldInputType={setNewFieldInputType}
|
|
newFieldRequired={newFieldRequired}
|
|
setNewFieldRequired={setNewFieldRequired}
|
|
newFieldDescription={newFieldDescription}
|
|
setNewFieldDescription={setNewFieldDescription}
|
|
newFieldOptions={newFieldOptions}
|
|
setNewFieldOptions={setNewFieldOptions as (options: string | string[]) => 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 && (
|
|
<FieldDrawer
|
|
isOpen={isFieldDialogOpen}
|
|
onOpenChange={setIsFieldDialogOpen}
|
|
editingFieldId={editingFieldId}
|
|
setEditingFieldId={setEditingFieldId}
|
|
fieldInputMode={fieldInputMode}
|
|
setFieldInputMode={setFieldInputMode}
|
|
showMasterFieldList={showMasterFieldList}
|
|
setShowMasterFieldList={setShowMasterFieldList}
|
|
selectedMasterFieldId={selectedMasterFieldId}
|
|
setSelectedMasterFieldId={setSelectedMasterFieldId}
|
|
textboxColumns={textboxColumns}
|
|
setTextboxColumns={setTextboxColumns}
|
|
newFieldConditionEnabled={newFieldConditionEnabled}
|
|
setNewFieldConditionEnabled={setNewFieldConditionEnabled}
|
|
newFieldConditionTargetType={newFieldConditionTargetType}
|
|
setNewFieldConditionTargetType={setNewFieldConditionTargetType}
|
|
newFieldConditionFields={newFieldConditionFields}
|
|
setNewFieldConditionFields={setNewFieldConditionFields}
|
|
newFieldConditionSections={newFieldConditionSections}
|
|
setNewFieldConditionSections={setNewFieldConditionSections}
|
|
tempConditionValue={tempConditionValue}
|
|
setTempConditionValue={setTempConditionValue}
|
|
newFieldName={newFieldName}
|
|
setNewFieldName={setNewFieldName}
|
|
newFieldKey={newFieldKey}
|
|
setNewFieldKey={setNewFieldKey}
|
|
newFieldInputType={newFieldInputType}
|
|
setNewFieldInputType={setNewFieldInputType}
|
|
newFieldRequired={newFieldRequired}
|
|
setNewFieldRequired={setNewFieldRequired}
|
|
newFieldDescription={newFieldDescription}
|
|
setNewFieldDescription={setNewFieldDescription}
|
|
newFieldOptions={newFieldOptions}
|
|
setNewFieldOptions={setNewFieldOptions as (options: string | string[]) => 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}
|
|
/>
|
|
)}
|
|
|
|
{/* 텍스트박스 컬럼 추가/수정 다이얼로그 */}
|
|
<ColumnDialog
|
|
isColumnDialogOpen={isColumnDialogOpen}
|
|
setIsColumnDialogOpen={setIsColumnDialogOpen}
|
|
editingColumnId={editingColumnId}
|
|
setEditingColumnId={setEditingColumnId}
|
|
columnName={columnName}
|
|
setColumnName={setColumnName}
|
|
columnKey={columnKey}
|
|
setColumnKey={setColumnKey}
|
|
textboxColumns={textboxColumns}
|
|
setTextboxColumns={setTextboxColumns}
|
|
/>
|
|
|
|
|
|
<MasterFieldDialog
|
|
isMasterFieldDialogOpen={isMasterFieldDialogOpen}
|
|
setIsMasterFieldDialogOpen={setIsMasterFieldDialogOpen}
|
|
editingMasterFieldId={editingMasterFieldId}
|
|
setEditingMasterFieldId={setEditingMasterFieldId}
|
|
newMasterFieldName={newMasterFieldName}
|
|
setNewMasterFieldName={setNewMasterFieldName}
|
|
newMasterFieldKey={newMasterFieldKey}
|
|
setNewMasterFieldKey={setNewMasterFieldKey}
|
|
newMasterFieldInputType={newMasterFieldInputType}
|
|
setNewMasterFieldInputType={setNewMasterFieldInputType}
|
|
newMasterFieldRequired={newMasterFieldRequired}
|
|
setNewMasterFieldRequired={setNewMasterFieldRequired}
|
|
newMasterFieldCategory={newMasterFieldCategory}
|
|
setNewMasterFieldCategory={setNewMasterFieldCategory}
|
|
newMasterFieldDescription={newMasterFieldDescription}
|
|
setNewMasterFieldDescription={setNewMasterFieldDescription}
|
|
newMasterFieldOptions={newMasterFieldOptions}
|
|
setNewMasterFieldOptions={setNewMasterFieldOptions}
|
|
newMasterFieldAttributeType={newMasterFieldAttributeType}
|
|
setNewMasterFieldAttributeType={setNewMasterFieldAttributeType}
|
|
newMasterFieldMultiColumn={newMasterFieldMultiColumn}
|
|
setNewMasterFieldMultiColumn={setNewMasterFieldMultiColumn}
|
|
newMasterFieldColumnCount={newMasterFieldColumnCount}
|
|
setNewMasterFieldColumnCount={setNewMasterFieldColumnCount}
|
|
newMasterFieldColumnNames={newMasterFieldColumnNames}
|
|
setNewMasterFieldColumnNames={setNewMasterFieldColumnNames}
|
|
handleUpdateMasterField={handleUpdateMasterField}
|
|
handleAddMasterField={handleAddMasterField}
|
|
/>
|
|
|
|
|
|
<SectionTemplateDialog
|
|
isSectionTemplateDialogOpen={isSectionTemplateDialogOpen}
|
|
setIsSectionTemplateDialogOpen={setIsSectionTemplateDialogOpen}
|
|
editingSectionTemplateId={editingSectionTemplateId}
|
|
setEditingSectionTemplateId={setEditingSectionTemplateId}
|
|
newSectionTemplateTitle={newSectionTemplateTitle}
|
|
setNewSectionTemplateTitle={setNewSectionTemplateTitle}
|
|
newSectionTemplateDescription={newSectionTemplateDescription}
|
|
setNewSectionTemplateDescription={setNewSectionTemplateDescription}
|
|
newSectionTemplateCategory={newSectionTemplateCategory}
|
|
setNewSectionTemplateCategory={handleSetNewSectionTemplateCategory}
|
|
newSectionTemplateType={newSectionTemplateType}
|
|
setNewSectionTemplateType={setNewSectionTemplateType}
|
|
handleUpdateSectionTemplate={handleUpdateSectionTemplate}
|
|
handleAddSectionTemplate={handleAddSectionTemplate}
|
|
/>
|
|
|
|
|
|
<TemplateFieldDialog
|
|
isTemplateFieldDialogOpen={isTemplateFieldDialogOpen}
|
|
setIsTemplateFieldDialogOpen={setIsTemplateFieldDialogOpen}
|
|
editingTemplateFieldId={editingTemplateFieldId}
|
|
setEditingTemplateFieldId={setEditingTemplateFieldId}
|
|
templateFieldName={templateFieldName}
|
|
setTemplateFieldName={setTemplateFieldName}
|
|
templateFieldKey={templateFieldKey}
|
|
setTemplateFieldKey={setTemplateFieldKey}
|
|
templateFieldInputType={templateFieldInputType}
|
|
setTemplateFieldInputType={setTemplateFieldInputType}
|
|
templateFieldRequired={templateFieldRequired}
|
|
setTemplateFieldRequired={setTemplateFieldRequired}
|
|
templateFieldOptions={templateFieldOptions}
|
|
setTemplateFieldOptions={setTemplateFieldOptions}
|
|
templateFieldDescription={templateFieldDescription}
|
|
setTemplateFieldDescription={setTemplateFieldDescription}
|
|
templateFieldMultiColumn={templateFieldMultiColumn}
|
|
setTemplateFieldMultiColumn={setTemplateFieldMultiColumn}
|
|
templateFieldColumnCount={templateFieldColumnCount}
|
|
setTemplateFieldColumnCount={setTemplateFieldColumnCount}
|
|
templateFieldColumnNames={templateFieldColumnNames}
|
|
setTemplateFieldColumnNames={setTemplateFieldColumnNames}
|
|
handleAddTemplateField={handleAddTemplateField}
|
|
// 마스터 항목 관련 props
|
|
itemMasterFields={itemMasterFields}
|
|
templateFieldInputMode={templateFieldInputMode}
|
|
setTemplateFieldInputMode={setTemplateFieldInputMode}
|
|
showMasterFieldList={templateFieldShowMasterFieldList}
|
|
setShowMasterFieldList={setTemplateFieldShowMasterFieldList}
|
|
selectedMasterFieldId={templateFieldSelectedMasterFieldId}
|
|
setSelectedMasterFieldId={handleSetTemplateFieldSelectedMasterFieldId}
|
|
/>
|
|
|
|
<LoadTemplateDialog
|
|
isLoadTemplateDialogOpen={isLoadTemplateDialogOpen}
|
|
setIsLoadTemplateDialogOpen={setIsLoadTemplateDialogOpen}
|
|
sectionTemplates={sectionTemplates}
|
|
selectedTemplateId={selectedTemplateId}
|
|
setSelectedTemplateId={handleSetSelectedTemplateId}
|
|
handleLoadTemplate={handleLoadTemplateWrapper}
|
|
/>
|
|
|
|
{/* 섹션 불러오기 다이얼로그 */}
|
|
<ImportSectionDialog
|
|
isOpen={isImportSectionDialogOpen}
|
|
setIsOpen={setIsImportSectionDialogOpen}
|
|
independentSections={independentSections}
|
|
selectedSectionId={selectedImportSectionId}
|
|
setSelectedSectionId={setSelectedImportSectionId}
|
|
onImport={handleImportSection}
|
|
onRefresh={refreshIndependentSections}
|
|
onGetUsage={getSectionUsage}
|
|
/>
|
|
|
|
{/* 필드 불러오기 다이얼로그 - 2025-11-27: 탭 통합 (항목+독립필드 → 필드) */}
|
|
<ImportFieldDialog
|
|
isOpen={isImportFieldDialogOpen}
|
|
setIsOpen={setIsImportFieldDialogOpen}
|
|
fields={itemMasterFields}
|
|
selectedFieldId={selectedImportFieldId}
|
|
setSelectedFieldId={setSelectedImportFieldId}
|
|
onImport={handleImportField}
|
|
onRefresh={refreshIndependentFields}
|
|
onGetUsage={getFieldUsage}
|
|
targetSectionTitle={
|
|
importFieldTargetSectionId
|
|
? selectedPage?.sections.find(s => s.id === importFieldTargetSectionId)?.title
|
|
: undefined
|
|
}
|
|
/>
|
|
</PageLayout>
|
|
);
|
|
}
|