/** * modal.js * 상품 상세 모달의 제어(열기/닫기), 데이터 렌더링 및 캐러셀 초기화 */ import { productsData } from './state.js'; import { initBetterCarousel } from './carousel.js'; import { TAG_STYLES, TAG_DEFAULT_STYLE, PRODUCT_CONDITIONS } from './config.js'; // ========================================================================== // 1. 모달 메인 제어 (Open / Close) // ========================================================================== /** * 특정 상품의 상세 모달을 오픈 * @param {string} id - 상품 고유 ID */ export function openModal(id) { const product = productsData.find((p) => p.id === id); if (!product) return; const modal = document.getElementById('product-modal'); modal.classList.replace('hidden', 'flex'); // [1] UI 콘텐츠 채우기 renderModalImages(product); renderModalInfo(product); setupCopyLink(product); // [2] 히스토리 및 브라우저 상태 제어 window.history.pushState({ modalOpen: true }, '', ''); document.body.classList.add('modal-open'); // [3] 캐러셀 초기화 const container = document.getElementById('modal-main-carousel-container'); const images = product.images; container.style.scrollBehavior = 'auto'; container.scrollLeft = container.clientWidth; // 루프 캐러셀을 위한 초기 위치 설정 initBetterCarousel(container, images.length); } /** 모달 닫기 및 상태 초기화 */ export function closeModal() { const modal = document.getElementById('product-modal'); // 스크롤 위치 초기화 const carouselContainer = document.getElementById('modal-main-carousel-container'); if (carouselContainer) carouselContainer.scrollLeft = 0; const contentScroll = document.getElementById('modal-content-scroll'); if (contentScroll) contentScroll.scrollTo(0, 0); // UI 닫기 modal.classList.add('hidden'); document.body.classList.remove('modal-open'); // 히스토리 정리 if (window.history.state && window.history.state.modalOpen) { window.history.back(); } const cleanUrl = window.location.origin + window.location.pathname; window.history.replaceState(null, '', cleanUrl); } // ========================================================================== // 2. 상세 데이터 렌더링 (Data Rendering) // ========================================================================== /** 모달 내 이미지 영역(메인, 썸네일, 인디케이터) 생성 */ function renderModalImages(product) { const images = product.images; const loopImages = [images[images.length - 1], ...images, images[0]]; // 메인 캐러셀 HTML const mainImagesHtml = loopImages.map(img => `
`).join(''); // 하단 썸네일 HTML const thumbnailsHtml = images.map((img, idx) => ` `).join(''); // 인디케이터 도트 HTML const dotsHtml = images.map((_, idx) => ` `).join(''); document.getElementById('modal-main-carousel').innerHTML = mainImagesHtml; document.getElementById('modal-thumbnails').innerHTML = thumbnailsHtml; document.getElementById('modal-dots').innerHTML = dotsHtml; } /** 상품 텍스트 정보 및 가격/상태 렌더링 */ function renderModalInfo(product) { document.getElementById('modal-title').textContent = product.title; // 카테고리 const modalCategory = document.getElementById('modal-category'); if (modalCategory) modalCategory.textContent = product.category; // 상품 상태(Status) 뱃지 스타일 const modalStatus = document.getElementById('modal-status'); if (modalStatus) { const statusStyles = { 판매중: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400', 판매예정: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400', 판매완료: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400', 미판매: 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400', }; modalStatus.textContent = product.status; modalStatus.className = 'inline-flex shrink-0 px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-md sm:rounded-lg text-[10px] sm:text-[11px] md:text-xs font-bold uppercase tracking-wider whitespace-nowrap ' + (statusStyles[product.status] || statusStyles['미판매']); } // 커스텀 태그 const customTagElement = document.getElementById('modal-custom-tag'); const tagText = product.customTag?.trim(); if (tagText) { customTagElement.textContent = tagText; customTagElement.classList.remove('hidden'); customTagElement.className = `inline-flex shrink-0 px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-md sm:rounded-lg text-[10px] sm:text-[11px] md:text-xs font-bold uppercase tracking-wider whitespace-nowrap ${TAG_STYLES[tagText] || TAG_DEFAULT_STYLE}`; } else { customTagElement.classList.add('hidden'); } // 상세 스펙 (구매일자, 제품상태) renderSpecs(product); // 가격 표시 로직 renderPrice(product); // 상세 설명 const modalDesc = document.getElementById('modal-desc'); if (modalDesc) { if (product.status === '미판매') { modalDesc.innerHTML = '

판매중인 상품이 아니기에 정보가 제공되지 않습니다.

'; } else { modalDesc.innerHTML = Array.isArray(product.fullDescription) ? product.fullDescription.join('
') : product.fullDescription || ''; } } } /** 상품 스펙 행(Row) 노출 제어 */ function renderSpecs(product) { // 구매일자 const modalDateRow = document.getElementById('modal-date-row'); const pDate = product.specs?.purchaseDate; if (pDate && String(pDate).trim() !== '' && String(pDate) !== 'null') { document.getElementById('modal-date').textContent = pDate; modalDateRow?.classList.replace('hidden', 'flex'); } else { modalDateRow?.classList.replace('flex', 'hidden'); } // 제품 상태(Condition) const conditionKey = product.specs?.condition; const conditionRowWrap = document.getElementById('modal-condition-row')?.parentElement; if (conditionKey) { const conditionLabel = PRODUCT_CONDITIONS[conditionKey]?.label || conditionKey; document.getElementById('modal-condition').textContent = conditionLabel; conditionRowWrap?.classList.replace('hidden', 'flex'); const verifiedIcon = document.getElementById('modal-verified-icon'); if (verifiedIcon) verifiedIcon.classList.toggle('hidden', !product.specs?.isVerified); } else { conditionRowWrap?.classList.replace('flex', 'hidden'); } } /** 가격 정보 렌더링 정책 적용 */ function renderPrice(product) { const priceValueEl = document.getElementById('modal-price'); const priceRowWrap = document.getElementById('modal-price-row')?.parentElement; if (!priceValueEl || !priceRowWrap) return; priceRowWrap.classList.remove('hidden'); if (product.status === '미판매') { priceValueEl.textContent = 'NOT FOR SALE'; priceValueEl.classList.add('text-gray-500'); } else if (product.price === null || product.price === undefined || product.price === '') { priceRowWrap.classList.add('hidden'); } else { const currency = product.currency || '₩'; priceValueEl.textContent = `${currency}${Number(product.price).toLocaleString()}`; priceValueEl.classList.remove('text-gray-500'); } } // ========================================================================== // 3. 유틸리티 및 이벤트 리스너 (Utils) // ========================================================================== /** 상품 공유 링크 복사 로직 설정 */ function setupCopyLink(product) { const copyBtn = document.getElementById('copy-link-btn'); const copyBtnText = document.getElementById('copy-btn-text'); if (!copyBtn) return; copyBtn.onclick = () => { const shareUrl = `${window.location.origin}${window.location.pathname}?id=${product.id}`; navigator.clipboard.writeText(shareUrl).then(() => { if (copyBtnText) copyBtnText.textContent = '링크가 복사되었습니다!'; copyBtn.classList.add('!bg-green-600'); setTimeout(() => { if (copyBtnText) copyBtnText.textContent = '상품 링크 복사하기'; copyBtn.classList.remove('!bg-green-600'); }, 2000); }); }; } /** 뒤로가기(브라우저/제스처) 시 모달 닫기 처리 */ window.addEventListener('popstate', () => { const modal = document.getElementById('product-modal'); if (!modal.classList.contains('hidden')) { modal.classList.add('hidden'); document.body.classList.remove('modal-open'); } });