feat(schedule): 카테고리 필터 개선 및 달력 오늘 버튼 추가
- 카테고리 카운트를 선택 날짜와 무관하게 해당 달 전체 기준으로 변경 - 카테고리 섹션이 길어지면 카드 내부 스크롤 (평소엔 콘텐츠 크기 유지) - 달력 하단에 오늘 날짜로 이동하는 버튼 추가 - 달력 하단 여백 24px → 20px Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
51e8f17cc1
commit
9e3bff9762
3 changed files with 40 additions and 20 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { useState, useRef, useEffect, useMemo } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { ChevronLeft, ChevronRight, ChevronDown } from 'lucide-react';
|
||||
import { ChevronLeft, ChevronRight, ChevronDown, CalendarDays } from 'lucide-react';
|
||||
import { getTodayKST, dayjs } from '@/utils';
|
||||
import { MIN_YEAR, WEEKDAYS, MONTH_NAMES } from '@/constants';
|
||||
|
||||
|
|
@ -93,6 +93,17 @@ function Calendar({
|
|||
}
|
||||
};
|
||||
|
||||
// 오늘 날짜로 이동
|
||||
const goToToday = () => {
|
||||
const today = new Date();
|
||||
const isFuture =
|
||||
today.getFullYear() > year ||
|
||||
(today.getFullYear() === year && today.getMonth() > month);
|
||||
setSlideDirection(isFuture ? 1 : -1);
|
||||
onDateChange(today);
|
||||
onSelectDate(getTodayKST());
|
||||
};
|
||||
|
||||
const selectYear = (newYear) => {
|
||||
onDateChange(new Date(newYear, month, 1));
|
||||
};
|
||||
|
|
@ -133,7 +144,7 @@ function Calendar({
|
|||
transition={{ duration: 0.2 }}
|
||||
className={disabled ? 'pointer-events-none' : ''}
|
||||
>
|
||||
<div className="bg-white rounded-2xl shadow-sm pt-8 px-8 pb-6 relative" ref={pickerRef}>
|
||||
<div className="bg-white rounded-2xl shadow-sm pt-8 px-8 pb-5 relative" ref={pickerRef}>
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<button
|
||||
|
|
@ -318,12 +329,20 @@ function Calendar({
|
|||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
{/* 범례 */}
|
||||
<div className="mt-6 pt-4 border-t border-gray-100 flex items-center text-sm">
|
||||
{/* 범례 + 오늘 버튼 */}
|
||||
<div className="mt-6 pt-4 border-t border-gray-100 flex items-center justify-between text-sm">
|
||||
<div className="flex items-center gap-1.5 text-gray-500">
|
||||
<span className="w-2 h-2 rounded-full bg-primary flex-shrink-0" />
|
||||
<span className="leading-none">일정 있음</span>
|
||||
</div>
|
||||
<button
|
||||
aria-label="오늘 날짜로 이동"
|
||||
onClick={goToToday}
|
||||
className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg text-primary font-medium hover:bg-primary/10 transition-colors"
|
||||
>
|
||||
<CalendarDays size={14} aria-hidden="true" />
|
||||
오늘
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ function CategoryFilter({
|
|||
<motion.div
|
||||
animate={{ opacity: disabled ? 0.4 : 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className={`bg-white rounded-2xl shadow-sm p-6 ${disabled ? 'pointer-events-none' : ''}`}
|
||||
className={`bg-white rounded-2xl shadow-sm p-6 flex flex-col min-h-0 ${disabled ? 'pointer-events-none' : ''}`}
|
||||
>
|
||||
<h3 className="font-bold text-gray-900 mb-4">카테고리</h3>
|
||||
<div className="space-y-1">
|
||||
<h3 className="font-bold text-gray-900 mb-4 flex-shrink-0">카테고리</h3>
|
||||
<div className="space-y-1 flex-1 min-h-0 overflow-y-auto -mr-3 pr-3">
|
||||
{/* 전체 */}
|
||||
<button
|
||||
onClick={onClear}
|
||||
|
|
|
|||
|
|
@ -206,14 +206,13 @@ function PCSchedule() {
|
|||
return Array.from(categoryMap.values());
|
||||
}, [schedules]);
|
||||
|
||||
// 카테고리별 카운트
|
||||
// 카테고리별 카운트 (선택 날짜와 무관하게 해당 달 전체 기준)
|
||||
const categoryCounts = useMemo(() => {
|
||||
const source = isSearchMode && searchTerm ? searchResults : schedules;
|
||||
const counts = new Map();
|
||||
let total = 0;
|
||||
|
||||
source.forEach((s) => {
|
||||
if (!(isSearchMode && searchTerm) && selectedDate && s.date !== selectedDate) return;
|
||||
const catId = s.category_id;
|
||||
if (catId) {
|
||||
counts.set(catId, (counts.get(catId) || 0) + 1);
|
||||
|
|
@ -222,7 +221,7 @@ function PCSchedule() {
|
|||
});
|
||||
counts.set('total', total);
|
||||
return counts;
|
||||
}, [schedules, searchResults, isSearchMode, searchTerm, selectedDate]);
|
||||
}, [schedules, searchResults, isSearchMode, searchTerm]);
|
||||
|
||||
// 카테고리 색상/이름 가져오기
|
||||
const getCategoryColor = useCallback(
|
||||
|
|
@ -345,17 +344,19 @@ function PCSchedule() {
|
|||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.3, duration: 0.4 }}
|
||||
className="space-y-6"
|
||||
className="flex flex-col gap-6 min-h-0"
|
||||
>
|
||||
<Calendar
|
||||
currentDate={currentDate}
|
||||
onDateChange={setCurrentDate}
|
||||
selectedDate={selectedDate}
|
||||
onSelectDate={setSelectedDate}
|
||||
schedules={calendarSchedules}
|
||||
getCategoryColor={getCategoryColor}
|
||||
disabled={isSearchMode}
|
||||
/>
|
||||
<div className="flex-shrink-0">
|
||||
<Calendar
|
||||
currentDate={currentDate}
|
||||
onDateChange={setCurrentDate}
|
||||
selectedDate={selectedDate}
|
||||
onSelectDate={setSelectedDate}
|
||||
schedules={calendarSchedules}
|
||||
getCategoryColor={getCategoryColor}
|
||||
disabled={isSearchMode}
|
||||
/>
|
||||
</div>
|
||||
<CategoryFilter
|
||||
categories={categories}
|
||||
selectedCategories={selectedCategories}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue