feat(Search): Admin Schedule 추천 검색어 연동 + 빈 상태 드롭다운 숨김

This commit is contained in:
caadiq 2026-01-11 21:39:23 +09:00
parent 727b05f0f5
commit 9c2ff7458d
2 changed files with 78 additions and 79 deletions

View file

@ -214,6 +214,34 @@ function AdminSchedule() {
prevInViewRef.current = inView; prevInViewRef.current = inView;
}, [inView, hasNextPage, isFetchingNextPage, fetchNextPage, isSearchMode, searchTerm]); }, [inView, hasNextPage, isFetchingNextPage, fetchNextPage, isSearchMode, searchTerm]);
// API (debounce )
useEffect(() => {
//
if (!originalSearchQuery || originalSearchQuery.trim().length === 0) {
setSuggestions([]);
return;
}
// debounce: 200ms API
const timeoutId = setTimeout(async () => {
setIsLoadingSuggestions(true);
try {
const response = await fetch(`/api/schedules/suggestions?q=${encodeURIComponent(originalSearchQuery)}&limit=10`);
if (response.ok) {
const data = await response.json();
setSuggestions(data.suggestions || []);
}
} catch (error) {
console.error('추천 검색어 API 오류:', error);
setSuggestions([]);
} finally {
setIsLoadingSuggestions(false);
}
}, 200);
return () => clearTimeout(timeoutId);
}, [originalSearchQuery]);
// selectedDate // selectedDate
useEffect(() => { useEffect(() => {
if (!selectedDate) { if (!selectedDate) {
@ -1005,33 +1033,28 @@ function AdminSchedule() {
}} }}
onFocus={() => setShowSuggestions(true)} onFocus={() => setShowSuggestions(true)}
onKeyDown={(e) => { onKeyDown={(e) => {
//
const dummySuggestions = ['성수기', '성수기 이채영', '이채영 먹방', 'NOW TOMORROW', '하얀 그리움', '콘서트', '월드투어'].filter(s =>
s.toLowerCase().includes(originalSearchQuery.toLowerCase())
).slice(0, 7);
if (e.key === 'ArrowDown') { if (e.key === 'ArrowDown') {
e.preventDefault(); e.preventDefault();
const newIndex = selectedSuggestionIndex < dummySuggestions.length - 1 const newIndex = selectedSuggestionIndex < suggestions.length - 1
? selectedSuggestionIndex + 1 ? selectedSuggestionIndex + 1
: 0; : 0;
setSelectedSuggestionIndex(newIndex); setSelectedSuggestionIndex(newIndex);
if (dummySuggestions[newIndex]) { if (suggestions[newIndex]) {
setSearchInput(dummySuggestions[newIndex]); setSearchInput(suggestions[newIndex]);
} }
} else if (e.key === 'ArrowUp') { } else if (e.key === 'ArrowUp') {
e.preventDefault(); e.preventDefault();
const newIndex = selectedSuggestionIndex > 0 const newIndex = selectedSuggestionIndex > 0
? selectedSuggestionIndex - 1 ? selectedSuggestionIndex - 1
: dummySuggestions.length - 1; : suggestions.length - 1;
setSelectedSuggestionIndex(newIndex); setSelectedSuggestionIndex(newIndex);
if (dummySuggestions[newIndex]) { if (suggestions[newIndex]) {
setSearchInput(dummySuggestions[newIndex]); setSearchInput(suggestions[newIndex]);
} }
} else if (e.key === 'Enter') { } else if (e.key === 'Enter') {
if (selectedSuggestionIndex >= 0 && dummySuggestions[selectedSuggestionIndex]) { if (selectedSuggestionIndex >= 0 && suggestions[selectedSuggestionIndex]) {
setSearchInput(dummySuggestions[selectedSuggestionIndex]); setSearchInput(suggestions[selectedSuggestionIndex]);
setSearchTerm(dummySuggestions[selectedSuggestionIndex]); setSearchTerm(suggestions[selectedSuggestionIndex]);
} else if (searchInput.trim()) { } else if (searchInput.trim()) {
setSearchTerm(searchInput); setSearchTerm(searchInput);
} }
@ -1065,22 +1088,9 @@ function AdminSchedule() {
</div> </div>
{/* 검색어 추천 드롭다운 */} {/* 검색어 추천 드롭다운 */}
{showSuggestions && originalSearchQuery.length > 0 && ( {showSuggestions && !isLoadingSuggestions && suggestions.length > 0 && (
<div className="absolute top-full left-0 right-8 mt-2 bg-white rounded-xl shadow-lg border border-gray-200 py-1 z-50 overflow-hidden"> <div className="absolute top-full left-0 right-8 mt-2 bg-white rounded-xl shadow-lg border border-gray-200 py-1 z-50 overflow-hidden">
{(() => { {suggestions.map((suggestion, index) => (
const dummySuggestions = ['성수기', '성수기 이채영', '이채영 먹방', 'NOW TOMORROW', '하얀 그리움', '콘서트', '월드투어'].filter(s =>
s.toLowerCase().includes(originalSearchQuery.toLowerCase())
).slice(0, 7);
if (dummySuggestions.length === 0) {
return (
<div className="px-4 py-3 text-sm text-gray-400 text-center">
추천 검색어가 없습니다
</div>
);
}
return dummySuggestions.map((suggestion, index) => (
<button <button
key={suggestion} key={suggestion}
onClick={() => { onClick={() => {
@ -1098,8 +1108,7 @@ function AdminSchedule() {
<Search size={14} className="text-gray-400 shrink-0" /> <Search size={14} className="text-gray-400 shrink-0" />
<span>{suggestion}</span> <span>{suggestion}</span>
</button> </button>
)); ))}
})()}
</div> </div>
)} )}
</div> </div>

View file

@ -854,18 +854,9 @@ function Schedule() {
</div> </div>
{/* 검색어 추천 드롭다운 */} {/* 검색어 추천 드롭다운 */}
{showSuggestions && originalSearchQuery.length > 0 && ( {showSuggestions && !isLoadingSuggestions && suggestions.length > 0 && (
<div className="absolute top-full mt-2 bg-white rounded-xl shadow-lg border border-gray-200 py-1 z-50 overflow-hidden" style={{ left: '44px', right: '66px' }}> <div className="absolute top-full mt-2 bg-white rounded-xl shadow-lg border border-gray-200 py-1 z-50 overflow-hidden" style={{ left: '44px', right: '66px' }}>
{isLoadingSuggestions ? ( {suggestions.map((suggestion, index) => (
<div className="px-4 py-3 text-gray-400 text-sm text-center">
검색 ...
</div>
) : suggestions.length === 0 ? (
<div className="px-4 py-3 text-gray-400 text-sm text-center">
추천 검색어가 없습니다
</div>
) : (
suggestions.map((suggestion, index) => (
<button <button
key={index} key={index}
onClick={() => { onClick={() => {
@ -884,8 +875,7 @@ function Schedule() {
<Search size={15} className={selectedSuggestionIndex === index ? 'text-primary' : 'text-gray-400'} /> <Search size={15} className={selectedSuggestionIndex === index ? 'text-primary' : 'text-gray-400'} />
<span className="text-sm">{suggestion}</span> <span className="text-sm">{suggestion}</span>
</button> </button>
)) ))}
)}
</div> </div>
)} )}
</div> </div>