diff --git a/apps/nowait-user/src/assets/icon/cancel.svg b/apps/nowait-user/src/assets/icon/cancel.svg index b3dfb7df..2767b326 100644 --- a/apps/nowait-user/src/assets/icon/cancel.svg +++ b/apps/nowait-user/src/assets/icon/cancel.svg @@ -1,4 +1,4 @@ - - + + diff --git a/apps/nowait-user/src/assets/icon/search.svg b/apps/nowait-user/src/assets/icon/search.svg index adcd7125..0cae67f4 100644 --- a/apps/nowait-user/src/assets/icon/search.svg +++ b/apps/nowait-user/src/assets/icon/search.svg @@ -1,3 +1,3 @@ - - + + diff --git a/apps/nowait-user/src/components/Header.tsx b/apps/nowait-user/src/components/Header.tsx index 1e9016d2..f05eb8b2 100644 --- a/apps/nowait-user/src/components/Header.tsx +++ b/apps/nowait-user/src/components/Header.tsx @@ -3,12 +3,14 @@ import { AnimatePresence, motion } from "framer-motion"; import { useNavigate } from "react-router-dom"; import Logo from "../assets/logo.svg?react"; import Menu from "../assets/icon/menu.svg?react"; -import Search from "../assets/icon/search_black.svg?react"; +import Search from "../assets/icon/search.svg?react"; import Cancel from "../assets/icon/cancel.svg?react"; import Portal from "./common/modal/Portal"; +import SearchModal from "./common/modal/SearchModal"; const HomeHeader = () => { const [isMenuOpen, setIsMenuOpen] = useState(false); + const [isSearchOpen, setIsSearchOpen] = useState(false); const navigate = useNavigate(); const toggleMenu = () => { @@ -19,6 +21,14 @@ const HomeHeader = () => { setIsMenuOpen(false); }; + const openSearch = () => { + setIsSearchOpen(true); + }; + + const closeSearch = () => { + setIsSearchOpen(false); + }; + const handleBookmarkClick = () => { closeMenu(); navigate("/bookmark"); @@ -34,7 +44,7 @@ const HomeHeader = () => {
- + + + {/* 검색 내용 영역 */} + + {searchQuery.trim() ? ( + // 검색 결과 표시 +
+
+ 검색 결과{" "} + {searchResults.length > 0 && `${searchResults.length}`} +
+ {isSearching ? ( +
+
+ 검색 중... +
+
+ ) : searchResults.length > 0 ? ( +
+ {searchResults.map((store) => ( + + ))} +
+ ) : ( +
+
+ 검색 결과가 없습니다 +
+
+ 다른 키워드로 검색해보세요 +
+
+ )} +
+ ) : ( + // 최근 검색 표시 +
+
+ 최근 검색 +
+ {recentSearches.length > 0 ? ( + recentSearches.map((searchTerm, index) => ( +
+ +
+ +
+
+ )) + ) : ( +
+ 최근 검색어가 없습니다 +
+ )} +
+ )} +
+
+ + )} + + + ); +}; + +export default SearchModal; diff --git a/apps/nowait-user/src/global.css b/apps/nowait-user/src/global.css index 8c273798..cd22f186 100644 --- a/apps/nowait-user/src/global.css +++ b/apps/nowait-user/src/global.css @@ -103,6 +103,28 @@ body { } } +@keyframes number-slide-up { + 0% { + transform: translateY(40%); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes number-slide-down { + 0% { + transform: translateY(-40%); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} + @keyframes slide-up-out { 0% { transform: translateY(0); @@ -145,6 +167,14 @@ body { animation: slide-down-out 0.3s ease-out forwards; } +.animate-number-slide-up { + animation: number-slide-up 0.3s ease-out forwards; +} + +.animate-number-slide-down { + animation: number-slide-down 0.3s ease-out forwards; +} + @media (prefers-reduced-motion: no-preference) { a:nth-of-type(2) .logo { animation: logo-spin infinite 20s linear; @@ -202,7 +232,7 @@ body { max-width: 11.25rem; max-height: 17px; color: var(--navy-70); - background-color: #F2F6F9; + background-color: #f2f6f9; padding: unset !important; justify-content: center; text-align: center; diff --git a/apps/nowait-user/src/pages/home/components/MyWaitingDetail.tsx b/apps/nowait-user/src/pages/home/components/MyWaitingDetail.tsx index 23eb4f7a..855eadf7 100644 --- a/apps/nowait-user/src/pages/home/components/MyWaitingDetail.tsx +++ b/apps/nowait-user/src/pages/home/components/MyWaitingDetail.tsx @@ -19,13 +19,36 @@ const MyWaitingDetail = ({ // 현재 활성 카드 인덱스 상태 const [currentIndex, setCurrentIndex] = useState(0); - const [prevIndex, setPrevIndex] = useState(0); + const [scrollOffset, setScrollOffset] = useState(0); + const [animationKey, setAnimationKey] = useState(0); const [animationDirection, setAnimationDirection] = useState< "up" | "down" | null >(null); - const [isAnimating, setIsAnimating] = useState(false); - const [scrollOffset, setScrollOffset] = useState(0); const scrollContainerRef = useRef(null); + const prevIndexRef = useRef(0); + + // 숫자 애니메이션 효과 + useEffect(() => { + if (currentIndex !== prevIndexRef.current) { + // 슬라이드 방향 판단 + const isRightSlide = currentIndex > prevIndexRef.current; + + // 애니메이션 방향 설정 + setAnimationDirection(isRightSlide ? "up" : "down"); + + // 강제 리렌더링을 위한 키 변경 + setAnimationKey((prev) => prev + 1); + + // 애니메이션 완료 후 방향 리셋 + const timer = setTimeout(() => { + setAnimationDirection(null); + }, 300); + + prevIndexRef.current = currentIndex; + + return () => clearTimeout(timer); + } + }, [currentIndex]); // 스크롤 이벤트 핸들러 useEffect(() => { @@ -44,23 +67,7 @@ const MyWaitingDetail = ({ setScrollOffset(offset); if (boundedIndex !== currentIndex) { - setPrevIndex(currentIndex); - - // 애니메이션 방향 결정 - if (boundedIndex > currentIndex) { - setAnimationDirection("up"); - } else { - setAnimationDirection("down"); - } - - setIsAnimating(true); setCurrentIndex(boundedIndex); - - // 애니메이션 완료 후 상태 초기화 - setTimeout(() => { - setIsAnimating(false); - setAnimationDirection(null); - }, 300); } }; @@ -68,91 +75,6 @@ const MyWaitingDetail = ({ return () => container.removeEventListener("scroll", handleScroll); }, [items.length, currentIndex]); - // 숫자 애니메이션 컴포넌트 (각 자릿수별 애니메이션) - const AnimatedDigits = ({ - value, - prevValue, - direction, - isAnimating, - }: { - value: number; - prevValue: number; - direction: "up" | "down" | null; - isAnimating: boolean; - }) => { - // 숫자를 문자열로 변환 후 각 자릿수로 분리 - const currentDigits = value.toString().split("").map(Number); - const prevDigits = prevValue.toString().split("").map(Number); - - // 단일 자릿수 애니메이션 컴포넌트 - const SingleDigit = ({ - digit, - prevDigit, - digitIndex, - totalDigits, - }: { - digit: number; - prevDigit: number; - digitIndex: number; - totalDigits: number; - }) => { - // 오른쪽부터 애니메이션 (delay 계산: 일의자리가 먼저, 십의자리가 나중에) - const reverseIndex = totalDigits - 1 - digitIndex; - const animationDelay = reverseIndex * 100; // 100ms씩 지연 - - // 이 자릿수가 실제로 변경되었는지 확인 - const isDigitChanged = digit !== prevDigit; - const shouldAnimate = isAnimating && isDigitChanged; - - return ( - - - {digit} - - {shouldAnimate && ( - - {prevDigit} - - )} - - ); - }; - - return ( - - {currentDigits.map((digit, index) => ( - - ))} - - ); - }; - return (
@@ -176,12 +98,18 @@ const MyWaitingDetail = ({
내 앞 대기 - + + {items[currentIndex]?.waitingCount || 0} +
diff --git a/apps/nowait-user/src/types/search.ts b/apps/nowait-user/src/types/search.ts new file mode 100644 index 00000000..a05151a8 --- /dev/null +++ b/apps/nowait-user/src/types/search.ts @@ -0,0 +1,18 @@ +export interface SearchStore { + storeId: number; + departmentId: number; + departmentName: string; + name: string; + location: string; + description: string; + profileImage: string | null; + bannerImages: any[]; + isActive: boolean; + deleted: boolean; + createdAt: string; +} + +export interface SearchResponse { + success: boolean; + response: SearchStore[]; +}