feat(schedule): 검색 결과 없음 UI 추가 및 빈 상태 위치 개선

- PC: 검색 모드에서 결과 0개일 때 아무것도 안 나오던 문제 수정
  (돋보기 아이콘 + 검색어 표시), 검색 중 로딩 구분
- 모바일: 검색/날짜별 빈 상태를 아이콘 포함 디자인으로 통일
- 빈 상태/로딩을 일정 영역 기준으로 배치
  (PC: 상단 30% 지점, 모바일: 중앙)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-05-31 22:15:10 +09:00
parent 8b8b9a7f53
commit dd5ef48592
2 changed files with 41 additions and 8 deletions

View file

@ -695,7 +695,7 @@ function MobileSchedule() {
className="mobile-content" className="mobile-content"
ref={isSearchMode && searchTerm && !showSuggestionsScreen ? scrollContainerRef : contentRef} ref={isSearchMode && searchTerm && !showSuggestionsScreen ? scrollContainerRef : contentRef}
> >
<div className={`px-4 pb-4 ${isSearchMode && showSuggestionsScreen ? 'pt-0' : 'pt-4'}`}> <div className={`px-4 pb-4 min-h-full flex flex-col ${isSearchMode && showSuggestionsScreen ? 'pt-0' : 'pt-4'}`}>
{isSearchMode ? ( {isSearchMode ? (
showSuggestionsScreen ? ( showSuggestionsScreen ? (
// //
@ -720,11 +720,19 @@ function MobileSchedule() {
) : !searchTerm ? ( ) : !searchTerm ? (
<div className="text-center py-8 text-gray-400">검색어를 입력하세요</div> <div className="text-center py-8 text-gray-400">검색어를 입력하세요</div>
) : searchLoading ? ( ) : searchLoading ? (
<div className="flex justify-center py-8"> <div className="flex-1 flex items-center justify-center">
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" /> <div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
</div> </div>
) : searchResults.length === 0 ? ( ) : searchResults.length === 0 ? (
<div className="text-center py-8 text-gray-400">검색 결과가 없습니다</div> <div className="flex-1 flex flex-col items-center justify-center">
<div className="w-20 h-20 bg-gray-100 rounded-full flex items-center justify-center mb-3">
<svg className="w-10 h-10 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M21 21l-4.35-4.35M11 18a7 7 0 110-14 7 7 0 010 14z" />
</svg>
</div>
<p className="text-gray-400 text-base font-medium mb-1">검색 결과가 없습니다</p>
<p className="text-gray-300 text-sm">'<span className="text-gray-400">{searchTerm}</span>' 대한 일정을 찾을 없습니다</p>
</div>
) : ( ) : (
<> <>
<div <div
@ -771,12 +779,19 @@ function MobileSchedule() {
</> </>
) )
) : loading ? ( ) : loading ? (
<div className="flex justify-center py-8"> <div className="flex-1 flex items-center justify-center">
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" /> <div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
</div> </div>
) : selectedDateSchedules.length === 0 ? ( ) : selectedDateSchedules.length === 0 ? (
<div className="text-center py-8 text-gray-400"> <div className="flex-1 flex flex-col items-center justify-center">
{selectedDate.getMonth() + 1} {selectedDate.getDate()} 일정이 없습니다 <div className="w-20 h-20 bg-gray-100 rounded-full flex items-center justify-center mb-3">
<svg className="w-10 h-10 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</div>
<p className="text-gray-400 text-base font-medium">
{selectedDate.getMonth() + 1} {selectedDate.getDate()} 일정이 없습니다
</p>
</div> </div>
) : ( ) : (
<div className="space-y-3"> <div className="space-y-3">

View file

@ -77,6 +77,7 @@ function PCSchedule() {
fetchNextPage, fetchNextPage,
hasNextPage, hasNextPage,
isFetchingNextPage, isFetchingNextPage,
isLoading: isSearchLoading,
} = useInfiniteQuery({ } = useInfiniteQuery({
queryKey: ['scheduleSearch', searchTerm], queryKey: ['scheduleSearch', searchTerm],
queryFn: async ({ pageParam = 0 }) => { queryFn: async ({ pageParam = 0 }) => {
@ -546,7 +547,7 @@ function PCSchedule() {
{/* 스케줄 목록 */} {/* 스케줄 목록 */}
<div ref={scrollContainerRef} className="flex-1 min-h-0 overflow-y-auto space-y-4 py-2 pr-2"> <div ref={scrollContainerRef} className="flex-1 min-h-0 overflow-y-auto space-y-4 py-2 pr-2">
{loading ? ( {loading ? (
<div className="text-center py-20 text-gray-500">로딩 ...</div> <div className="h-full flex items-center justify-center text-gray-500">로딩 ...</div>
) : filteredSchedules.length > 0 ? ( ) : filteredSchedules.length > 0 ? (
isSearchMode && searchTerm ? ( isSearchMode && searchTerm ? (
<> <>
@ -609,9 +610,26 @@ function PCSchedule() {
</motion.div> </motion.div>
)) ))
) )
) : isSearchMode && searchTerm ? (
isSearchLoading ? (
<div className="h-full flex items-center justify-center text-gray-500">검색 ...</div>
) : (
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="h-full flex flex-col items-center justify-start pt-[20%]">
<div className="w-24 h-24 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<svg className="w-12 h-12 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M21 21l-4.35-4.35M11 18a7 7 0 110-14 7 7 0 010 14z" />
</svg>
</div>
<p className="text-gray-400 text-lg font-medium mb-1">검색 결과가 없습니다</p>
<p className="text-gray-300 text-sm">
'<span className="text-gray-400">{searchTerm}</span>'
{selectedCategories.length > 0 ? ' 검색 결과가 없습니다' : '에 대한 일정을 찾을 수 없습니다'}
</p>
</motion.div>
)
) : ( ) : (
!isSearchMode && ( !isSearchMode && (
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="flex flex-col items-center justify-center py-16"> <motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="h-full flex flex-col items-center justify-start pt-[20%]">
<div className="w-24 h-24 bg-gray-100 rounded-full flex items-center justify-center mb-4"> <div className="w-24 h-24 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<svg className="w-12 h-12 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-12 h-12 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />