모바일 일정: 달력 점 표시 PC 버전과 통일, 멤버 배지 초록색, 월 변경 시 날짜 선택 로직 개선
This commit is contained in:
parent
f2b0170cf8
commit
030c495c01
1 changed files with 85 additions and 38 deletions
|
|
@ -13,7 +13,7 @@ function MobileSchedule() {
|
|||
const [isSearchMode, setIsSearchMode] = useState(false);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [showCalendar, setShowCalendar] = useState(false);
|
||||
const [calendarViewDate, setCalendarViewDate] = useState(new Date()); // 달력 뷰 날짜
|
||||
const [calendarViewDate, setCalendarViewDate] = useState(() => new Date(selectedDate)); // 달력 뷰 날짜
|
||||
const [calendarShowYearMonth, setCalendarShowYearMonth] = useState(false); // 달력 년월 선택 모드
|
||||
|
||||
// 달력 월 변경 함수
|
||||
|
|
@ -84,7 +84,17 @@ function MobileSchedule() {
|
|||
const changeMonth = (delta) => {
|
||||
const newDate = new Date(selectedDate);
|
||||
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);
|
||||
setCalendarViewDate(newDate);
|
||||
};
|
||||
|
||||
// 캘린더가 열릴 때 배경 스크롤 방지
|
||||
|
|
@ -306,13 +316,20 @@ function MobileSchedule() {
|
|||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
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 (
|
||||
<button
|
||||
key={date.getDate()}
|
||||
onClick={() => setSelectedDate(date)}
|
||||
className={`flex flex-col items-center min-w-[44px] py-2 px-1 rounded-xl transition-all ${
|
||||
onClick={() => {
|
||||
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)
|
||||
? 'bg-primary text-white'
|
||||
: 'hover:bg-gray-100'
|
||||
|
|
@ -338,9 +355,20 @@ function MobileSchedule() {
|
|||
}`}>
|
||||
{date.getDate()}
|
||||
</span>
|
||||
{hasSchedule && !isSelected(date) && (
|
||||
<div className="w-1 h-1 rounded-full bg-primary mt-1" />
|
||||
)}
|
||||
{/* 일정 점 (최대 3개) */}
|
||||
<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>
|
||||
);
|
||||
})}
|
||||
|
|
@ -369,6 +397,8 @@ function MobileSchedule() {
|
|||
onShowYearMonthChange={setCalendarShowYearMonth}
|
||||
onSelectDate={(date) => {
|
||||
setSelectedDate(date);
|
||||
setCalendarViewDate(date);
|
||||
setCalendarShowYearMonth(false);
|
||||
setShowCalendar(false);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -571,7 +601,7 @@ function TimelineScheduleCard({ schedule, categoryColor, categories, delay = 0 }
|
|||
memberList.map((name, i) => (
|
||||
<span
|
||||
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()}
|
||||
</span>
|
||||
|
|
@ -617,7 +647,7 @@ function CalendarPicker({
|
|||
const scheduleDates = useMemo(() => {
|
||||
const dateMap = {};
|
||||
schedules.forEach(schedule => {
|
||||
const date = schedule.date;
|
||||
const date = schedule.date.split('T')[0]; // YYYY-MM-DD 형식으로 통일
|
||||
if (!dateMap[date]) {
|
||||
dateMap[date] = [];
|
||||
}
|
||||
|
|
@ -627,11 +657,13 @@ function CalendarPicker({
|
|||
return dateMap;
|
||||
}, [schedules, categories]);
|
||||
|
||||
const getScheduleColors = (date) => {
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
const colors = scheduleDates[dateStr] || [];
|
||||
// 최대 3개까지만 표시
|
||||
return [...new Set(colors)].slice(0, 3);
|
||||
// 날짜별 일정 목록 가져오기 (점 표시용, 최대 3개)
|
||||
const getDaySchedules = (date) => {
|
||||
const y = date.getFullYear();
|
||||
const m = String(date.getMonth() + 1).padStart(2, '0');
|
||||
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();
|
||||
|
|
@ -691,6 +723,13 @@ function CalendarPicker({
|
|||
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 showYearMonth = externalShowYearMonth !== undefined ? externalShowYearMonth : internalShowYearMonth;
|
||||
|
|
@ -767,37 +806,45 @@ function CalendarPicker({
|
|||
const dayOfWeek = index % 7;
|
||||
const isSunday = dayOfWeek === 0;
|
||||
const isSaturday = dayOfWeek === 6;
|
||||
const scheduleColors = item.isCurrentMonth ? getScheduleColors(item.date) : [];
|
||||
const daySchedules = item.isCurrentMonth ? getDaySchedules(item.date) : [];
|
||||
|
||||
return (
|
||||
<button
|
||||
key={index}
|
||||
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
|
||||
? 'text-gray-300'
|
||||
: isToday(item.date)
|
||||
? 'bg-primary text-white font-bold'
|
||||
: isSunday
|
||||
? 'text-red-500 hover:bg-red-50'
|
||||
: isSaturday
|
||||
? 'text-blue-500 hover:bg-blue-50'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
: isSelected(item.date)
|
||||
? 'bg-primary text-white font-bold shadow-lg'
|
||||
: isToday(item.date)
|
||||
? 'text-primary font-bold'
|
||||
: isSunday
|
||||
? 'text-red-500 hover:bg-red-50'
|
||||
: isSaturday
|
||||
? 'text-blue-500 hover:bg-blue-50'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}>
|
||||
{item.day}
|
||||
</span>
|
||||
{/* 일정 점 */}
|
||||
<div className="flex gap-0.5 mt-0.5 h-1.5">
|
||||
{scheduleColors.map((color, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="w-1 h-1 rounded-full"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{/* 일정 점 - 선택된 날짜에는 표시하지 않음, 최대 3개 */}
|
||||
{!isSelected(item.date) && daySchedules.length > 0 && (
|
||||
<div className="flex gap-0.5 mt-0.5 h-1.5">
|
||||
{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>
|
||||
);
|
||||
})}
|
||||
|
|
@ -882,10 +929,10 @@ function CalendarPicker({
|
|||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="calendar"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 10 }}
|
||||
key={`calendar-${year}-${month}`}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
>
|
||||
{/* 달력 헤더 - hideHeader일 때 숨김 */}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue