/** * 팝업관리 상세 페이지 설정 * IntegratedDetailTemplate V2 마이그레이션 */ import { Megaphone } from 'lucide-react'; import type { DetailConfig, FieldDefinition, SectionDefinition } from '@/components/templates/IntegratedDetailTemplate/types'; import type { Popup, PopupFormData, PopupTarget, PopupStatus } from './types'; import { RichTextEditor } from '@/components/board/RichTextEditor'; import { createElement, useState, useEffect } from 'react'; import { sanitizeHTML } from '@/lib/sanitize'; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select'; import { getDepartmentList } from './actions'; // ===== 대상 옵션 ===== const TARGET_OPTIONS = [ { value: 'all', label: '전사' }, { value: 'department', label: '부서별' }, ]; // ===== 상태 옵션 ===== const STATUS_OPTIONS = [ { value: 'inactive', label: '사용안함' }, { value: 'active', label: '사용함' }, ]; /** * target 값 인코딩/디코딩 헬퍼 * 'all' → target_type: all, target_id: null * 'department:13' → target_type: department, target_id: 13 */ export function encodeTargetValue(targetType: string, departmentId?: number | null): string { if (targetType === 'department' && departmentId) { return `department:${departmentId}`; } return targetType; } export function decodeTargetValue(value: string): { targetType: PopupTarget; departmentId: number | null } { if (value.startsWith('department:')) { const id = parseInt(value.split(':')[1]); return { targetType: 'department', departmentId: isNaN(id) ? null : id }; } if (value === 'department') { return { targetType: 'department', departmentId: null }; } return { targetType: 'all', departmentId: null }; } // ===== 필드 정의 ===== export const popupFields: FieldDefinition[] = [ { key: 'target', label: '대상', type: 'custom', required: true, validation: [ { type: 'custom', message: '대상을 선택해주세요.', validate: (value) => !!value && value !== '', }, { type: 'custom', message: '부서를 선택해주세요.', validate: (value) => { const str = value as string; if (str === 'department') return false; // 부서 미선택 return true; }, }, ], renderField: ({ value, onChange, mode, disabled }) => { const strValue = (value as string) || 'all'; const { targetType, departmentId } = decodeTargetValue(strValue); if (mode === 'view') { // view 모드에서는 formatValue로 처리 return null; } // Edit/Create 모드: 대상 타입 Select + 조건부 부서 Select return createElement(TargetSelectorField, { targetType, departmentId, onChange, disabled: !!disabled, }); }, formatValue: (value) => { // view 모드에서 표시할 텍스트 — 실제 부서명은 PopupDetailClientV2에서 처리 const strValue = (value as string) || 'all'; if (strValue === 'all') return '전사'; if (strValue.startsWith('department:')) return '부서별'; // 부서명은 아래서 덮어씌움 return '부서별'; }, }, { key: 'startDate', label: '시작일', type: 'date', required: true, validation: [ { type: 'required', message: '시작일을 선택해주세요.' }, ], }, { key: 'endDate', label: '종료일', type: 'date', required: true, validation: [ { type: 'required', message: '종료일을 선택해주세요.' }, { type: 'custom', message: '종료일은 시작일 이후여야 합니다.', validate: (value, formData) => { const startDate = formData.startDate as string; const endDate = value as string; if (!startDate || !endDate) return true; return endDate >= startDate; }, }, ], }, { key: 'title', label: '제목', type: 'text', required: true, placeholder: '제목을 입력해주세요', gridSpan: 2, validation: [ { type: 'required', message: '제목을 입력해주세요.' }, ], }, { key: 'content', label: '내용', type: 'custom', required: true, gridSpan: 2, validation: [ { type: 'custom', message: '내용을 입력해주세요.', validate: (value) => { const content = value as string; return !!content && content.trim() !== '' && content !== '
'; }, }, ], renderField: ({ value, onChange, mode, disabled }) => { if (mode === 'view') { return createElement('div', { className: 'border border-gray-200 rounded-md p-4 bg-gray-50 min-h-[100px] prose prose-sm max-w-none', dangerouslySetInnerHTML: { __html: sanitizeHTML((value as string) || '') }, }); } return createElement(RichTextEditor, { value: (value as string) || '', onChange: onChange, placeholder: '내용을 입력해주세요', minHeight: '200px', disabled: disabled, }); }, formatValue: (value) => { if (!value) return '-'; return createElement('div', { className: 'border border-gray-200 rounded-md p-4 bg-gray-50 min-h-[100px] prose prose-sm max-w-none', dangerouslySetInnerHTML: { __html: sanitizeHTML(value as string) }, }); }, }, { key: 'status', label: '상태', type: 'radio', options: STATUS_OPTIONS, defaultValue: 'inactive', }, { key: 'author', label: '작성자', type: 'text', disabled: true, hideInForm: false, }, { key: 'createdAt', label: '등록일시', type: 'text', disabled: true, hideInForm: false, }, ]; // ===== 섹션 정의 ===== export const popupSections: SectionDefinition[] = [ { id: 'basicInfo', title: '팝업 정보', description: '팝업의 기본 정보를 입력해주세요', fields: ['target', 'startDate', 'endDate', 'title', 'content', 'status', 'author', 'createdAt'], }, ]; // ===== 설정 ===== export const popupDetailConfig: DetailConfig