fix(frontend): 멤버 표시 오류 수정 및 생일 우선 정렬 추가
수정: - AdminSchedule.jsx 1306번 라인 멤버 처리 오류 수정 - getMemberList 헬퍼 함수 사용으로 통일 생일 우선 정렬: - PC 공개 일정 페이지: filteredSchedules에 생일 우선 정렬 추가 - PC 관리 페이지: filteredSchedules에 생일 우선 정렬 추가 - 모바일 공개 페이지: groupedSchedules, selectedDateSchedules에 생일 우선 정렬 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b4c393c20a
commit
9d365dcadb
3 changed files with 76 additions and 26 deletions
|
|
@ -440,7 +440,7 @@ function MobileSchedule() {
|
||||||
return category?.color || '#6b7280';
|
return category?.color || '#6b7280';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 날짜별 일정 그룹화
|
// 날짜별 일정 그룹화 (생일 우선)
|
||||||
const groupedSchedules = useMemo(() => {
|
const groupedSchedules = useMemo(() => {
|
||||||
const groups = {};
|
const groups = {};
|
||||||
schedules.forEach(schedule => {
|
schedules.forEach(schedule => {
|
||||||
|
|
@ -448,6 +448,16 @@ function MobileSchedule() {
|
||||||
if (!groups[date]) groups[date] = [];
|
if (!groups[date]) groups[date] = [];
|
||||||
groups[date].push(schedule);
|
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]));
|
return Object.entries(groups).sort((a, b) => a[0].localeCompare(b[0]));
|
||||||
}, [schedules]);
|
}, [schedules]);
|
||||||
|
|
||||||
|
|
@ -463,7 +473,7 @@ function MobileSchedule() {
|
||||||
return days;
|
return days;
|
||||||
}, [selectedDate]);
|
}, [selectedDate]);
|
||||||
|
|
||||||
// 선택된 날짜의 일정
|
// 선택된 날짜의 일정 (생일 우선)
|
||||||
const selectedDateSchedules = useMemo(() => {
|
const selectedDateSchedules = useMemo(() => {
|
||||||
// KST 기준 날짜 문자열 생성
|
// KST 기준 날짜 문자열 생성
|
||||||
const year = selectedDate.getFullYear();
|
const year = selectedDate.getFullYear();
|
||||||
|
|
@ -471,7 +481,15 @@ function MobileSchedule() {
|
||||||
const day = String(selectedDate.getDate()).padStart(2, '0');
|
const day = String(selectedDate.getDate()).padStart(2, '0');
|
||||||
const dateStr = `${year}-${month}-${day}`;
|
const dateStr = `${year}-${month}-${day}`;
|
||||||
// API 응답의 date는 ISO 형식이므로 T 이전 부분만 비교
|
// 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]);
|
}, [schedules, selectedDate]);
|
||||||
|
|
||||||
// 요일 이름
|
// 요일 이름
|
||||||
|
|
|
||||||
|
|
@ -575,18 +575,31 @@ function AdminSchedule() {
|
||||||
|
|
||||||
// 일정 목록 (검색 모드일 때 searchResults, 일반 모드일 때 로컬 필터링) - useMemo로 최적화
|
// 일정 목록 (검색 모드일 때 searchResults, 일반 모드일 때 로컬 필터링) - useMemo로 최적화
|
||||||
const filteredSchedules = useMemo(() => {
|
const filteredSchedules = useMemo(() => {
|
||||||
|
let result;
|
||||||
if (isSearchMode) {
|
if (isSearchMode) {
|
||||||
if (!searchTerm) return [];
|
if (!searchTerm) return [];
|
||||||
// 카테고리 필터링 적용
|
// 카테고리 필터링 적용
|
||||||
if (selectedCategories.length === 0) return searchResults;
|
if (selectedCategories.length === 0) {
|
||||||
return searchResults.filter(s => selectedCategories.includes(s.category_id));
|
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 => {
|
return result.sort((a, b) => {
|
||||||
const matchesCategory = selectedCategories.length === 0 || selectedCategories.includes(schedule.category_id);
|
const aIsBirthday = a.is_birthday || String(a.id).startsWith('birthday-');
|
||||||
const scheduleDate = formatDate(schedule.date);
|
const bIsBirthday = b.is_birthday || String(b.id).startsWith('birthday-');
|
||||||
const matchesDate = !selectedDate || scheduleDate === selectedDate;
|
if (aIsBirthday && !bIsBirthday) return -1;
|
||||||
return matchesCategory && matchesDate;
|
if (!aIsBirthday && bIsBirthday) return 1;
|
||||||
|
return 0;
|
||||||
});
|
});
|
||||||
}, [isSearchMode, searchTerm, searchResults, schedules, selectedCategories, selectedDate]);
|
}, [isSearchMode, searchTerm, searchResults, schedules, selectedCategories, selectedDate]);
|
||||||
|
|
||||||
|
|
@ -1300,18 +1313,19 @@ function AdminSchedule() {
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{(schedule.members?.length > 0 || schedule.member_names) && (
|
{(() => {
|
||||||
<div className="flex flex-wrap gap-1.5 mt-2">
|
const memberList = getMemberList(schedule);
|
||||||
{(() => {
|
if (memberList.length === 0) return null;
|
||||||
const memberList = schedule.members?.map(m => m.name) || schedule.member_names?.split(',') || [];
|
return (
|
||||||
return memberList.map((name, i) => (
|
<div className="flex flex-wrap gap-1.5 mt-2">
|
||||||
|
{memberList.map((name, i) => (
|
||||||
<span key={i} className="px-2 py-0.5 bg-primary/10 text-primary text-xs font-medium rounded-full">
|
<span key={i} className="px-2 py-0.5 bg-primary/10 text-primary text-xs font-medium rounded-full">
|
||||||
{name.trim()}
|
{name}
|
||||||
</span>
|
</span>
|
||||||
));
|
))}
|
||||||
})()}
|
</div>
|
||||||
</div>
|
);
|
||||||
)}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
<div className="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
|
|
|
||||||
|
|
@ -483,18 +483,29 @@ function Schedule() {
|
||||||
const currentYearMonth = `${year}-${String(month + 1).padStart(2, '0')}`;
|
const currentYearMonth = `${year}-${String(month + 1).padStart(2, '0')}`;
|
||||||
|
|
||||||
const filteredSchedules = useMemo(() => {
|
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) {
|
if (isSearchMode) {
|
||||||
// 검색 전엔 빈 목록, 검색 후엔 API 결과 (Meilisearch 유사도순 유지)
|
// 검색 전엔 빈 목록, 검색 후엔 API 결과 (Meilisearch 유사도순 유지)
|
||||||
if (!searchTerm) return [];
|
if (!searchTerm) return [];
|
||||||
// 카테고리 필터링 적용
|
// 카테고리 필터링 적용
|
||||||
if (selectedCategories.length === 0) return searchResults;
|
if (selectedCategories.length === 0) return sortWithBirthdayFirst(searchResults);
|
||||||
return searchResults.filter(s => selectedCategories.includes(s.category_id));
|
return sortWithBirthdayFirst(searchResults.filter(s => selectedCategories.includes(s.category_id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 일반 모드: 기존 필터링
|
// 일반 모드: 기존 필터링
|
||||||
return schedules
|
const filtered = schedules
|
||||||
.filter(s => {
|
.filter(s => {
|
||||||
const scheduleDate = s.date ? s.date.split('T')[0] : '';
|
const scheduleDate = s.date ? s.date.split('T')[0] : '';
|
||||||
const matchesDate = selectedDate
|
const matchesDate = selectedDate
|
||||||
|
|
@ -504,6 +515,12 @@ function Schedule() {
|
||||||
return matchesDate && matchesCategory;
|
return matchesDate && matchesCategory;
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.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 dateA = a.date ? a.date.split('T')[0] : '';
|
||||||
const dateB = b.date ? b.date.split('T')[0] : '';
|
const dateB = b.date ? b.date.split('T')[0] : '';
|
||||||
if (dateA !== dateB) return dateA.localeCompare(dateB);
|
if (dateA !== dateB) return dateA.localeCompare(dateB);
|
||||||
|
|
@ -511,6 +528,7 @@ function Schedule() {
|
||||||
const timeB = b.time || '00:00:00';
|
const timeB = b.time || '00:00:00';
|
||||||
return timeA.localeCompare(timeB);
|
return timeA.localeCompare(timeB);
|
||||||
});
|
});
|
||||||
|
return filtered;
|
||||||
}, [schedules, selectedDate, currentYearMonth, selectedCategories, isSearchMode, searchTerm, searchResults]);
|
}, [schedules, selectedDate, currentYearMonth, selectedCategories, isSearchMode, searchTerm, searchResults]);
|
||||||
|
|
||||||
// 가상 스크롤 설정 (검색 모드에서만 활성화, 동적 높이 지원)
|
// 가상 스크롤 설정 (검색 모드에서만 활성화, 동적 높이 지원)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue