import React, { useState, useEffect, Fragment } from 'react'; import styled, { css, keyframes } from 'styled-components'; import { Title, SelectInput, BtnWrapper, TextInput, Label, InputLabel, Textarea, SearchBarAlert, ButtonGroupWrapper, } from '../../styles/Components'; import Button from '../common/button/Button'; import Modal from '../common/modal/Modal'; import { EventIsItem, EventModify, MenuBannerModify } from '../../apis'; import { authList } from '../../store/authList'; import { useRecoilValue } from 'recoil'; import { useTranslation } from 'react-i18next'; import { authType, benItems, commonStatus, currencyItemCode } from '../../assets/data'; import { DetailInputItem, DetailInputRow, DetailModalWrapper, RegistGroup, DetailRegistInfo, DetailState, FormRowGroup, FormLabel, FormInput, } from '../../styles/ModuleComponents'; import { convertKTC, combineDateTime, timeDiffMinute, convertKTCDate } from '../../utils'; import DateTimeInput from '../common/input/DateTimeInput'; import { useLoading } from '../../context/LoadingProvider'; import { useAlert } from '../../context/AlertProvider'; import { alertTypes, battleEventStatusType, languageNames } from '../../assets/data/types'; import { Tabs, Image as AntImage, Spin } from 'antd'; import { TYPE_MODIFY, TYPE_REGISTRY } from '../../assets/data/adminConstants'; import { AntButton, DateTimeRangePicker, DetailLayout, SingleTimePicker } from '../common'; import AnimatedTabs from '../common/control/AnimatedTabs'; function renderImageContent(imageData) { if (!imageData) { return 이미지가 없습니다; } return ( <> } // preview={{ // mask: '미리보기', // maskClassName: 'custom-mask', // }} fallback="data:image/png;base64,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" /> {imageData.content && {imageData.content} } > ); } const MenuBannerDetailModal = ({ detailView, handleDetailView, content, setDetailData }) => { const userInfo = useRecoilValue(authList); const { t } = useTranslation(); const token = sessionStorage.getItem('token'); const {withLoading} = useLoading(); const {showModal, showToast} = useAlert(); const id = content && content.id; const updateAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.menuBannerUpdate); const [time, setTime] = useState({ start_hour: '00', start_min: '00', end_hour: '00', end_min: '00', }); //시간 정보 const [resultData, setResultData] = useState(initData); const [activeLanguage, setActiveLanguage] = useState('KO'); // 이미지 프리로드를 위한 상태 const [allImagesLoaded, setAllImagesLoaded] = useState(false); const [showTabContent, setShowTabContent] = useState(false); const [loadedImages, setLoadedImages] = useState([]); const [totalImageCount, setTotalImageCount] = useState(0); const [tabItems, setTabItems] = useState([]); useEffect(() => { if(content){ console.log(content); const start_dt_KTC = convertKTCDate(content.start_dt); const end_dt_KTC = convertKTCDate(content.end_dt); setResultData({ id: content.id, title: content.title, start_dt: start_dt_KTC, end_dt: end_dt_KTC, status: content.status, order_id: content.order_id, is_link: content.is_link, image_list: content.image_list, }); setTime({ ...time, start_hour: String(start_dt_KTC.getHours()).padStart(2, '0'), start_min: String(start_dt_KTC.getMinutes()).padStart(2, '0'), end_hour: String(end_dt_KTC.getHours()).padStart(2, '0'), end_min: String(end_dt_KTC.getMinutes()).padStart(2, '0') }); } }, [content]); useEffect(() => { if (content && content.image_list) { // 초기화 setAllImagesLoaded(false); setShowTabContent(false); setLoadedImages([]); // 이미지 개수 설정 setTotalImageCount(content.image_list ? content.image_list.length : 0); // 첫 번째 언어를 활성 언어로 설정 if (content.image_list && content.image_list.length > 0) { setActiveLanguage(content.image_list[0].language); } // 동적으로 탭 아이템 생성 const newTabItems = content.image_list ? content.image_list.map(imageData => ({ key: imageData.language, label: languageNames[imageData.language] || imageData.language, children: ( {renderImageContent(imageData)} ) })) : []; setTabItems(newTabItems); // 모든 이미지 프리로딩 시작 setTimeout(() => { preloadAllImages(); }, 100); } }, [content]); const preloadAllImages = () => { if (!content || !content.image_list || content.image_list.length === 0) { // 이미지가 없는 경우 바로 로딩 완료 처리 // console.log('이미지가 없습니다. 로딩 완료 처리합니다.'); setAllImagesLoaded(true); setShowTabContent(true); return; } // console.log(`총 ${content.image_list.length}개의 이미지 로딩을 시작합니다.`); // 이미지 개수가 0이면 로딩 완료 처리 if (content.image_list.length === 0) { setAllImagesLoaded(true); setShowTabContent(true); return; } let loadedCount = 0; // 이미지 로드 완료 이벤트 핸들러 const handleImageLoad = (url) => { loadedCount++; // console.log(`이미지 로드 완료 (${loadedCount}/${content.image_list.length}): ${url}`); // 모든 이미지가 로드되었는지 확인 if (loadedCount >= content.image_list.length) { // console.log('모든 이미지 로딩 완료!'); setAllImagesLoaded(true); setShowTabContent(true); } }; // 각 이미지에 대해 프리로드 객체 생성 content.image_list.forEach(img => { if (img.title) { // console.log(`이미지 로딩 시작: ${img.title}`); const image = new Image(); image.onload = () => handleImageLoad(img.title); image.onerror = () => { console.log(`이미지 로드 실패: ${img.title}`); handleImageLoad(img.title); // 오류 시에도 카운트 }; image.src = img.title; // src 속성은 onload/onerror 핸들러 설정 후에 설정 } else { // console.log('이미지 URL이 없습니다.'); handleImageLoad('empty'); // URL이 없는 경우에도 카운트 } }); // 안전장치: 5초 후에도 로딩이 완료되지 않으면 강제로 완료 처리 setTimeout(() => { if (!allImagesLoaded) { // console.log('시간 초과로 로딩 강제 완료'); setAllImagesLoaded(true); setShowTabContent(true); } }, 5000); }; // 날짜 처리 // const handleDateChange = (data, type) => { // const date = new Date(data); // setResultData({ // ...resultData, // [`${type}_dt`]: combineDateTime(date, time[`${type}_hour`], time[`${type}_min`]), // }); // }; // 시간 처리 const handleTimeChange = (e, type) => { const { id, value } = e.target; const newTime = { ...time, [`${type}_${id}`]: value }; setTime(newTime); const date = resultData[`${type}_dt`] ? new Date(resultData[`${type}_dt`]) : new Date(); setResultData({ ...resultData, [`${type}_dt`]: combineDateTime(date, newTime[`${type}_hour`], newTime[`${type}_min`]), }); }; const handleDateChange = { start: (date) => { setResultData(prev => ({ ...prev, start_dt: date })); }, end: (date) => { setResultData(prev => ({ ...prev, end_dt: date })); } }; // 확인 버튼 후 다 초기화 const handleReset = () => { }; const checkCondition = () => { return ( (resultData.start_dt.length !== 0) && (resultData.end_dt.length !== 0) && resultData.title !== '' && resultData.order_id !== '' ); }; // 탭 변경 핸들러 const handleTabChange = (key) => { setActiveLanguage(key); }; const handleSubmit = async (type, param = null) => { switch (type) { case "submit": if (!checkCondition()) return; showModal('MENU_BANNER_UPDATE_SAVE', { type: alertTypes.confirm, onConfirm: () => handleSubmit('updateConfirm') }); break; case "updateConfirm": withLoading( async () => { return await MenuBannerModify(token, id, resultData); }).catch(error => { showToast('API_FAIL', {type: alertTypes.error}); }).finally(() => { showToast('UPDATE_COMPLETED', {type: alertTypes.success}); handleDetailView(); }); break; } } const detailState = (status) => { switch (status) { case commonStatus.wait: return 대기; case commonStatus.running: return 진행중; case commonStatus.finish: return 만료; case commonStatus.fail: return 실패; case commonStatus.delete: return 삭제; default: return null; } }; //true 수정불가, false 수정가능 const isView = (fieldName) => { if (!updateAuth) return false; if (fieldName === 'editButton') { // updateAuth가 없거나 FINISH 상태면 수정 버튼 숨김 (false 반환) return !updateAuth || content?.status === commonStatus.finish; } switch (content?.status) { case commonStatus.running: // RUNNING 상태일 때는 end_dt와 order_id만 수정 가능 return fieldName !== 'date' && fieldName !== 'order_id'; case commonStatus.wait: return true; default: return false; } } const itemGroups = [ { items: [ { row: 0, col: 0, colSpan: 2, type: 'text', key: 'title', label: '제목', disabled: !isView('title'), width: '250px', }, { row: 0, col: 2, colSpan: 2, type: 'number', key: 'order_id', label: '순서', disabled: !isView('order_id'), width: '100px', min: 0, }, { row: 1, col: 0, colSpan: 2, type: 'status', key: 'status', label: '상태', value: resultData.status, }, { row: 2, col: 0, colSpan: 2, type: 'dateRange', key: 'dateRange', keys: {start: 'start_dt', end: 'end_dt'}, label: '기간', disabled: !isView('date'), format: 'YYYY-MM-DD HH:mm' }, ] } ]; return ( <> 배너 상세 정보 {/**/} {/* {content &&*/} {/* */} {/* */} {/* */} {/* 제목*/} {/* setResultData({ ...resultData, title: e.target.value })}*/} {/* width="300px"*/} {/* />*/} {/* */} {/* */} {/* 순서*/} {/* setResultData({ ...resultData, order_id: e.target.value })}*/} {/* width="200px"*/} {/* />*/} {/* */} {/* */} {/* */} {/* */} {/* */} {/* */} {/* */} {/* 상태*/} {/* {detailState(content.status)}*/} {/* */} {/* */} {/* {content.image_list && content.image_list.length > 0 && (*/} {/* */} {/* */} {/* {!showTabContent ? (*/} {/* */} {/* */} {/* */} {/* ) : (*/} {/* */} {/* */} {/* */} {/* )}*/} {/* */} {/* */} {/* )}*/} {/* */} {/* }*/} {/**/} { handleDetailView(); handleReset(); setDetailData(''); }} /> {!isView('editButton') && ( handleSubmit('submit')} /> )} > ); }; export default MenuBannerDetailModal; const initData = { title: '', is_link: false, start_dt: '', end_dt: '', image_list: [ { language: 'KO', content: '', title: '' }, { language: 'EN', content: '', title: '' }, { language: 'JA', content: '', title: '' }, ] } const StyledTabs = styled(Tabs)` margin-top: 20px; width: 100%; display: flex; flex-direction: column; align-items: center; .ant-tabs-nav { margin-bottom: 16px; width: 80%; } .ant-tabs-nav-wrap { justify-content: center; } .ant-tabs-tab { padding: 8px 16px; font-size: 14px; font-weight: 500; transition: all 0.3s; } .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn { color: #1890ff; font-weight: 600; } .ant-tabs-ink-bar { background-color: #1890ff; } .ant-tabs-content-holder { width: 100%; } `; const ImageContainer = styled.div` padding: 16px; display: flex; flex-direction: column; align-items: center; gap: 16px; width: 100%; max-width: 700px; margin: 0 auto; `; const ImageWrapper = styled.div` position: relative; border-radius: 8px; overflow: hidden; border: 1px solid #f0f0f0; width: 100%; text-align: center; `; const ImageUrlInfo = styled.div` margin-top: 16px; padding: 12px; background-color: #f5f5f5; border-radius: 6px; font-size: 14px; word-break: break-all; width: 100%; `; const UrlLink = styled.a` color: #1890ff; text-decoration: underline; cursor: pointer; &:hover { opacity: 0.8; } `; const NoImagePlaceholder = styled.div` width: 100%; height: 200px; display: flex; align-items: center; justify-content: center; background-color: #f0f2f5; color: #8c8c8c; border-radius: 8px; `; // 로딩 인디케이터를 위한 컨테이너 const LoadingContainer = styled.div` display: flex; justify-content: center; align-items: center; height: 300px; width: 100%; `; // 컨텐츠 래퍼 - 로딩 상태에 따라 가시성 설정 const ContentWrapper = styled.div` width: 100%; opacity: ${props => props.$isLoaded ? 1 : 0}; transition: opacity 0.3s ease-in-out; height: ${props => props.$isLoaded ? 'auto' : '0'}; overflow: hidden; `;