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 { useState, useRef, useEffect, useMemo } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
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 { getTodayKST, dayjs } from '@/utils';
|
||||||
import { MIN_YEAR, WEEKDAYS, MONTH_NAMES } from '@/constants';
|
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) => {
|
const selectYear = (newYear) => {
|
||||||
onDateChange(new Date(newYear, month, 1));
|
onDateChange(new Date(newYear, month, 1));
|
||||||
};
|
};
|
||||||
|
|
@ -133,7 +144,7 @@ function Calendar({
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
className={disabled ? 'pointer-events-none' : ''}
|
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">
|
<div className="flex items-center justify-between mb-8">
|
||||||
<button
|
<button
|
||||||
|
|
@ -318,12 +329,20 @@ function Calendar({
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</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">
|
<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="w-2 h-2 rounded-full bg-primary flex-shrink-0" />
|
||||||
<span className="leading-none">일정 있음</span>
|
<span className="leading-none">일정 있음</span>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,10 @@ function CategoryFilter({
|
||||||
<motion.div
|
<motion.div
|
||||||
animate={{ opacity: disabled ? 0.4 : 1 }}
|
animate={{ opacity: disabled ? 0.4 : 1 }}
|
||||||
transition={{ duration: 0.2 }}
|
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>
|
<h3 className="font-bold text-gray-900 mb-4 flex-shrink-0">카테고리</h3>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1 flex-1 min-h-0 overflow-y-auto -mr-3 pr-3">
|
||||||
{/* 전체 */}
|
{/* 전체 */}
|
||||||
<button
|
<button
|
||||||
onClick={onClear}
|
onClick={onClear}
|
||||||
|
|
|
||||||
|
|
@ -206,14 +206,13 @@ function PCSchedule() {
|
||||||
return Array.from(categoryMap.values());
|
return Array.from(categoryMap.values());
|
||||||
}, [schedules]);
|
}, [schedules]);
|
||||||
|
|
||||||
// 카테고리별 카운트
|
// 카테고리별 카운트 (선택 날짜와 무관하게 해당 달 전체 기준)
|
||||||
const categoryCounts = useMemo(() => {
|
const categoryCounts = useMemo(() => {
|
||||||
const source = isSearchMode && searchTerm ? searchResults : schedules;
|
const source = isSearchMode && searchTerm ? searchResults : schedules;
|
||||||
const counts = new Map();
|
const counts = new Map();
|
||||||
let total = 0;
|
let total = 0;
|
||||||
|
|
||||||
source.forEach((s) => {
|
source.forEach((s) => {
|
||||||
if (!(isSearchMode && searchTerm) && selectedDate && s.date !== selectedDate) return;
|
|
||||||
const catId = s.category_id;
|
const catId = s.category_id;
|
||||||
if (catId) {
|
if (catId) {
|
||||||
counts.set(catId, (counts.get(catId) || 0) + 1);
|
counts.set(catId, (counts.get(catId) || 0) + 1);
|
||||||
|
|
@ -222,7 +221,7 @@ function PCSchedule() {
|
||||||
});
|
});
|
||||||
counts.set('total', total);
|
counts.set('total', total);
|
||||||
return counts;
|
return counts;
|
||||||
}, [schedules, searchResults, isSearchMode, searchTerm, selectedDate]);
|
}, [schedules, searchResults, isSearchMode, searchTerm]);
|
||||||
|
|
||||||
// 카테고리 색상/이름 가져오기
|
// 카테고리 색상/이름 가져오기
|
||||||
const getCategoryColor = useCallback(
|
const getCategoryColor = useCallback(
|
||||||
|
|
@ -345,8 +344,9 @@ function PCSchedule() {
|
||||||
initial={{ opacity: 0, x: -20 }}
|
initial={{ opacity: 0, x: -20 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ delay: 0.3, duration: 0.4 }}
|
transition={{ delay: 0.3, duration: 0.4 }}
|
||||||
className="space-y-6"
|
className="flex flex-col gap-6 min-h-0"
|
||||||
>
|
>
|
||||||
|
<div className="flex-shrink-0">
|
||||||
<Calendar
|
<Calendar
|
||||||
currentDate={currentDate}
|
currentDate={currentDate}
|
||||||
onDateChange={setCurrentDate}
|
onDateChange={setCurrentDate}
|
||||||
|
|
@ -356,6 +356,7 @@ function PCSchedule() {
|
||||||
getCategoryColor={getCategoryColor}
|
getCategoryColor={getCategoryColor}
|
||||||
disabled={isSearchMode}
|
disabled={isSearchMode}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<CategoryFilter
|
<CategoryFilter
|
||||||
categories={categories}
|
categories={categories}
|
||||||
selectedCategories={selectedCategories}
|
selectedCategories={selectedCategories}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue