Fix mobile search and update resale prices

This commit is contained in:
2026-05-19 18:50:37 +09:00
parent 37926e9d7f
commit 908fd1035c
3 changed files with 187 additions and 27 deletions

View File

@@ -2416,7 +2416,7 @@ export const NSW_RESALE_DB = {
"price": 1480,
"sale": {
"currency": "KRW",
"suggestedPrice": 12000,
"suggestedPrice": 30000,
"priceRange": {
"min": 10000,
"max": 14000
@@ -2426,7 +2426,7 @@ export const NSW_RESALE_DB = {
"confidence": "low",
"memo": "한국 중고가 우선, 국내 시세가 약한/없는 일본판·희소 타이틀은 일본 중고가를 원화 환산한 대략 판매가. 상태/구성품/특전/케이스 상태에 따라 조정 필요.",
"checkedAt": "2026-05-19",
"priceVerified": false
"priceVerified": true
},
"itemCondition": "-",
"status": "available"
@@ -2606,7 +2606,7 @@ export const NSW_RESALE_DB = {
"price": 6578,
"sale": {
"currency": "KRW",
"suggestedPrice": 28000,
"suggestedPrice": 40000,
"priceRange": {
"min": 24000,
"max": 32000
@@ -2616,7 +2616,7 @@ export const NSW_RESALE_DB = {
"confidence": "low",
"memo": "한국 중고가 우선, 국내 시세가 약한/없는 일본판·희소 타이틀은 일본 중고가를 원화 환산한 대략 판매가. 상태/구성품/특전/케이스 상태에 따라 조정 필요.",
"checkedAt": "2026-05-19",
"priceVerified": false
"priceVerified": true
},
"itemCondition": "-",
"status": "available"
@@ -5456,7 +5456,7 @@ export const NSW_RESALE_DB = {
"price": 2480,
"sale": {
"currency": "KRW",
"suggestedPrice": 12000,
"suggestedPrice": 25000,
"priceRange": {
"min": 10000,
"max": 14000
@@ -6710,7 +6710,7 @@ export const NSW_RESALE_DB = {
"price": 9172,
"sale": {
"currency": "KRW",
"suggestedPrice": 41000,
"suggestedPrice": 90000,
"priceRange": {
"min": 35000,
"max": 47000
@@ -6720,7 +6720,7 @@ export const NSW_RESALE_DB = {
"confidence": "medium-low",
"memo": "한국 중고가 우선, 국내 시세가 약한/없는 일본판·희소 타이틀은 일본 중고가를 원화 환산한 대략 판매가. 상태/구성품/특전/케이스 상태에 따라 조정 필요.",
"checkedAt": "2026-05-19",
"priceVerified": false
"priceVerified": true
},
"itemCondition": "-",
"status": "available"

View File

@@ -15,6 +15,46 @@
</div>
<section aria-labelledby="products-heading" class="pt-6 pb-12">
<div id="mobileSearchForm" class="mb-4 lg:hidden">
<label
for="mobile-search-input"
class="block text-sm font-medium leading-6 text-gray-900">
직접 검색
</label>
<div class="relative mt-2 flex rounded-md shadow-sm">
<span
class="inline-flex items-center rounded-l-md border border-r-0 border-gray-300 px-3 text-gray-500 sm:text-sm">
게임명
</span>
<input
type="text"
id="mobile-search-input"
data-search-input
enterkeyhint="search"
autocomplete="off"
oninput="window.updateNswSearch && window.updateNswSearch(this.value)"
onchange="window.updateNswSearch && window.updateNswSearch(this.value)"
class="block w-full min-w-0 flex-1 rounded-none rounded-r-md px-2 py-2 pr-9 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder=" " />
<button
type="button"
class="absolute inset-y-0 right-0 z-10 flex items-center pr-3"
id="mobile-reset-search"
aria-label="검색어 지우기"
onclick="window.clearNswSearch && window.clearNswSearch()">
<svg
class="h-5 w-5 text-gray-400"
viewBox="0 0 20 20"
fill="currentColor">
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z"
clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
<div class="grid grid-cols-1 gap-x-8 gap-y-2 lg:grid-cols-4">
<!-- 필터 섹션 -->
<div class="lg:block">
@@ -78,7 +118,7 @@
<div class="flex items-center justify-between">
<div class="w-full">
<form id="searchForm" class="mb-4">
<form id="searchForm" class="mb-4" onsubmit="return false">
<label
for="search-input"
class="block text-sm font-medium leading-6 text-gray-900">
@@ -91,13 +131,18 @@
</span>
<input
type="text"
name="search-input"
id="search-input"
class="block w-full px-2 min-w-0 flex-1 rounded-none rounded-r-md py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
data-search-input
enterkeyhint="search"
autocomplete="off"
oninput="window.updateNswSearch && window.updateNswSearch(this.value)"
onchange="window.updateNswSearch && window.updateNswSearch(this.value)"
class="block w-full px-2 min-w-0 flex-1 rounded-none rounded-r-md py-1.5 pr-9 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder=" " />
<div
class="cursor-pointer absolute inset-y-0 right-0 flex items-center pr-3"
id="reset-search">
class="cursor-pointer absolute inset-y-0 right-0 z-10 flex items-center pr-3"
id="reset-search"
onclick="window.clearNswSearch && window.clearNswSearch()">
<svg
class="h-5 w-5 text-gray-400"
viewBox="0 0 20 20"
@@ -497,6 +542,6 @@
</main>
</div>
</div>
<script type="module" src="./script/nsw.js"></script>
<script type="module" src="./script/nsw.js?v=20260519-mobile-search"></script>
</body>
</html>

View File

@@ -1,5 +1,7 @@
import NSW_DB from '../db/nsw.resale.db.js';
window.NSW_APP_VERSION = '20260519-mobile-search';
const SHOW_SOLD_BY_DEFAULT = false;
const SHOW_RECOMMENDED_PRICE_RANGE_BY_DEFAULT = false;
@@ -816,7 +818,9 @@ document.addEventListener('DOMContentLoaded', () => {
cero: [],
};
filterState.searchText = '';
document.getElementById('search-input').value = '';
document.querySelectorAll('[data-search-input]').forEach(input => {
input.value = '';
});
// 게임 목록 다시 렌더링
renderGames();
@@ -827,25 +831,136 @@ document.addEventListener('DOMContentLoaded', () => {
// 필터 체크박스 이벤트 리스너 설정
function setupFilterListeners() {
const searchInput = document.getElementById('search-input');
const searchForm = document.getElementById('searchForm');
const resetSearch = document.getElementById('reset-search');
const searchInputs = [
document.getElementById('search-input'),
document.getElementById('mobile-search-input'),
].filter(Boolean);
const searchForms = [document.getElementById('searchForm')].filter(Boolean);
const resetSearchButtons = [
document.getElementById('reset-search'),
document.getElementById('mobile-reset-search'),
].filter(Boolean);
searchForm.addEventListener('submit', event => {
event.preventDefault();
filterState.searchText = searchInput.value.trim().toLowerCase();
function updateSearchText(value) {
filterState.searchText = value.trim().toLowerCase();
searchInputs.forEach(input => {
if (input.value !== value) input.value = value;
});
renderGames();
}
window.updateNswSearch = value => {
updateSearchText(value || '');
};
window.clearNswSearch = () => {
updateSearchText('');
};
function updateSearchFromInput(input, options = {}) {
window.setTimeout(() => {
updateSearchText(input.value);
if (options.blur) input.blur();
}, 0);
}
function isSearchInput(target) {
return target instanceof HTMLInputElement && target.matches('[data-search-input]');
}
document.addEventListener(
'submit',
event => {
if (!event.target.querySelector?.('[data-search-input]')) return;
event.preventDefault();
if (event.stopImmediatePropagation) event.stopImmediatePropagation();
event.stopPropagation();
const searchInput = event.target.querySelector('[data-search-input]');
if (searchInput) updateSearchFromInput(searchInput, { blur: true });
},
true
);
document.addEventListener(
'input',
event => {
if (!isSearchInput(event.target)) return;
updateSearchText(event.target.value);
},
true
);
document.addEventListener(
'change',
event => {
if (!isSearchInput(event.target)) return;
updateSearchFromInput(event.target);
},
true
);
document.addEventListener(
'focusout',
event => {
if (!isSearchInput(event.target)) return;
updateSearchFromInput(event.target);
},
true
);
searchForms.forEach(form => {
form.addEventListener(
'submit',
event => {
event.preventDefault();
if (event.stopImmediatePropagation) event.stopImmediatePropagation();
event.stopPropagation();
const searchInput = form.querySelector('[data-search-input]');
if (searchInput) updateSearchFromInput(searchInput, { blur: true });
},
true
);
});
searchInput.addEventListener('input', event => {
filterState.searchText = event.target.value.trim().toLowerCase();
renderGames();
searchInputs.forEach(input => {
let isComposing = false;
input.addEventListener('compositionstart', () => {
isComposing = true;
});
input.addEventListener('blur', event => {
updateSearchFromInput(event.target);
});
input.addEventListener('compositionend', event => {
isComposing = false;
updateSearchFromInput(event.target);
});
input.addEventListener('keydown', event => {
if (event.key !== 'Enter') return;
if (isComposing || event.isComposing || event.keyCode === 229) return;
event.preventDefault();
updateSearchFromInput(event.target, { blur: true });
});
input.addEventListener('keyup', event => {
if (isComposing || event.isComposing || event.keyCode === 229) return;
updateSearchFromInput(event.target, { blur: event.key === 'Enter' });
});
});
resetSearch.addEventListener('click', () => {
filterState.searchText = '';
searchInput.value = '';
renderGames();
resetSearchButtons.forEach(button => {
function clearSearch(event) {
event.preventDefault();
event.stopPropagation();
updateSearchText('');
}
button.addEventListener('pointerdown', clearSearch);
button.addEventListener('touchstart', clearSearch);
button.addEventListener('click', clearSearch);
});
// 언어 필터