모바일 일정: 달력 점 표시 PC 버전과 통일, 멤버 배지 초록색, 월 변경 시 날짜 선택 로직 개선

This commit is contained in:
caadiq 2026-01-07 16:00:42 +09:00
parent f2b0170cf8
commit 030c495c01

View file

@ -13,7 +13,7 @@ function MobileSchedule() {
const [isSearchMode, setIsSearchMode] = useState(false); const [isSearchMode, setIsSearchMode] = useState(false);
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [showCalendar, setShowCalendar] = useState(false); const [showCalendar, setShowCalendar] = useState(false);
const [calendarViewDate, setCalendarViewDate] = useState(new Date()); // const [calendarViewDate, setCalendarViewDate] = useState(() => new Date(selectedDate)); //
const [calendarShowYearMonth, setCalendarShowYearMonth] = useState(false); // const [calendarShowYearMonth, setCalendarShowYearMonth] = useState(false); //
// //
@ -84,7 +84,17 @@ function MobileSchedule() {
const changeMonth = (delta) => { const changeMonth = (delta) => {
const newDate = new Date(selectedDate); const newDate = new Date(selectedDate);
newDate.setMonth(newDate.getMonth() + delta); newDate.setMonth(newDate.getMonth() + delta);
// , 1
const today = new Date();
if (newDate.getFullYear() === today.getFullYear() && newDate.getMonth() === today.getMonth()) {
newDate.setDate(today.getDate());
} else {
newDate.setDate(1);
}
setSelectedDate(newDate); setSelectedDate(newDate);
setCalendarViewDate(newDate);
}; };
// //
@ -306,13 +316,20 @@ function MobileSchedule() {
const month = String(date.getMonth() + 1).padStart(2, '0'); const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0');
const dateStr = `${year}-${month}-${day}`; const dateStr = `${year}-${month}-${day}`;
const hasSchedule = schedules.some(s => s.date.split('T')[0] === dateStr);
// ( 3)
const daySchedules = schedules
.filter(s => s.date?.split('T')[0] === dateStr)
.slice(0, 3);
return ( return (
<button <button
key={date.getDate()} key={date.getDate()}
onClick={() => setSelectedDate(date)} onClick={() => {
className={`flex flex-col items-center min-w-[44px] py-2 px-1 rounded-xl transition-all ${ setSelectedDate(date);
setCalendarViewDate(date);
}}
className={`flex flex-col items-center min-w-[44px] h-[64px] py-2 px-1 rounded-xl transition-all ${
isSelected(date) isSelected(date)
? 'bg-primary text-white' ? 'bg-primary text-white'
: 'hover:bg-gray-100' : 'hover:bg-gray-100'
@ -338,9 +355,20 @@ function MobileSchedule() {
}`}> }`}>
{date.getDate()} {date.getDate()}
</span> </span>
{hasSchedule && !isSelected(date) && ( {/* 일정 점 (최대 3개) */}
<div className="w-1 h-1 rounded-full bg-primary mt-1" /> <div className="flex gap-0.5 mt-1 h-1.5">
)} {!isSelected(date) && daySchedules.map((schedule, i) => {
const cat = categories.find(c => c.id === schedule.category_id);
const color = cat?.color || '#6b7280';
return (
<div
key={i}
className="w-1 h-1 rounded-full"
style={{ backgroundColor: color }}
/>
);
})}
</div>
</button> </button>
); );
})} })}
@ -369,6 +397,8 @@ function MobileSchedule() {
onShowYearMonthChange={setCalendarShowYearMonth} onShowYearMonthChange={setCalendarShowYearMonth}
onSelectDate={(date) => { onSelectDate={(date) => {
setSelectedDate(date); setSelectedDate(date);
setCalendarViewDate(date);
setCalendarShowYearMonth(false);
setShowCalendar(false); setShowCalendar(false);
}} }}
/> />
@ -571,7 +601,7 @@ function TimelineScheduleCard({ schedule, categoryColor, categories, delay = 0 }
memberList.map((name, i) => ( memberList.map((name, i) => (
<span <span
key={i} key={i}
className="px-2 py-0.5 bg-gray-100 text-gray-600 text-xs rounded-lg font-medium" className="px-2.5 py-1 bg-gradient-to-r from-primary to-primary-dark text-white text-xs rounded-lg font-semibold shadow-sm"
> >
{name.trim()} {name.trim()}
</span> </span>
@ -617,7 +647,7 @@ function CalendarPicker({
const scheduleDates = useMemo(() => { const scheduleDates = useMemo(() => {
const dateMap = {}; const dateMap = {};
schedules.forEach(schedule => { schedules.forEach(schedule => {
const date = schedule.date; const date = schedule.date.split('T')[0]; // YYYY-MM-DD
if (!dateMap[date]) { if (!dateMap[date]) {
dateMap[date] = []; dateMap[date] = [];
} }
@ -627,11 +657,13 @@ function CalendarPicker({
return dateMap; return dateMap;
}, [schedules, categories]); }, [schedules, categories]);
const getScheduleColors = (date) => { // ( , 3)
const dateStr = date.toISOString().split('T')[0]; const getDaySchedules = (date) => {
const colors = scheduleDates[dateStr] || []; const y = date.getFullYear();
// 3 const m = String(date.getMonth() + 1).padStart(2, '0');
return [...new Set(colors)].slice(0, 3); const d = String(date.getDate()).padStart(2, '0');
const dateStr = `${y}-${m}-${d}`;
return schedules.filter(s => s.date?.split('T')[0] === dateStr).slice(0, 3);
}; };
const year = viewDate.getFullYear(); const year = viewDate.getFullYear();
@ -691,6 +723,13 @@ function CalendarPicker({
date.getFullYear() === today.getFullYear(); date.getFullYear() === today.getFullYear();
}; };
//
const isSelected = (date) => {
return date.getDate() === selectedDate.getDate() &&
date.getMonth() === selectedDate.getMonth() &&
date.getFullYear() === selectedDate.getFullYear();
};
// - // -
const [internalShowYearMonth, setInternalShowYearMonth] = useState(false); const [internalShowYearMonth, setInternalShowYearMonth] = useState(false);
const showYearMonth = externalShowYearMonth !== undefined ? externalShowYearMonth : internalShowYearMonth; const showYearMonth = externalShowYearMonth !== undefined ? externalShowYearMonth : internalShowYearMonth;
@ -767,19 +806,21 @@ function CalendarPicker({
const dayOfWeek = index % 7; const dayOfWeek = index % 7;
const isSunday = dayOfWeek === 0; const isSunday = dayOfWeek === 0;
const isSaturday = dayOfWeek === 6; const isSaturday = dayOfWeek === 6;
const scheduleColors = item.isCurrentMonth ? getScheduleColors(item.date) : []; const daySchedules = item.isCurrentMonth ? getDaySchedules(item.date) : [];
return ( return (
<button <button
key={index} key={index}
onClick={() => onSelectDate(item.date)} onClick={() => onSelectDate(item.date)}
className="flex flex-col items-center py-1" className="flex flex-col items-center py-2"
> >
<span className={`w-7 h-7 flex items-center justify-center text-xs rounded-full transition-colors ${ <span className={`w-9 h-9 flex items-center justify-center text-sm font-medium rounded-full transition-all ${
!item.isCurrentMonth !item.isCurrentMonth
? 'text-gray-300' ? 'text-gray-300'
: isSelected(item.date)
? 'bg-primary text-white font-bold shadow-lg'
: isToday(item.date) : isToday(item.date)
? 'bg-primary text-white font-bold' ? 'text-primary font-bold'
: isSunday : isSunday
? 'text-red-500 hover:bg-red-50' ? 'text-red-500 hover:bg-red-50'
: isSaturday : isSaturday
@ -788,16 +829,22 @@ function CalendarPicker({
}`}> }`}>
{item.day} {item.day}
</span> </span>
{/* 일정 점 */} {/* 일정 점 - 선택된 날짜에는 표시하지 않음, 최대 3개 */}
{!isSelected(item.date) && daySchedules.length > 0 && (
<div className="flex gap-0.5 mt-0.5 h-1.5"> <div className="flex gap-0.5 mt-0.5 h-1.5">
{scheduleColors.map((color, i) => ( {daySchedules.map((schedule, i) => {
const cat = categories.find(c => c.id === schedule.category_id);
const color = cat?.color || '#6b7280';
return (
<div <div
key={i} key={i}
className="w-1 h-1 rounded-full" className="w-1 h-1 rounded-full"
style={{ backgroundColor: color }} style={{ backgroundColor: color }}
/> />
))} );
})}
</div> </div>
)}
</button> </button>
); );
})} })}
@ -882,10 +929,10 @@ function CalendarPicker({
</motion.div> </motion.div>
) : ( ) : (
<motion.div <motion.div
key="calendar" key={`calendar-${year}-${month}`}
initial={{ opacity: 0, y: 10 }} initial={{ opacity: 0 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1 }}
exit={{ opacity: 0, y: 10 }} exit={{ opacity: 0 }}
transition={{ duration: 0.15 }} transition={{ duration: 0.15 }}
> >
{/* 달력 헤더 - hideHeader일 때 숨김 */} {/* 달력 헤더 - hideHeader일 때 숨김 */}