feat(schedule): 카테고리 필터 개선 및 달력 오늘 버튼 추가

- 카테고리 카운트를 선택 날짜와 무관하게 해당 달 전체 기준으로 변경
- 카테고리 섹션이 길어지면 카드 내부 스크롤 (평소엔 콘텐츠 크기 유지)
- 달력 하단에 오늘 날짜로 이동하는 버튼 추가
- 달력 하단 여백 24px → 20px

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-05-20 22:47:13 +09:00
parent 51e8f17cc1
commit 9e3bff9762
3 changed files with 40 additions and 20 deletions

View file

@ -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>

View file

@ -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}

View file

@ -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}