diff --git a/frontend/src/pages/mobile/public/Schedule.jsx b/frontend/src/pages/mobile/public/Schedule.jsx index 41f7d86..548507b 100644 --- a/frontend/src/pages/mobile/public/Schedule.jsx +++ b/frontend/src/pages/mobile/public/Schedule.jsx @@ -440,7 +440,7 @@ function MobileSchedule() { return category?.color || '#6b7280'; }; - // 날짜별 일정 그룹화 + // 날짜별 일정 그룹화 (생일 우선) const groupedSchedules = useMemo(() => { const groups = {}; schedules.forEach(schedule => { @@ -448,6 +448,16 @@ function MobileSchedule() { if (!groups[date]) groups[date] = []; groups[date].push(schedule); }); + // 각 날짜 그룹 내에서 생일 우선 정렬 + Object.values(groups).forEach(group => { + group.sort((a, b) => { + const aIsBirthday = a.is_birthday || String(a.id).startsWith('birthday-'); + const bIsBirthday = b.is_birthday || String(b.id).startsWith('birthday-'); + if (aIsBirthday && !bIsBirthday) return -1; + if (!aIsBirthday && bIsBirthday) return 1; + return 0; + }); + }); return Object.entries(groups).sort((a, b) => a[0].localeCompare(b[0])); }, [schedules]); @@ -463,7 +473,7 @@ function MobileSchedule() { return days; }, [selectedDate]); - // 선택된 날짜의 일정 + // 선택된 날짜의 일정 (생일 우선) const selectedDateSchedules = useMemo(() => { // KST 기준 날짜 문자열 생성 const year = selectedDate.getFullYear(); @@ -471,7 +481,15 @@ function MobileSchedule() { const day = String(selectedDate.getDate()).padStart(2, '0'); const dateStr = `${year}-${month}-${day}`; // API 응답의 date는 ISO 형식이므로 T 이전 부분만 비교 - return schedules.filter(s => s.date.split('T')[0] === dateStr); + return schedules + .filter(s => s.date.split('T')[0] === dateStr) + .sort((a, b) => { + const aIsBirthday = a.is_birthday || String(a.id).startsWith('birthday-'); + const bIsBirthday = b.is_birthday || String(b.id).startsWith('birthday-'); + if (aIsBirthday && !bIsBirthday) return -1; + if (!aIsBirthday && bIsBirthday) return 1; + return 0; + }); }, [schedules, selectedDate]); // 요일 이름 diff --git a/frontend/src/pages/pc/admin/AdminSchedule.jsx b/frontend/src/pages/pc/admin/AdminSchedule.jsx index e0a25a0..8882ab0 100644 --- a/frontend/src/pages/pc/admin/AdminSchedule.jsx +++ b/frontend/src/pages/pc/admin/AdminSchedule.jsx @@ -575,18 +575,31 @@ function AdminSchedule() { // 일정 목록 (검색 모드일 때 searchResults, 일반 모드일 때 로컬 필터링) - useMemo로 최적화 const filteredSchedules = useMemo(() => { + let result; if (isSearchMode) { if (!searchTerm) return []; // 카테고리 필터링 적용 - if (selectedCategories.length === 0) return searchResults; - return searchResults.filter(s => selectedCategories.includes(s.category_id)); + if (selectedCategories.length === 0) { + result = [...searchResults]; + } else { + result = searchResults.filter(s => selectedCategories.includes(s.category_id)); + } + } else { + // 일반 모드: 로컬 필터링 + result = schedules.filter(schedule => { + const matchesCategory = selectedCategories.length === 0 || selectedCategories.includes(schedule.category_id); + const scheduleDate = formatDate(schedule.date); + const matchesDate = !selectedDate || scheduleDate === selectedDate; + return matchesCategory && matchesDate; + }); } - // 일반 모드: 로컬 필터링 - return schedules.filter(schedule => { - const matchesCategory = selectedCategories.length === 0 || selectedCategories.includes(schedule.category_id); - const scheduleDate = formatDate(schedule.date); - const matchesDate = !selectedDate || scheduleDate === selectedDate; - return matchesCategory && matchesDate; + // 생일 일정을 맨 위로 정렬 + return result.sort((a, b) => { + const aIsBirthday = a.is_birthday || String(a.id).startsWith('birthday-'); + const bIsBirthday = b.is_birthday || String(b.id).startsWith('birthday-'); + if (aIsBirthday && !bIsBirthday) return -1; + if (!aIsBirthday && bIsBirthday) return 1; + return 0; }); }, [isSearchMode, searchTerm, searchResults, schedules, selectedCategories, selectedDate]); @@ -1300,18 +1313,19 @@ function AdminSchedule() { )} - {(schedule.members?.length > 0 || schedule.member_names) && ( -
- {(() => { - const memberList = schedule.members?.map(m => m.name) || schedule.member_names?.split(',') || []; - return memberList.map((name, i) => ( + {(() => { + const memberList = getMemberList(schedule); + if (memberList.length === 0) return null; + return ( +
+ {memberList.map((name, i) => ( - {name.trim()} + {name} - )); - })()} -
- )} + ))} +
+ ); + })()}
diff --git a/frontend/src/pages/pc/public/Schedule.jsx b/frontend/src/pages/pc/public/Schedule.jsx index 0dbe493..ef81dc8 100644 --- a/frontend/src/pages/pc/public/Schedule.jsx +++ b/frontend/src/pages/pc/public/Schedule.jsx @@ -483,27 +483,44 @@ function Schedule() { const currentYearMonth = `${year}-${String(month + 1).padStart(2, '0')}`; const filteredSchedules = useMemo(() => { + // 생일 우선 정렬 함수 + const sortWithBirthdayFirst = (list) => { + return [...list].sort((a, b) => { + const aIsBirthday = a.is_birthday || String(a.id).startsWith('birthday-'); + const bIsBirthday = b.is_birthday || String(b.id).startsWith('birthday-'); + if (aIsBirthday && !bIsBirthday) return -1; + if (!aIsBirthday && bIsBirthday) return 1; + return 0; + }); + }; + // 검색 모드일 때 if (isSearchMode) { // 검색 전엔 빈 목록, 검색 후엔 API 결과 (Meilisearch 유사도순 유지) if (!searchTerm) return []; // 카테고리 필터링 적용 - if (selectedCategories.length === 0) return searchResults; - return searchResults.filter(s => selectedCategories.includes(s.category_id)); + if (selectedCategories.length === 0) return sortWithBirthdayFirst(searchResults); + return sortWithBirthdayFirst(searchResults.filter(s => selectedCategories.includes(s.category_id))); } - + // 일반 모드: 기존 필터링 - return schedules + const filtered = schedules .filter(s => { const scheduleDate = s.date ? s.date.split('T')[0] : ''; - const matchesDate = selectedDate + const matchesDate = selectedDate ? scheduleDate === selectedDate : scheduleDate.startsWith(currentYearMonth); const matchesCategory = selectedCategories.length === 0 || selectedCategories.includes(s.category_id); return matchesDate && matchesCategory; }) .sort((a, b) => { + // 생일 우선 + const aIsBirthday = a.is_birthday || String(a.id).startsWith('birthday-'); + const bIsBirthday = b.is_birthday || String(b.id).startsWith('birthday-'); + if (aIsBirthday && !bIsBirthday) return -1; + if (!aIsBirthday && bIsBirthday) return 1; + // 날짜/시간순 const dateA = a.date ? a.date.split('T')[0] : ''; const dateB = b.date ? b.date.split('T')[0] : ''; if (dateA !== dateB) return dateA.localeCompare(dateB); @@ -511,6 +528,7 @@ function Schedule() { const timeB = b.time || '00:00:00'; return timeA.localeCompare(timeB); }); + return filtered; }, [schedules, selectedDate, currentYearMonth, selectedCategories, isSearchMode, searchTerm, searchResults]); // 가상 스크롤 설정 (검색 모드에서만 활성화, 동적 높이 지원)