Refine resale sorting and cache version

This commit is contained in:
2026-05-21 12:32:42 +09:00
parent 707da3b402
commit 11ec940e48
4 changed files with 37 additions and 77 deletions

View File

@@ -3477,7 +3477,7 @@ export const NSW_RESALE_DB = {
"basisPrice": 2380, "basisPrice": 2380,
"suggestedPrice": 50000 "suggestedPrice": 50000
}, },
"itemCondition": "", "itemCondition": "미개봉 새제품",
"status": "available" "status": "available"
}, },
{ {

View File

@@ -328,7 +328,7 @@
id="sort-button" id="sort-button"
aria-expanded="false" aria-expanded="false"
aria-haspopup="true"> aria-haspopup="true">
<span class="col-start-1 row-start-1 truncate pr-6">순번 최신순</span> <span class="col-start-1 row-start-1 truncate pr-6">무작위</span>
<svg <svg
class="w-4 h-4 transform transition-transform duration-200" class="w-4 h-4 transform transition-transform duration-200"
fill="none" fill="none"
@@ -348,46 +348,6 @@
tabindex="-1" tabindex="-1"
role="listbox" role="listbox"
aria-labelledby="sort-label"> aria-labelledby="sort-label">
<li
class="relative cursor-pointer hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900"
role="option"
data-value="sortByNoDesc">
<span class="block truncate font-semibold">순번 최신순</span>
<span
class="absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600">
<svg
class="size-5"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
data-slot="icon">
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z"
clip-rule="evenodd" />
</svg>
</span>
</li>
<li
class="relative cursor-pointer hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900"
role="option"
data-value="sortByNo">
<span class="block truncate">순번 과거순</span>
<span
class="absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 hidden">
<svg
class="size-5"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
data-slot="icon">
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z"
clip-rule="evenodd" />
</svg>
</span>
</li>
<li <li
class="relative cursor-pointer hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900" class="relative cursor-pointer hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900"
role="option" role="option"
@@ -472,9 +432,9 @@
class="relative cursor-pointer hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900" class="relative cursor-pointer hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900"
role="option" role="option"
data-value="sortByRandom"> data-value="sortByRandom">
<span class="block truncate">무작위</span> <span class="block truncate font-semibold">무작위</span>
<span <span
class="absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600 hidden"> class="absolute inset-y-0 right-0 flex items-center pr-4 text-indigo-600">
<svg <svg
class="size-5" class="size-5"
viewBox="0 0 20 20" viewBox="0 0 20 20"
@@ -535,6 +495,6 @@
</main> </main>
</div> </div>
</div> </div>
<script type="module" src="./script/nsw.js?v=20260521-price-pending"></script> <script type="module" src="./script/nsw.js?v=20260521-badges-v2"></script>
</body> </body>
</html> </html>

View File

@@ -249,7 +249,7 @@ document.addEventListener('DOMContentLoaded', () => {
const gameTitleEl = document.getElementById('gameTitle'); const gameTitleEl = document.getElementById('gameTitle');
gameTitleEl.className = gameTitleEl.className =
'text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl flex items-center justify-center gap-2'; 'text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl flex items-center justify-center gap-2';
gameTitleEl.innerHTML = `${game.sale?.priceVerified ? `${VERIFIED_PRICE_MARK} ` : ''}<span>${game.no}. ${displayTitle}</span>`; gameTitleEl.innerHTML = `${game.sale?.priceVerified ? `${VERIFIED_PRICE_MARK} ` : ''}<span>${displayTitle}</span>`;
document.getElementById('gameTags').textContent = convertTags(game.tags); document.getElementById('gameTags').textContent = convertTags(game.tags);
document.getElementById('infoTitle').textContent = currentTexts.infoTitle; document.getElementById('infoTitle').textContent = currentTexts.infoTitle;
document.getElementById('purchaseTitle').textContent = currentTexts.purchaseTitle; document.getElementById('purchaseTitle').textContent = currentTexts.purchaseTitle;

View File

@@ -1,6 +1,6 @@
import NSW_DB from '../db/nsw.resale.db.js?v=20260521-price-pending'; import NSW_DB from '../db/nsw.resale.db.js?v=20260521-badges-v2';
window.NSW_APP_VERSION = '20260521-price-pending'; window.NSW_APP_VERSION = '20260521-badges-v2';
const SHOW_SOLD_BY_DEFAULT = false; const SHOW_SOLD_BY_DEFAULT = false;
const SHOW_RECOMMENDED_PRICE_RANGE_BY_DEFAULT = false; const SHOW_RECOMMENDED_PRICE_RANGE_BY_DEFAULT = false;
@@ -32,8 +32,6 @@ document.addEventListener('DOMContentLoaded', () => {
status: '판매 상태', status: '판매 상태',
}, },
sortOptions: { sortOptions: {
sortByNoDesc: '순번 최신순',
sortByNo: '순번 과거순',
sortByDateDesc: '발매일 최신순', sortByDateDesc: '발매일 최신순',
sortByDate: '발매일 과거순', sortByDate: '발매일 과거순',
sortByPriceDesc: '판매가 높은순', sortByPriceDesc: '판매가 높은순',
@@ -62,8 +60,6 @@ document.addEventListener('DOMContentLoaded', () => {
status: '販売状態', status: '販売状態',
}, },
sortOptions: { sortOptions: {
sortByNoDesc: '番号降順',
sortByNo: '番号昇順',
sortByDateDesc: '発売日降順', sortByDateDesc: '発売日降順',
sortByDate: '発売日昇順', sortByDate: '発売日昇順',
sortByPriceDesc: '販売価格降順', sortByPriceDesc: '販売価格降順',
@@ -88,7 +84,7 @@ document.addEventListener('DOMContentLoaded', () => {
cero: [], cero: [],
}, },
searchText: '', searchText: '',
sortBy: 'sortByNoDesc', // 기본 정렬 옵션 sortBy: 'sortByRandom',
}; };
// UI 텍스트 업데이트 함수 // UI 텍스트 업데이트 함수
@@ -99,26 +95,30 @@ document.addEventListener('DOMContentLoaded', () => {
document.querySelector('h1').textContent = texts.title; document.querySelector('h1').textContent = texts.title;
// 언어 선택 섹션 업데이트 // 언어 선택 섹션 업데이트
document.querySelector('label.text-base').textContent = texts.languageSelect; setTextContent('label.text-base', texts.languageSelect);
document.querySelector('p.text-sm').textContent = texts.languageDescription; setTextContent('p.text-sm', texts.languageDescription);
document.querySelector('label[for="ko"]').textContent = texts.korean; setTextContent('label[for="ko"]', texts.korean);
document.querySelector('label[for="ja"]').textContent = texts.japanese; setTextContent('label[for="ja"]', texts.japanese);
// 테이블 헤더 업데이트 // 테이블 헤더 업데이트
const headers = document.querySelectorAll('th'); const headers = document.querySelectorAll('th');
headers[0].textContent = texts.tableHeaders.title; if (headers[0]) headers[0].textContent = texts.tableHeaders.title;
headers[1].textContent = texts.tableHeaders.info; if (headers[1]) headers[1].textContent = texts.tableHeaders.info;
headers[2].textContent = texts.tableHeaders.status; if (headers[2]) headers[2].textContent = texts.tableHeaders.status;
// 로딩 텍스트 업데이트 // 로딩 텍스트 업데이트
loading.textContent = texts.loading; loading.textContent = texts.loading;
document.getElementById('priceProgressNotice').textContent = texts.listIntro; setTextContent('#priceProgressNotice', texts.listIntro);
// 필터 텍스트 업데이트 // 필터 텍스트 업데이트
document.getElementById('resetFilters').textContent = texts.filter.reset; setTextContent('#resetFilters', texts.filter.reset);
document.querySelector('label[for="korean-support"]').textContent = texts.filter.koreanSupport; setTextContent('label[for="korean-support"]', texts.filter.koreanSupport);
document.querySelector('label[for="korean-not-support"]').textContent = setTextContent('label[for="korean-not-support"]', texts.filter.koreanNotSupport);
texts.filter.koreanNotSupport; }
function setTextContent(selector, text) {
const element = document.querySelector(selector);
if (element) element.textContent = text;
} }
// 언어 변경 이벤트 리스너 // 언어 변경 이벤트 리스너
@@ -777,7 +777,7 @@ document.addEventListener('DOMContentLoaded', () => {
</div> </div>
<div class="ml-4 min-w-0 flex-1"> <div class="ml-4 min-w-0 flex-1">
<div class="font-medium leading-5 text-gray-900 flex items-center gap-1"> <div class="font-medium leading-5 text-gray-900 flex items-center gap-1">
${game.sale?.priceVerified ? `${VERIFIED_PRICE_MARK} ` : ''}<span>${game.no}. ${game.formattedTitle}</span> ${game.sale?.priceVerified ? `${VERIFIED_PRICE_MARK} ` : ''}<span>${game.formattedTitle}</span>
</div> </div>
<div class="mt-2"> <div class="mt-2">
${renderInfoBadges(game)} ${renderInfoBadges(game)}
@@ -853,7 +853,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
// 필터 초기화 버튼 이벤트 리스너 설정 // 필터 초기화 버튼 이벤트 리스너 설정
document.getElementById('resetFilters').addEventListener('click', resetFilters); document.getElementById('resetFilters')?.addEventListener('click', resetFilters);
// 필터 체크박스 이벤트 리스너 설정 // 필터 체크박스 이벤트 리스너 설정
function setupFilterListeners() { function setupFilterListeners() {
@@ -1100,43 +1100,39 @@ document.addEventListener('DOMContentLoaded', () => {
// 정렬 함수 // 정렬 함수
function sortGames(games) { function sortGames(games) {
switch (filterState.sortBy) { switch (filterState.sortBy) {
case 'sortByNoDesc':
return [...games].sort((a, b) => b.no - a.no);
case 'sortByNo':
return [...games].sort((a, b) => a.no - b.no);
case 'sortByDateDesc': case 'sortByDateDesc':
return [...games].sort((a, b) => { return [...games].sort((a, b) => {
const aDate = new Date(a.release.replace(/年|月/g, '/').replace(/日/g, '')).toUTCString(); const aDate = new Date(a.release.replace(/年|月/g, '/').replace(/日/g, '')).toUTCString();
const bDate = new Date(b.release.replace(/年|月/g, '/').replace(/日/g, '')).toUTCString(); const bDate = new Date(b.release.replace(/年|月/g, '/').replace(/日/g, '')).toUTCString();
const dateDiff = new Date(bDate) - new Date(aDate); const dateDiff = new Date(bDate) - new Date(aDate);
return dateDiff === 0 ? b.no - a.no : dateDiff; return dateDiff === 0 ? compareTitles(a, b) : dateDiff;
}); });
case 'sortByDate': case 'sortByDate':
return [...games].sort((a, b) => { return [...games].sort((a, b) => {
const aDate = new Date(a.release.replace(/年|月/g, '/').replace(/日/g, '')).toUTCString(); const aDate = new Date(a.release.replace(/年|月/g, '/').replace(/日/g, '')).toUTCString();
const bDate = new Date(b.release.replace(/年|月/g, '/').replace(/日/g, '')).toUTCString(); const bDate = new Date(b.release.replace(/年|月/g, '/').replace(/日/g, '')).toUTCString();
const dateDiff = new Date(aDate) - new Date(bDate); const dateDiff = new Date(aDate) - new Date(bDate);
return dateDiff === 0 ? b.no - a.no : dateDiff; return dateDiff === 0 ? compareTitles(a, b) : dateDiff;
}); });
case 'sortByPriceDesc': case 'sortByPriceDesc':
return [...games].sort((a, b) => { return [...games].sort((a, b) => {
const aPrice = getSuggestedPriceValue(a); const aPrice = getSuggestedPriceValue(a);
const bPrice = getSuggestedPriceValue(b); const bPrice = getSuggestedPriceValue(b);
if (aPrice === null && bPrice === null) return b.no - a.no; if (aPrice === null && bPrice === null) return compareTitles(a, b);
if (aPrice === null) return 1; if (aPrice === null) return 1;
if (bPrice === null) return -1; if (bPrice === null) return -1;
const priceDiff = bPrice - aPrice; const priceDiff = bPrice - aPrice;
return priceDiff === 0 ? b.no - a.no : priceDiff; return priceDiff === 0 ? compareTitles(a, b) : priceDiff;
}); });
case 'sortByPrice': case 'sortByPrice':
return [...games].sort((a, b) => { return [...games].sort((a, b) => {
const aPrice = getSuggestedPriceValue(a); const aPrice = getSuggestedPriceValue(a);
const bPrice = getSuggestedPriceValue(b); const bPrice = getSuggestedPriceValue(b);
if (aPrice === null && bPrice === null) return b.no - a.no; if (aPrice === null && bPrice === null) return compareTitles(a, b);
if (aPrice === null) return 1; if (aPrice === null) return 1;
if (bPrice === null) return -1; if (bPrice === null) return -1;
const priceDiff = aPrice - bPrice; const priceDiff = aPrice - bPrice;
return priceDiff === 0 ? b.no - a.no : priceDiff; return priceDiff === 0 ? compareTitles(a, b) : priceDiff;
}); });
case 'sortByRandom': case 'sortByRandom':
return [...games].sort(() => Math.random() - 0.5); return [...games].sort(() => Math.random() - 0.5);
@@ -1145,6 +1141,10 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
function compareTitles(a, b) {
return a.formattedTitle.localeCompare(b.formattedTitle, filterState.language === 'ko' ? 'ko' : 'ja');
}
// 정렬 UI 초기화 // 정렬 UI 초기화
function setupSortUI() { function setupSortUI() {
const sortButton = document.getElementById('sort-button'); const sortButton = document.getElementById('sort-button');