diff --git a/data/games.js b/data/games.js index f7a874f..6fd8730 100644 --- a/data/games.js +++ b/data/games.js @@ -5,7 +5,7 @@ const games = [ updatedAt: '2026-02-11', title: '-KATANA Project CompleteBox- 煌花絢爛', - price: 0, + price: 250000, currency: '₩', category: 'PC Games', status: '미판매', @@ -88,7 +88,7 @@ const games = [ price: 60000, currency: '₩', category: 'Games', - status: '판매완료', + status: '판매중', customTag: '', tags: ['Switch', 'JP'], diff --git a/scripts/config.js b/scripts/config.js index 2063a0d..544df8b 100644 --- a/scripts/config.js +++ b/scripts/config.js @@ -11,7 +11,7 @@ export const STATUS_META = { 미판매: { selectable: false, // 체크박스 선택 불가 isDefaultActive: false, // 초기 로드 시 미체크 상태 - isSystemVisible: false, // 아예 리스트/필터에서 제외 (완전 숨김) + isSystemVisible: true, // 아예 리스트/필터에서 제외 (완전 숨김) soldOut: false, }, 판매예정: { @@ -30,7 +30,7 @@ export const STATUS_META = { selectable: false, isDefaultActive: false, // 초기에는 안 보이지만, 사용자가 필터 클릭하면 보임 isSystemVisible: true, - soldOut: true, // 이미지에 SOLD OUT 표시 + soldOut: true, }, }; diff --git a/scripts/main.js b/scripts/main.js index e1b68bf..cc708bb 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -256,3 +256,59 @@ window.exportToExcel = () => { link.click(); document.body.removeChild(link); }; + +let currentHoverIndex = -1; + +window.handleThumbnailHover = (event, productId) => { + const product = productsData.find((p) => p.id === productId); + if (!product || product.images.length <= 1) return; + + const container = event.currentTarget; + const thumbImg = document.getElementById(`thumb-${productId}`); + const indicator = document.getElementById(`indicator-${productId}`); + + const rect = container.getBoundingClientRect(); + const x = event.clientX - rect.left; + const index = Math.min(Math.floor((x / rect.width) * product.images.length), product.images.length - 1); + + // [핵심] 인덱스가 실제로 바뀔 때만 딱 한 번 실행 + if (currentHoverIndex !== index) { + currentHoverIndex = index; + const targetImgUrl = product.images[index]; + + requestAnimationFrame(() => { + // 이미 프리로드된 상태라면 즉시 교체됨 + thumbImg.style.backgroundImage = `url("${targetImgUrl}")`; + + if (indicator) { + Array.from(indicator.children).forEach((dot, i) => { + if (i === index) { + dot.style.backgroundColor = 'white'; + dot.style.transform = 'scale(1.2)'; + dot.style.opacity = '1'; + } else { + dot.style.backgroundColor = 'rgba(255, 255, 255, 0.4)'; + dot.style.transform = 'scale(1)'; + dot.style.opacity = '0.7'; + } + }); + } + }); + } +}; + +window.handleThumbnailLeave = (productId) => { + currentHoverIndex = -1; // 인덱스 초기화 + const product = productsData.find((p) => p.id === productId); + if (!product) return; + + const thumbImg = document.getElementById(`thumb-${productId}`); + const indicator = document.getElementById(`indicator-${productId}`); + + thumbImg.style.backgroundImage = `url("${product.images[0]}")`; + if (indicator) { + Array.from(indicator.children).forEach((dot, i) => { + dot.style.backgroundColor = i === 0 ? 'white' : 'rgba(255,255,255,0.3)'; + }); + } +}; diff --git a/scripts/productList.js b/scripts/productList.js index 49ec6cb..58acff5 100644 --- a/scripts/productList.js +++ b/scripts/productList.js @@ -24,137 +24,184 @@ export function renderProducts(page = 1) { if (!grid || !tableWrapper) return; - // 1. 결과가 0개인 경우 (그리드/테이블 공통 안내) + // 1. 결과가 0개인 경우 안내 if (state.visibleProducts.length === 0) { - grid.classList.remove('grid', 'hidden'); - grid.innerHTML = ` -
+ grid.classList.remove('grid'); + grid.classList.add('hidden'); + tableWrapper.classList.add('hidden'); + + // 검색 결과 없음 메시지를 표시할 별도의 컨테이너가 없다면 grid 영역을 빌려 씁니다. + const emptyMsg = ` +
search_off

검색 결과가 없습니다

입력하신 검색어나 선택한 필터를 확인해 주세요.

`; - tableWrapper.classList.add('hidden'); + grid.innerHTML = emptyMsg; + grid.classList.remove('hidden'); if (paginationContainer) paginationContainer.innerHTML = ''; return; } - // 2. 뷰 모드에 따른 컨테이너 노출 설정 + // 2. 뷰 모드 설정 및 컨테이너 노출 정리 (hidden/flex/grid 충돌 방지) if (state.viewMode === 'grid') { - // 그리드 활성화 grid.classList.remove('hidden'); grid.classList.add('grid'); - - // 테이블 및 요약바 비활성화 tableWrapper.classList.add('hidden'); if (summaryBar) { - summaryBar.classList.remove('flex'); // flex 제거 + summaryBar.classList.remove('flex'); summaryBar.classList.add('hidden'); } } else { - // 그리드 비활성화 grid.classList.remove('grid'); grid.classList.add('hidden'); - - // 테이블 활성화 tableWrapper.classList.remove('hidden'); - - // 요약바 노출 여부는 데이터 상태에 따라 updateSummary에서 결정 - updateSummary(); + updateSummary(); // 테이블일 때만 요약바 노출 여부 결정 } - // 3. 현재 페이지 데이터 슬라이싱 + // 3. 현재 페이지 데이터 계산 const startIndex = (page - 1) * ITEMS_PER_PAGE; const pagedProducts = state.visibleProducts.slice(startIndex, startIndex + ITEMS_PER_PAGE); if (state.viewMode === 'grid') { grid.innerHTML = ''; pagedProducts.forEach((product) => { - const isSold = STATUS_META[product.status]?.soldOut === true; - grid.insertAdjacentHTML('beforeend', ` -
-
-
-
- - ${product.status} - -
-
-
-
-

${product.title}

-

${product.currency}${product.price.toLocaleString()}

-
-

${product.description}

-
+ // 1. 상태 판별 + const isSold = STATUS_META[product.status]?.soldOut === true; + const isNonSale = product.status === '미판매'; // 상태값이 '미판매'일 때 + + // 2. 스펙(Condition) 정보 추출 + const conditionKey = product.specs?.condition; + const conditionConfig = PRODUCT_CONDITIONS[conditionKey]; + const conditionDisplay = conditionConfig ? conditionConfig.label : (conditionKey || ''); + + grid.insertAdjacentHTML('beforeend', ` +
+ +
+
- `); - }); - } else { - // [테이블 렌더링: 스타일 & 체크박스 제어 추가] - tableBody.innerHTML = pagedProducts - .map((product) => { - const meta = STATUS_META[product.status]; - const isSold = meta?.soldOut === true; - const isSelectable = meta?.selectable !== false; - - // 1. 상태(Condition) 설정 로직 - const conditionKey = product.specs.condition; - const conditionConfig = PRODUCT_CONDITIONS[conditionKey]; - let conditionDisplay = ''; - let conditionClass = 'text-slate-500'; - if (conditionConfig) { - conditionDisplay = conditionConfig.label; - conditionClass = conditionConfig.color; - } else if (conditionKey && conditionKey.trim() !== '') { - conditionDisplay = conditionKey; - } else { - conditionDisplay = '상세 설명 참고 ℹ️'; - conditionClass = 'text-indigo-500 italic'; +
+ + ${product.status} + +
+ + ${!isSold && product.images?.length > 1 ? ` +
+ ${product.images.map((_, i) => ` +
+ `).join('')} +
+ ` : ''} +
+ +
+
+

+ ${product.title} +

+

+ ${isNonSale ? 'Not for Sale' : `₩${product.price.toLocaleString()}`} +

+
+ +
+ ${conditionDisplay ? `${conditionDisplay}` : ''} +

+ ${product.description} +

+
+
+
+ `); +}); + + // 지연 로딩(Lazy Loading) 관찰자 설정 + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const card = entry.target; + const productId = card.getAttribute('data-id'); + const product = state.visibleProducts.find(p => p.id === productId); + const thumb = document.getElementById(`thumb-${productId}`); + + if (product && thumb) { + // 1. 첫 번째 이미지 로드 + thumb.style.backgroundImage = `url("${product.images[0]}")`; + + // 2. 마우스 올리기 전, 나머지 이미지들 백그라운드 프리로드 + if (!STATUS_META[product.status]?.soldOut && product.images.length > 1) { + product.images.slice(1).forEach(url => { + const img = new Image(); + img.src = url; + }); + } + } + observer.unobserve(card); } + }); + }, { threshold: 0.1 }); - // 2. 행 전체 스타일 및 제목 스타일 - const rowClass = isSold ? 'opacity-50 grayscale-[0.5]' : ''; - const titleClass = isSold ? 'line-through text-slate-400' : 'text-slate-900 dark:text-white'; + document.querySelectorAll('.product-card').forEach(card => observer.observe(card)); - return ` - - - - - ${product.title} - ${conditionDisplay} - - ₩${product.price.toLocaleString()} - - - ${product.status} - - `; - }) - .join(''); + } else { + // 테이블 렌더링 + tableBody.innerHTML = pagedProducts.map((product) => { + const meta = STATUS_META[product.status]; + const isSold = meta?.soldOut === true; + const isSelectable = meta?.selectable !== false; + + const conditionKey = product.specs.condition; + const conditionConfig = PRODUCT_CONDITIONS[conditionKey]; + let conditionDisplay = conditionConfig ? conditionConfig.label : (conditionKey || '상세 설명 참고 ℹ️'); + let conditionClass = conditionConfig ? conditionConfig.color : 'text-slate-500'; + + return ` + + + + + ${product.title} + ${conditionDisplay} + + ₩${product.price.toLocaleString()} + + + ${product.status} + + `; + }).join(''); } - // [중요] 전체 선택 체크박스 상태 동기화 + // 전체 선택 체크박스 상태 동기화 const selectAllCheck = document.getElementById('select-all-current'); if (selectAllCheck) { const startIndex = (page - 1) * ITEMS_PER_PAGE; - const currentSelectableItems = state.visibleProducts.slice(startIndex, startIndex + ITEMS_PER_PAGE).filter((p) => STATUS_META[p.status]?.selectable !== false); - + const currentSelectableItems = state.visibleProducts.slice(startIndex, startIndex + ITEMS_PER_PAGE) + .filter((p) => STATUS_META[p.status]?.selectable !== false); selectAllCheck.checked = currentSelectableItems.length > 0 && currentSelectableItems.every((p) => state.selectedIds.has(p.id)); } - renderPagination(); + // 페이지네이션 함수 호출 (이 함수는 외부에 정의되어 있어야 함) + if (typeof renderPagination === 'function') { + renderPagination(); + } } export function renderPagination() {