247 lines
8.6 KiB
TypeScript
247 lines
8.6 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState } from 'react';
|
||
|
|
import { toast } from 'sonner';
|
||
|
|
import { useItemMaster } from '@/contexts/ItemMasterContext';
|
||
|
|
import type { ItemPage, ItemSection, SectionTemplate } from '@/contexts/ItemMasterContext';
|
||
|
|
|
||
|
|
export interface UseSectionManagementReturn {
|
||
|
|
// 상태
|
||
|
|
editingSectionId: number | null;
|
||
|
|
setEditingSectionId: (id: number | null) => void;
|
||
|
|
editingSectionTitle: string;
|
||
|
|
setEditingSectionTitle: (title: string) => void;
|
||
|
|
isSectionDialogOpen: boolean;
|
||
|
|
setIsSectionDialogOpen: (open: boolean) => void;
|
||
|
|
newSectionTitle: string;
|
||
|
|
setNewSectionTitle: (title: string) => void;
|
||
|
|
newSectionDescription: string;
|
||
|
|
setNewSectionDescription: (desc: string) => void;
|
||
|
|
newSectionType: 'fields' | 'bom';
|
||
|
|
setNewSectionType: (type: 'fields' | 'bom') => void;
|
||
|
|
sectionInputMode: 'custom' | 'template';
|
||
|
|
setSectionInputMode: (mode: 'custom' | 'template') => void;
|
||
|
|
selectedSectionTemplateId: number | null;
|
||
|
|
setSelectedSectionTemplateId: (id: number | null) => void;
|
||
|
|
expandedSections: Record<string, boolean>;
|
||
|
|
setExpandedSections: React.Dispatch<React.SetStateAction<Record<string, boolean>>>;
|
||
|
|
|
||
|
|
// 핸들러
|
||
|
|
handleAddSection: (selectedPage: ItemPage | undefined) => void;
|
||
|
|
handleLinkTemplate: (template: SectionTemplate, selectedPage: ItemPage | undefined) => void;
|
||
|
|
handleEditSectionTitle: (sectionId: number, currentTitle: string) => void;
|
||
|
|
handleSaveSectionTitle: (selectedPage: ItemPage | undefined) => void;
|
||
|
|
handleDeleteSection: (pageId: number, sectionId: number) => void;
|
||
|
|
toggleSection: (sectionId: string) => void;
|
||
|
|
resetSectionForm: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useSectionManagement(): UseSectionManagementReturn {
|
||
|
|
const {
|
||
|
|
itemPages,
|
||
|
|
addSectionToPage,
|
||
|
|
updateSection,
|
||
|
|
deleteSection,
|
||
|
|
addSectionTemplate,
|
||
|
|
tenantId,
|
||
|
|
} = useItemMaster();
|
||
|
|
|
||
|
|
// 상태
|
||
|
|
const [editingSectionId, setEditingSectionId] = useState<number | null>(null);
|
||
|
|
const [editingSectionTitle, setEditingSectionTitle] = useState('');
|
||
|
|
const [isSectionDialogOpen, setIsSectionDialogOpen] = useState(false);
|
||
|
|
const [newSectionTitle, setNewSectionTitle] = useState('');
|
||
|
|
const [newSectionDescription, setNewSectionDescription] = useState('');
|
||
|
|
const [newSectionType, setNewSectionType] = useState<'fields' | 'bom'>('fields');
|
||
|
|
const [sectionInputMode, setSectionInputMode] = useState<'custom' | 'template'>('custom');
|
||
|
|
const [selectedSectionTemplateId, setSelectedSectionTemplateId] = useState<number | null>(null);
|
||
|
|
const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({});
|
||
|
|
|
||
|
|
// 섹션 추가
|
||
|
|
const handleAddSection = (selectedPage: ItemPage | undefined) => {
|
||
|
|
if (!selectedPage || !newSectionTitle.trim()) {
|
||
|
|
toast.error('하위섹션 제목을 입력해주세요');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const sectionType: 'BASIC' | 'BOM' | 'CUSTOM' = newSectionType === 'bom' ? 'BOM' : 'BASIC';
|
||
|
|
const newSection: ItemSection = {
|
||
|
|
id: Date.now(),
|
||
|
|
page_id: selectedPage.id,
|
||
|
|
section_name: newSectionTitle,
|
||
|
|
section_type: sectionType,
|
||
|
|
description: newSectionDescription || undefined,
|
||
|
|
order_no: selectedPage.sections.length + 1,
|
||
|
|
is_collapsible: true,
|
||
|
|
is_default_open: true,
|
||
|
|
created_at: new Date().toISOString(),
|
||
|
|
updated_at: new Date().toISOString(),
|
||
|
|
fields: [],
|
||
|
|
bomItems: sectionType === 'BOM' ? [] : undefined
|
||
|
|
};
|
||
|
|
|
||
|
|
console.log('Adding section to page:', {
|
||
|
|
pageId: selectedPage.id,
|
||
|
|
page_name: selectedPage.page_name,
|
||
|
|
sectionTitle: newSection.section_name,
|
||
|
|
sectionType: newSection.section_type,
|
||
|
|
currentSectionCount: selectedPage.sections.length,
|
||
|
|
newSection: newSection
|
||
|
|
});
|
||
|
|
|
||
|
|
// 1. 페이지에 섹션 추가
|
||
|
|
addSectionToPage(selectedPage.id, newSection);
|
||
|
|
|
||
|
|
// 2. 섹션관리 탭에도 템플릿으로 자동 추가
|
||
|
|
const newTemplateData = {
|
||
|
|
tenant_id: tenantId ?? 0,
|
||
|
|
template_name: newSection.section_name,
|
||
|
|
section_type: newSection.section_type as 'BASIC' | 'BOM' | 'CUSTOM',
|
||
|
|
description: newSection.description ?? null,
|
||
|
|
default_fields: null,
|
||
|
|
created_by: null,
|
||
|
|
updated_by: null,
|
||
|
|
};
|
||
|
|
addSectionTemplate(newTemplateData);
|
||
|
|
|
||
|
|
console.log('Section added to both page and template:', {
|
||
|
|
sectionId: newSection.id,
|
||
|
|
templateTitle: newTemplateData.template_name
|
||
|
|
});
|
||
|
|
|
||
|
|
resetSectionForm();
|
||
|
|
toast.success(`${newSectionType === 'bom' ? 'BOM' : '일반'} 섹션이 페이지에 추가되고 템플릿으로도 등록되었습니다!`);
|
||
|
|
};
|
||
|
|
|
||
|
|
// 섹션 템플릿을 페이지에 연결
|
||
|
|
const handleLinkTemplate = (template: SectionTemplate, selectedPage: ItemPage | undefined) => {
|
||
|
|
if (!selectedPage) {
|
||
|
|
toast.error('페이지를 먼저 선택해주세요');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 템플릿을 섹션으로 변환하여 페이지에 추가
|
||
|
|
const newSection: Omit<ItemSection, 'id' | 'created_at' | 'updated_at'> = {
|
||
|
|
page_id: selectedPage.id,
|
||
|
|
section_name: template.template_name,
|
||
|
|
section_type: template.section_type,
|
||
|
|
description: template.description || undefined,
|
||
|
|
order_no: selectedPage.sections.length + 1,
|
||
|
|
is_collapsible: true,
|
||
|
|
is_default_open: true,
|
||
|
|
fields: template.fields ? template.fields.map((field, idx) => ({
|
||
|
|
id: Date.now() + idx,
|
||
|
|
section_id: 0, // 추후 업데이트됨
|
||
|
|
field_name: field.name,
|
||
|
|
field_type: field.property.inputType,
|
||
|
|
order_no: idx + 1,
|
||
|
|
is_required: field.property.required,
|
||
|
|
placeholder: field.description || null,
|
||
|
|
default_value: null,
|
||
|
|
display_condition: null,
|
||
|
|
validation_rules: null,
|
||
|
|
options: field.property.options
|
||
|
|
? field.property.options.map(opt => ({ label: opt, value: opt }))
|
||
|
|
: null,
|
||
|
|
properties: field.property.multiColumn ? {
|
||
|
|
multiColumn: true,
|
||
|
|
columnCount: field.property.columnCount,
|
||
|
|
columnNames: field.property.columnNames
|
||
|
|
} : null,
|
||
|
|
created_at: new Date().toISOString(),
|
||
|
|
updated_at: new Date().toISOString()
|
||
|
|
})) : [],
|
||
|
|
bomItems: template.section_type === 'BOM' ? (template.bomItems || []) : undefined
|
||
|
|
};
|
||
|
|
|
||
|
|
console.log('Linking template to page:', {
|
||
|
|
templateId: template.id,
|
||
|
|
templateName: template.template_name,
|
||
|
|
pageId: selectedPage.id,
|
||
|
|
newSection
|
||
|
|
});
|
||
|
|
|
||
|
|
addSectionToPage(selectedPage.id, newSection);
|
||
|
|
resetSectionForm();
|
||
|
|
toast.success(`"${template.template_name}" 템플릿이 페이지에 연결되었습니다!`);
|
||
|
|
};
|
||
|
|
|
||
|
|
// 섹션 제목 수정 시작
|
||
|
|
const handleEditSectionTitle = (sectionId: number, currentTitle: string) => {
|
||
|
|
setEditingSectionId(sectionId);
|
||
|
|
setEditingSectionTitle(currentTitle);
|
||
|
|
};
|
||
|
|
|
||
|
|
// 섹션 제목 저장
|
||
|
|
const handleSaveSectionTitle = (selectedPage: ItemPage | undefined) => {
|
||
|
|
if (!selectedPage || !editingSectionId || !editingSectionTitle.trim()) {
|
||
|
|
toast.error('하위섹션 제목을 입력해주세요');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
updateSection(editingSectionId, { section_name: editingSectionTitle });
|
||
|
|
setEditingSectionId(null);
|
||
|
|
setEditingSectionTitle('');
|
||
|
|
toast.success('하위섹션 제목이 수정되었습니다 (저장 필요)');
|
||
|
|
};
|
||
|
|
|
||
|
|
// 섹션 삭제
|
||
|
|
const handleDeleteSection = (pageId: number, sectionId: number) => {
|
||
|
|
const page = itemPages.find(p => p.id === pageId);
|
||
|
|
const sectionToDelete = page?.sections.find(s => s.id === sectionId);
|
||
|
|
const fieldIds = sectionToDelete?.fields?.map(f => f.id) || [];
|
||
|
|
|
||
|
|
deleteSection(sectionId);
|
||
|
|
|
||
|
|
console.log('섹션 삭제 완료:', {
|
||
|
|
sectionId,
|
||
|
|
removedFields: fieldIds.length
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// 섹션 확장/축소 토글
|
||
|
|
const toggleSection = (sectionId: string) => {
|
||
|
|
setExpandedSections(prev => ({ ...prev, [sectionId]: !prev[sectionId] }));
|
||
|
|
};
|
||
|
|
|
||
|
|
// 폼 초기화
|
||
|
|
const resetSectionForm = () => {
|
||
|
|
setNewSectionTitle('');
|
||
|
|
setNewSectionDescription('');
|
||
|
|
setNewSectionType('fields');
|
||
|
|
setSectionInputMode('custom');
|
||
|
|
setSelectedSectionTemplateId(null);
|
||
|
|
setIsSectionDialogOpen(false);
|
||
|
|
};
|
||
|
|
|
||
|
|
return {
|
||
|
|
// 상태
|
||
|
|
editingSectionId,
|
||
|
|
setEditingSectionId,
|
||
|
|
editingSectionTitle,
|
||
|
|
setEditingSectionTitle,
|
||
|
|
isSectionDialogOpen,
|
||
|
|
setIsSectionDialogOpen,
|
||
|
|
newSectionTitle,
|
||
|
|
setNewSectionTitle,
|
||
|
|
newSectionDescription,
|
||
|
|
setNewSectionDescription,
|
||
|
|
newSectionType,
|
||
|
|
setNewSectionType,
|
||
|
|
sectionInputMode,
|
||
|
|
setSectionInputMode,
|
||
|
|
selectedSectionTemplateId,
|
||
|
|
setSelectedSectionTemplateId,
|
||
|
|
expandedSections,
|
||
|
|
setExpandedSections,
|
||
|
|
|
||
|
|
// 핸들러
|
||
|
|
handleAddSection,
|
||
|
|
handleLinkTemplate,
|
||
|
|
handleEditSectionTitle,
|
||
|
|
handleSaveSectionTitle,
|
||
|
|
handleDeleteSection,
|
||
|
|
toggleSection,
|
||
|
|
resetSectionForm,
|
||
|
|
};
|
||
|
|
}
|