프리티어 변경
필터링 AND OR 조건 추가 데이터 JSON 구조 변경 오류 수정
This commit is contained in:
14
.prettierrc
14
.prettierrc
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"printWidth": 300,
|
|
||||||
"tabWidth": 4,
|
|
||||||
"useTabs": false,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"bracketSameLine": true,
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"arrowParens": "always",
|
|
||||||
"endOfLine": "auto",
|
|
||||||
"htmlWhitespaceSensitivity": "ignore",
|
|
||||||
"embeddedLanguageFormatting": "auto"
|
|
||||||
}
|
|
||||||
24
.prettierrc.js
Normal file
24
.prettierrc.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/** @type {import("prettier").Config} */
|
||||||
|
module.exports = {
|
||||||
|
printWidth: 300,
|
||||||
|
tabWidth: 4,
|
||||||
|
useTabs: false,
|
||||||
|
semi: true,
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: 'all',
|
||||||
|
bracketSameLine: true,
|
||||||
|
bracketSpacing: true,
|
||||||
|
arrowParens: 'always',
|
||||||
|
endOfLine: 'auto',
|
||||||
|
htmlWhitespaceSensitivity: 'ignore',
|
||||||
|
embeddedLanguageFormatting: 'auto',
|
||||||
|
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['games.js', 'tech.js', 'furniture.js'],
|
||||||
|
options: {
|
||||||
|
printWidth: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
1825
data/games.js
1825
data/games.js
File diff suppressed because it is too large
Load Diff
@@ -37,31 +37,30 @@ export const SEARCH_CONFIG = {
|
|||||||
* isSystemVisible: 필터 목록 및 리스트 노출 여부
|
* isSystemVisible: 필터 목록 및 리스트 노출 여부
|
||||||
* soldOut: 판매 완료 처리 여부 (이미지 그레이스케일 등)
|
* soldOut: 판매 완료 처리 여부 (이미지 그레이스케일 등)
|
||||||
*/
|
*/
|
||||||
|
export const STATUS = {
|
||||||
|
UNLISTED: '미판매',
|
||||||
|
RESERVED: '판매예정',
|
||||||
|
ON_SALE: '판매중',
|
||||||
|
SOLD_OUT: '판매완료',
|
||||||
|
};
|
||||||
|
|
||||||
export const STATUS_META = {
|
export const STATUS_META = {
|
||||||
미판매: {
|
[STATUS.UNLISTED]: { selectable: false, isDefaultActive: false, isSystemVisible: true, soldOut: false },
|
||||||
selectable: false,
|
[STATUS.RESERVED]: { selectable: false, isDefaultActive: false, isSystemVisible: true, soldOut: false },
|
||||||
isDefaultActive: false,
|
[STATUS.ON_SALE]: { selectable: true, isDefaultActive: true, isSystemVisible: true, soldOut: false },
|
||||||
isSystemVisible: true,
|
[STATUS.SOLD_OUT]: { selectable: false, isDefaultActive: false, isSystemVisible: true, soldOut: true },
|
||||||
soldOut: false,
|
};
|
||||||
},
|
|
||||||
판매예정: {
|
export const CATEGORIES = {
|
||||||
selectable: false,
|
GAMES: 'Games',
|
||||||
isDefaultActive: false,
|
TECH: 'Tech',
|
||||||
isSystemVisible: true,
|
FURNITURE: 'Furniture',
|
||||||
soldOut: false,
|
};
|
||||||
},
|
|
||||||
판매중: {
|
export const CURRENCIES = {
|
||||||
selectable: true,
|
KRW: '₩',
|
||||||
isDefaultActive: true,
|
USD: '$',
|
||||||
isSystemVisible: true,
|
JPY: '¥'
|
||||||
soldOut: false,
|
|
||||||
},
|
|
||||||
판매완료: {
|
|
||||||
selectable: false,
|
|
||||||
isDefaultActive: false,
|
|
||||||
isSystemVisible: true,
|
|
||||||
soldOut: true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 필터 칩 표시 순서 정의 */
|
/** 필터 칩 표시 순서 정의 */
|
||||||
@@ -106,15 +105,26 @@ export const TAG_STYLES = {
|
|||||||
export const TAG_DEFAULT_STYLE = 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400';
|
export const TAG_DEFAULT_STYLE = 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400';
|
||||||
|
|
||||||
/** * 상품 상태(Condition) 등급 및 라벨 설정
|
/** * 상품 상태(Condition) 등급 및 라벨 설정
|
||||||
* specs.condition 값과 매칭됩니다.
|
* condition 값과 매칭됩니다.
|
||||||
*/
|
*/
|
||||||
export const PRODUCT_CONDITIONS = {
|
export const CONDITIONS = {
|
||||||
BRAND_NEW: { label: 'Brand New (미개봉)', color: 'text-emerald-600', level: 'S' },
|
BRAND_NEW: 'BRAND_NEW',
|
||||||
LIKE_NEW: { label: 'Like New (단순개봉)', color: 'text-blue-600', level: 'A+' },
|
LIKE_NEW: 'LIKE_NEW',
|
||||||
EXCELLENT: { label: 'Excellent (최상급)', color: 'text-sky-600', level: 'A' },
|
EXCELLENT: 'EXCELLENT',
|
||||||
GOOD: { label: 'Good (보통/사용감)', color: 'text-slate-600', level: 'B' },
|
GOOD: 'GOOD',
|
||||||
INCOMPLETE: { label: 'Incomplete (구성품 누락)', color: 'text-amber-600', level: 'C' },
|
INCOMPLETE: 'INCOMPLETE',
|
||||||
DAMAGED: { label: 'Damaged (하자/파손)', color: 'text-orange-600', level: 'D' },
|
DAMAGED: 'DAMAGED',
|
||||||
JUNK: { label: 'Junk (동작불가/부품용)', color: 'text-red-600', level: 'F' },
|
JUNK: 'JUNK',
|
||||||
OTHER: { label: '기타 (상세설명 참고)', color: 'text-indigo-600', level: '-' },
|
OTHER: 'OTHER',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PRODUCT_CONDITIONS = {
|
||||||
|
[CONDITIONS.BRAND_NEW]: { label: 'Brand New (미개봉)', color: 'text-emerald-600', level: 'S' },
|
||||||
|
[CONDITIONS.LIKE_NEW]: { label: 'Like New (단순개봉)', color: 'text-blue-600', level: 'A+' },
|
||||||
|
[CONDITIONS.EXCELLENT]: { label: 'Excellent (최상급)', color: 'text-sky-600', level: 'A' },
|
||||||
|
[CONDITIONS.GOOD]: { label: 'Good (보통/사용감)', color: 'text-slate-600', level: 'B' },
|
||||||
|
[CONDITIONS.INCOMPLETE]: { label: 'Incomplete (구성품 누락)', color: 'text-amber-600', level: 'C' },
|
||||||
|
[CONDITIONS.DAMAGED]: { label: 'Damaged (하자/파손)', color: 'text-orange-600', level: 'D' },
|
||||||
|
[CONDITIONS.JUNK]: { label: 'Junk (동작불가/부품용)', color: 'text-red-600', level: 'F' },
|
||||||
|
[CONDITIONS.OTHER]: { label: '기타 (상세설명 참고)', color: 'text-indigo-600', level: '-' },
|
||||||
};
|
};
|
||||||
@@ -67,7 +67,7 @@ export function applyFilters() {
|
|||||||
const statusMatch = state.activeStatuses.has(product.status);
|
const statusMatch = state.activeStatuses.has(product.status);
|
||||||
const categoryMatch = state.activeCategories.has('All') || state.activeCategories.has(product.category);
|
const categoryMatch = state.activeCategories.has('All') || state.activeCategories.has(product.category);
|
||||||
const searchMatch = checkSearchMatch(product, keyword);
|
const searchMatch = checkSearchMatch(product, keyword);
|
||||||
const tagMatch = state.activeTags.size === 0 || Array.from(state.activeTags).every((tag) => product.tags && product.tags.includes(tag));
|
const tagMatch = state.activeTags.size === 0 || (state.tagMode === 'AND' ? Array.from(state.activeTags).every((tag) => product.tags?.includes(tag)) : Array.from(state.activeTags).some((tag) => product.tags?.includes(tag)));
|
||||||
|
|
||||||
return statusMatch && categoryMatch && searchMatch && tagMatch;
|
return statusMatch && categoryMatch && searchMatch && tagMatch;
|
||||||
})
|
})
|
||||||
@@ -195,8 +195,15 @@ export function renderTagChips() {
|
|||||||
${hasActive ? 'bg-red-50 text-red-500 border-red-200 hover:bg-red-100' : 'bg-slate-50 text-slate-400 border-slate-200 opacity-60'}" title="태그 초기화">
|
${hasActive ? 'bg-red-50 text-red-500 border-red-200 hover:bg-red-100' : 'bg-slate-50 text-slate-400 border-slate-200 opacity-60'}" title="태그 초기화">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
|
||||||
</button>`;
|
</button>`;
|
||||||
|
const modeBtnHtml = `
|
||||||
|
<button id="tag-mode-btn" class="px-2 py-1 rounded-full text-[10px] font-bold border transition-colors
|
||||||
|
${state.tagMode === 'AND' ? 'bg-indigo-100 text-indigo-600 border-indigo-200' : 'bg-orange-100 text-orange-600 border-orange-200'}">
|
||||||
|
${state.tagMode}
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
container.innerHTML =
|
container.innerHTML =
|
||||||
|
modeBtnHtml +
|
||||||
resetBtnHtml +
|
resetBtnHtml +
|
||||||
sortedTags
|
sortedTags
|
||||||
.map((tag) => {
|
.map((tag) => {
|
||||||
@@ -229,6 +236,11 @@ export function renderTagChips() {
|
|||||||
applyFilters();
|
applyFilters();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
document.getElementById('tag-mode-btn').onclick = () => {
|
||||||
|
state.tagMode = state.tagMode === 'AND' ? 'OR' : 'AND';
|
||||||
|
applyFilters(); // 필터 재적용 및 UI 갱신
|
||||||
|
};
|
||||||
|
|
||||||
container.querySelectorAll('.tag-chip').forEach((chip) => {
|
container.querySelectorAll('.tag-chip').forEach((chip) => {
|
||||||
chip.onclick = () => {
|
chip.onclick = () => {
|
||||||
const tag = chip.dataset.tag;
|
const tag = chip.dataset.tag;
|
||||||
@@ -263,23 +275,42 @@ export function toggleCategory(category) {
|
|||||||
* 로고(타이틀) 클릭 시 모든 필터 초기화
|
* 로고(타이틀) 클릭 시 모든 필터 초기화
|
||||||
*/
|
*/
|
||||||
document.getElementById('logo-title')?.addEventListener('click', () => {
|
document.getElementById('logo-title')?.addEventListener('click', () => {
|
||||||
|
// 1. 기본 상태 초기화
|
||||||
state.searchKeyword = '';
|
state.searchKeyword = '';
|
||||||
|
state.activeTags.clear();
|
||||||
|
state.activeCategories.clear();
|
||||||
|
state.activeCategories.add('All');
|
||||||
|
state.currentPage = 1;
|
||||||
|
|
||||||
|
// 2. [수정] config.js의 명칭(isDefaultActive)에 맞춰 필터 복구
|
||||||
|
state.activeStatuses.clear();
|
||||||
|
|
||||||
|
// STATUS_FILTERS를 순회하며 STATUS_META에 정의된 기본 활성 상태를 추가
|
||||||
|
STATUS_FILTERS.forEach((f) => {
|
||||||
|
// config.js에서 정의한 isDefaultActive 속성을 확인합니다.
|
||||||
|
if (STATUS_META[f.key]?.isDefaultActive) {
|
||||||
|
state.activeStatuses.add(f.key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 만약 config 설정 실수로 아무것도 추가되지 않았다면 '판매중'이라도 강제로 넣음 (안전장치)
|
||||||
|
if (state.activeStatuses.size === 0) {
|
||||||
|
state.activeStatuses.add('판매중');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. UI 리셋
|
||||||
const searchInput = document.getElementById('search-input');
|
const searchInput = document.getElementById('search-input');
|
||||||
if (searchInput) searchInput.value = '';
|
if (searchInput) searchInput.value = '';
|
||||||
|
|
||||||
state.activeCategories.clear();
|
// 4. 데이터 갱신 및 UI 렌더링
|
||||||
state.activeCategories.add('All');
|
|
||||||
|
|
||||||
state.activeStatuses.clear();
|
|
||||||
STATUS_FILTERS.filter((f) => f.defaultActive).forEach((f) => state.activeStatuses.add(f.key));
|
|
||||||
|
|
||||||
state.activeTags.clear();
|
|
||||||
|
|
||||||
applyFilters();
|
applyFilters();
|
||||||
renderStatusChips();
|
renderStatusChips();
|
||||||
renderCategoryChips(productsData);
|
renderCategoryChips(productsData);
|
||||||
|
renderTagChips();
|
||||||
|
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
|
||||||
|
console.log("필터 복구 완료:", Array.from(state.activeStatuses));
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ function renderModalInfo(product) {
|
|||||||
function renderSpecs(product) {
|
function renderSpecs(product) {
|
||||||
// 구매일자
|
// 구매일자
|
||||||
const modalDateRow = document.getElementById('modal-date-row');
|
const modalDateRow = document.getElementById('modal-date-row');
|
||||||
const pDate = product.specs?.purchaseDate;
|
const pDate = product.purchaseDate;
|
||||||
if (pDate && String(pDate).trim() !== '' && String(pDate) !== 'null') {
|
if (pDate && String(pDate).trim() !== '' && String(pDate) !== 'null') {
|
||||||
document.getElementById('modal-date').textContent = pDate;
|
document.getElementById('modal-date').textContent = pDate;
|
||||||
modalDateRow?.classList.replace('hidden', 'flex');
|
modalDateRow?.classList.replace('hidden', 'flex');
|
||||||
@@ -162,7 +162,7 @@ function renderSpecs(product) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 제품 상태(Condition)
|
// 제품 상태(Condition)
|
||||||
const conditionKey = product.specs?.condition;
|
const conditionKey = product.condition;
|
||||||
const conditionRowWrap = document.getElementById('modal-condition-row')?.parentElement;
|
const conditionRowWrap = document.getElementById('modal-condition-row')?.parentElement;
|
||||||
if (conditionKey) {
|
if (conditionKey) {
|
||||||
const conditionLabel = PRODUCT_CONDITIONS[conditionKey]?.label || conditionKey;
|
const conditionLabel = PRODUCT_CONDITIONS[conditionKey]?.label || conditionKey;
|
||||||
@@ -170,7 +170,7 @@ function renderSpecs(product) {
|
|||||||
conditionRowWrap?.classList.replace('hidden', 'flex');
|
conditionRowWrap?.classList.replace('hidden', 'flex');
|
||||||
|
|
||||||
const verifiedIcon = document.getElementById('modal-verified-icon');
|
const verifiedIcon = document.getElementById('modal-verified-icon');
|
||||||
if (verifiedIcon) verifiedIcon.classList.toggle('hidden', !product.specs?.isVerified);
|
if (verifiedIcon) verifiedIcon.classList.toggle('hidden', !product.isVerified);
|
||||||
} else {
|
} else {
|
||||||
conditionRowWrap?.classList.replace('flex', 'hidden');
|
conditionRowWrap?.classList.replace('flex', 'hidden');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,13 +149,17 @@ function renderEmpty(grid, tableWrapper, paginationContainer) {
|
|||||||
|
|
||||||
/** 그리드 뷰 렌더링 */
|
/** 그리드 뷰 렌더링 */
|
||||||
function renderGridView(grid, tableWrapper, products) {
|
function renderGridView(grid, tableWrapper, products) {
|
||||||
grid.classList.replace('hidden', 'grid');
|
// 1. 표시 모드 전환 (테이블 숨기고 그리드 표시)
|
||||||
tableWrapper.classList.add('hidden');
|
tableWrapper.classList.add('hidden');
|
||||||
|
grid.classList.remove('hidden');
|
||||||
|
|
||||||
|
// 2. [핵심] 렌더링 시 그리드 클래스 복구 및 보장
|
||||||
|
grid.classList.add('grid');
|
||||||
|
|
||||||
const summaryBar = document.getElementById('selection-summary');
|
const summaryBar = document.getElementById('selection-summary');
|
||||||
if (summaryBar) summaryBar.classList.add('hidden');
|
if (summaryBar) summaryBar.classList.add('hidden');
|
||||||
|
|
||||||
grid.innerHTML = products.map(product => createProductCardHTML(product)).join('');
|
grid.innerHTML = products.map((product) => createProductCardHTML(product)).join('');
|
||||||
setupLazyLoading();
|
setupLazyLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,10 +190,14 @@ function createProductCardHTML(product) {
|
|||||||
<span class="px-2 py-1 text-[10px] uppercase tracking-wider font-bold rounded ${STATUS_COLOR[product.status]} backdrop-blur-md border">${product.status}</span>
|
<span class="px-2 py-1 text-[10px] uppercase tracking-wider font-bold rounded ${STATUS_COLOR[product.status]} backdrop-blur-md border">${product.status}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${!isSold && product.images?.length > 1 ? `
|
${
|
||||||
|
!isSold && product.images?.length > 1
|
||||||
|
? `
|
||||||
<div id="indicator-${product.id}" class="absolute bottom-3 left-1/2 -translate-x-1/2 hidden md:flex gap-1.5 opacity-0 group-hover:opacity-100 transition-all duration-300 pointer-events-none z-10">
|
<div id="indicator-${product.id}" class="absolute bottom-3 left-1/2 -translate-x-1/2 hidden md:flex gap-1.5 opacity-0 group-hover:opacity-100 transition-all duration-300 pointer-events-none z-10">
|
||||||
${product.images.map((_, i) => `<div class="w-1.5 h-1.5 rounded-full transition-all duration-300 shadow-sm ${i === 0 ? 'bg-white scale-125' : 'bg-white/40'}"></div>`).join('')}
|
${product.images.map((_, i) => `<div class="w-1.5 h-1.5 rounded-full transition-all duration-300 shadow-sm ${i === 0 ? 'bg-white scale-125' : 'bg-white/40'}"></div>`).join('')}
|
||||||
</div>` : ''}
|
</div>`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1.5">
|
<div class="flex flex-col gap-1.5">
|
||||||
@@ -213,7 +221,7 @@ function renderTableView(grid, tableWrapper, products) {
|
|||||||
tableWrapper.classList.remove('hidden');
|
tableWrapper.classList.remove('hidden');
|
||||||
|
|
||||||
const tableBody = document.getElementById('product-table-body');
|
const tableBody = document.getElementById('product-table-body');
|
||||||
tableBody.innerHTML = products.map(product => createTableRowHTML(product)).join('');
|
tableBody.innerHTML = products.map((product) => createTableRowHTML(product)).join('');
|
||||||
updateSummary();
|
updateSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +230,7 @@ function createTableRowHTML(product) {
|
|||||||
const meta = STATUS_META[product.status];
|
const meta = STATUS_META[product.status];
|
||||||
const isSold = meta?.soldOut === true;
|
const isSold = meta?.soldOut === true;
|
||||||
const isSelectable = meta?.selectable !== false;
|
const isSelectable = meta?.selectable !== false;
|
||||||
const conditionConfig = PRODUCT_CONDITIONS[product.specs.condition];
|
const conditionConfig = PRODUCT_CONDITIONS[product.condition];
|
||||||
const conditionDisplay = conditionConfig ? conditionConfig.label : '상세 설명 참고 ℹ️';
|
const conditionDisplay = conditionConfig ? conditionConfig.label : '상세 설명 참고 ℹ️';
|
||||||
const conditionClass = conditionConfig ? conditionConfig.color : 'text-slate-500';
|
const conditionClass = conditionConfig ? conditionConfig.color : 'text-slate-500';
|
||||||
|
|
||||||
@@ -253,17 +261,15 @@ function updateSelectAllCheckbox(page) {
|
|||||||
if (!selectAllCheck) return;
|
if (!selectAllCheck) return;
|
||||||
|
|
||||||
const startIndex = (page - 1) * ITEMS_PER_PAGE;
|
const startIndex = (page - 1) * ITEMS_PER_PAGE;
|
||||||
const currentSelectableItems = state.visibleProducts
|
const currentSelectableItems = state.visibleProducts.slice(startIndex, startIndex + ITEMS_PER_PAGE).filter((p) => STATUS_META[p.status]?.selectable !== false);
|
||||||
.slice(startIndex, startIndex + ITEMS_PER_PAGE)
|
|
||||||
.filter((p) => STATUS_META[p.status]?.selectable !== false);
|
|
||||||
|
|
||||||
selectAllCheck.checked = currentSelectableItems.length > 0 &&
|
selectAllCheck.checked = currentSelectableItems.length > 0 && currentSelectableItems.every((p) => state.selectedIds.has(p.id));
|
||||||
currentSelectableItems.every((p) => state.selectedIds.has(p.id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Intersection Observer를 이용한 썸네일 지연 로딩 */
|
/** Intersection Observer를 이용한 썸네일 지연 로딩 */
|
||||||
function setupLazyLoading() {
|
function setupLazyLoading() {
|
||||||
const observer = new IntersectionObserver((entries) => {
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (entry.isIntersecting) {
|
if (entry.isIntersecting) {
|
||||||
const card = entry.target;
|
const card = entry.target;
|
||||||
@@ -275,13 +281,18 @@ function setupLazyLoading() {
|
|||||||
thumb.style.backgroundImage = `url("${product.images[0]}")`;
|
thumb.style.backgroundImage = `url("${product.images[0]}")`;
|
||||||
// 마우스 호버를 대비해 나머지 이미지 미리 로드
|
// 마우스 호버를 대비해 나머지 이미지 미리 로드
|
||||||
if (!STATUS_META[product.status]?.soldOut && product.images.length > 1) {
|
if (!STATUS_META[product.status]?.soldOut && product.images.length > 1) {
|
||||||
product.images.slice(1).forEach(url => { const img = new Image(); img.src = url; });
|
product.images.slice(1).forEach((url) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
observer.unobserve(card);
|
observer.unobserve(card);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, { threshold: 0.1 });
|
},
|
||||||
|
{ threshold: 0.1 },
|
||||||
|
);
|
||||||
|
|
||||||
document.querySelectorAll('.product-card').forEach((card) => observer.observe(card));
|
document.querySelectorAll('.product-card').forEach((card) => observer.observe(card));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ export const state = {
|
|||||||
/** 화면 보기 모드 ('grid' | 'table') */
|
/** 화면 보기 모드 ('grid' | 'table') */
|
||||||
viewMode: 'grid',
|
viewMode: 'grid',
|
||||||
|
|
||||||
|
/** 태그 필터리 모드 ('AND' | 'OR') */
|
||||||
|
tagMode: 'AND',
|
||||||
|
|
||||||
/** 장바구니/내보내기 등을 위해 선택된 상품 ID 세트 (세션 스토리지 복원) */
|
/** 장바구니/내보내기 등을 위해 선택된 상품 ID 세트 (세션 스토리지 복원) */
|
||||||
selectedIds: new Set(JSON.parse(sessionStorage.getItem('selectedProductIds') || '[]')),
|
selectedIds: new Set(JSON.parse(sessionStorage.getItem('selectedProductIds') || '[]')),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user