diff --git a/frontend/src/pages/pc/admin/AdminSchedule.jsx b/frontend/src/pages/pc/admin/AdminSchedule.jsx
index 8ff7633..c7f2c7d 100644
--- a/frontend/src/pages/pc/admin/AdminSchedule.jsx
+++ b/frontend/src/pages/pc/admin/AdminSchedule.jsx
@@ -536,13 +536,15 @@ function AdminSchedule() {
// 카테고리별 카운트 맵 (useMemo로 미리 계산) - 선택된 날짜 기준
const categoryCounts = useMemo(() => {
+ // 검색어가 있을 때만 검색 결과 사용, 아니면 기존 schedules 사용
const source = (isSearchMode && searchTerm) ? searchResults : schedules;
const counts = new Map();
let total = 0;
source.forEach(s => {
- // 검색 모드가 아닐 때만 선택된 날짜 기준으로 필터링
- if (!isSearchMode && selectedDate) {
+ // 검색 모드에서 검색어가 있을 때는 전체 대상
+ // 그 외에는 선택된 날짜 기준으로 필터링
+ if (!(isSearchMode && searchTerm) && selectedDate) {
const scheduleDate = formatDate(s.date);
if (scheduleDate !== selectedDate) return;
}
diff --git a/frontend/src/pages/pc/public/Schedule.jsx b/frontend/src/pages/pc/public/Schedule.jsx
index 22066d7..85abe11 100644
--- a/frontend/src/pages/pc/public/Schedule.jsx
+++ b/frontend/src/pages/pc/public/Schedule.jsx
@@ -1,7 +1,7 @@
import { useState, useEffect, useRef, useMemo, useDeferredValue, memo } from 'react';
import { useNavigate } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
-import { Clock, ChevronLeft, ChevronRight, ChevronDown, Tag, Search, ArrowLeft, Link2 } from 'lucide-react';
+import { Clock, ChevronLeft, ChevronRight, ChevronDown, Tag, Search, ArrowLeft, Link2, X } from 'lucide-react';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useInView } from 'react-intersection-observer';
@@ -37,11 +37,15 @@ function Schedule() {
const [showCategoryTooltip, setShowCategoryTooltip] = useState(false);
const categoryRef = useRef(null);
const scrollContainerRef = useRef(null); // 일정 목록 스크롤 컨테이너
+ const searchContainerRef = useRef(null); // 검색 컨테이너 (외부 클릭 감지용)
// 검색 상태
const [isSearchMode, setIsSearchMode] = useState(false);
- const [searchInput, setSearchInput] = useState('');
+ const [searchInput, setSearchInput] = useState(''); // 입력창에 표시되는 값
+ const [originalSearchQuery, setOriginalSearchQuery] = useState(''); // 사용자가 직접 입력한 원본 값 (필터링용)
const [searchTerm, setSearchTerm] = useState('');
+ const [showSuggestions, setShowSuggestions] = useState(false);
+ const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(-1);
const SEARCH_LIMIT = 20; // 페이지당 20개
const ESTIMATED_ITEM_HEIGHT = 120; // 아이템 추정 높이 (동적 측정)
@@ -138,6 +142,11 @@ function Schedule() {
if (categoryRef.current && !categoryRef.current.contains(event.target)) {
setShowCategoryTooltip(false);
}
+ // 검색 추천 드롭다운 외부 클릭 시 닫기
+ if (searchContainerRef.current && !searchContainerRef.current.contains(event.target)) {
+ setShowSuggestions(false);
+ setSelectedSuggestionIndex(-1);
+ }
};
document.addEventListener('mousedown', handleClickOutside);
@@ -299,14 +308,16 @@ function Schedule() {
// 카테고리별 카운트 맵 (useMemo로 미리 계산) - 선택된 날짜 기준
const categoryCounts = useMemo(() => {
+ // 검색어가 있을 때만 검색 결과 사용, 아니면 기존 schedules 사용
const source = (isSearchMode && searchTerm) ? searchResults : schedules;
const counts = new Map();
let total = 0;
source.forEach(s => {
const scheduleDate = s.date ? s.date.split('T')[0] : '';
- // 검색 모드가 아닐 때만 선택된 날짜 기준으로 필터링
- if (!isSearchMode && selectedDate) {
+ // 검색 모드에서 검색어가 있을 때는 전체 대상
+ // 그 외에는 선택된 날짜 기준으로 필터링
+ if (!(isSearchMode && searchTerm) && selectedDate) {
if (scheduleDate !== selectedDate) return;
}
@@ -688,67 +699,180 @@ function Schedule() {
{/* 스케줄 리스트 */}
{/* 헤더 */}
-
+
{isSearchMode ? (
/* 검색 모드 - 밑줄 스타일 */
-
-
-
setSearchInput(e.target.value)}
- onKeyDown={(e) => {
- if (e.key === 'Enter') {
- setSearchTerm(searchInput);
- } else if (e.key === 'Escape') {
+ {/* 검색창 컨테이너 - 화살표와 검색창 일체형 */}
+
+
+ {/* 뒤로가기 영역 */}
+
+
+ {/* 검색 입력 영역 */}
+
+ {
+ setSearchInput(e.target.value);
+ setOriginalSearchQuery(e.target.value); // 원본 쿼리도 업데이트
+ setShowSuggestions(true);
+ setSelectedSuggestionIndex(-1);
+ }}
+ onFocus={() => setShowSuggestions(true)}
+ onKeyDown={(e) => {
+ // 필터링은 원본 쿼리 기준으로 유지
+ const dummySuggestions = ['성수기', '성수기 이채영', '이채영 먹방', 'NOW TOMORROW', '하얀 그리움', '콘서트', '월드투어'].filter(s =>
+ s.toLowerCase().includes(originalSearchQuery.toLowerCase())
+ ).slice(0, 7);
+
+ if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ const newIndex = selectedSuggestionIndex < dummySuggestions.length - 1
+ ? selectedSuggestionIndex + 1
+ : 0;
+ setSelectedSuggestionIndex(newIndex);
+ if (dummySuggestions[newIndex]) {
+ setSearchInput(dummySuggestions[newIndex]);
+ }
+ } else if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ const newIndex = selectedSuggestionIndex > 0
+ ? selectedSuggestionIndex - 1
+ : dummySuggestions.length - 1;
+ setSelectedSuggestionIndex(newIndex);
+ if (dummySuggestions[newIndex]) {
+ setSearchInput(dummySuggestions[newIndex]);
+ }
+ } else if (e.key === 'Enter') {
+ if (selectedSuggestionIndex >= 0 && dummySuggestions[selectedSuggestionIndex]) {
+ setSearchInput(dummySuggestions[selectedSuggestionIndex]);
+ setSearchTerm(dummySuggestions[selectedSuggestionIndex]);
+ } else if (searchInput.trim()) {
+ setSearchTerm(searchInput);
+ }
+ setShowSuggestions(false);
+ setSelectedSuggestionIndex(-1);
+ } else if (e.key === 'Escape') {
+ setIsSearchMode(false);
+ setSearchInput('');
+ setOriginalSearchQuery('');
+ setSearchTerm('');
+ setShowSuggestions(false);
+ setSelectedSuggestionIndex(-1);
+ // 스크롤 위치 초기화
+ if (scrollContainerRef.current) {
+ scrollContainerRef.current.scrollTop = 0;
+ }
+ }
+ }}
+ className="flex-1 bg-transparent focus:outline-none text-gray-700 placeholder-gray-400 text-sm"
+ />
+ {/* 입력 지우기 버튼 - 항상 공간 차지, 입력 있을 때만 보임 */}
+
+
+
+ {/* 검색 버튼 영역 */}
+
+
+
+ {/* 검색어 추천 드롭다운 */}
+ {showSuggestions && originalSearchQuery.length > 0 && (
+
+ {(() => {
+ const dummySuggestions = ['성수기', '성수기 이채영', '이채영 먹방', 'NOW TOMORROW', '하얀 그리움', '콘서트', '월드투어'].filter(s =>
+ s.toLowerCase().includes(originalSearchQuery.toLowerCase())
+ ).slice(0, 7);
+
+ if (dummySuggestions.length === 0) {
+ return (
+
+ 추천 검색어가 없습니다
+
+ );
+ }
+
+ return dummySuggestions.map((suggestion, index) => (
+
+ ));
+ })()}
+
+ )}
-
) : (
/* 일반 모드 */
+ !isSearchMode && (
+
+ {selectedDate ? '선택한 날짜에 일정이 없습니다.' : '예정된 일정이 없습니다.'}
+
+ )
)}