fix: 생일 표시 버그 수정 및 데이트픽커 개선
- 생일이 생년 이전 년도에 표시되는 버그 수정 - 페이지 진입 애니메이션 추가 (사전 관리, 일정 추가) - 데이트픽커 12년 단위 이동으로 변경 - 년도 선택 시 월 선택 화면 전환 제거 - 시작 년도 2025년 고정, 이전 이동 비활성화 - PC/모바일 일정 페이지, 관리자 페이지 모두 적용 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2576a244c0
commit
1a9fa54981
7 changed files with 39 additions and 26 deletions
|
|
@ -327,6 +327,11 @@ async function handleMonthlySchedules(db, year, month) {
|
||||||
// 생일 일정 추가
|
// 생일 일정 추가
|
||||||
for (const member of birthdays) {
|
for (const member of birthdays) {
|
||||||
const birthDate = new Date(member.birth_date);
|
const birthDate = new Date(member.birth_date);
|
||||||
|
const birthYear = birthDate.getFullYear();
|
||||||
|
|
||||||
|
// 조회 연도가 생년보다 이전이면 스킵
|
||||||
|
if (year < birthYear) continue;
|
||||||
|
|
||||||
const birthdayThisYear = new Date(year, birthDate.getMonth(), birthDate.getDate());
|
const birthdayThisYear = new Date(year, birthDate.getMonth(), birthDate.getDate());
|
||||||
const dateKey = birthdayThisYear.toISOString().split('T')[0];
|
const dateKey = birthdayThisYear.toISOString().split('T')[0];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,15 @@ function CustomDatePicker({ value, onChange, placeholder = '날짜 선택', show
|
||||||
days.push(i);
|
days.push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
const startYear = Math.floor(year / 10) * 10 - 1;
|
const MIN_YEAR = 2025;
|
||||||
|
const startYear = Math.max(MIN_YEAR, Math.floor(year / 12) * 12 - 1);
|
||||||
const years = Array.from({ length: 12 }, (_, i) => startYear + i);
|
const years = Array.from({ length: 12 }, (_, i) => startYear + i);
|
||||||
|
const canGoPrevYearRange = startYear > MIN_YEAR;
|
||||||
|
|
||||||
const prevMonth = () => setViewDate(new Date(year, month - 1, 1));
|
const prevMonth = () => setViewDate(new Date(year, month - 1, 1));
|
||||||
const nextMonth = () => setViewDate(new Date(year, month + 1, 1));
|
const nextMonth = () => setViewDate(new Date(year, month + 1, 1));
|
||||||
const prevYearRange = () => setViewDate(new Date(year - 10, month, 1));
|
const prevYearRange = () => canGoPrevYearRange && setViewDate(new Date(Math.max(MIN_YEAR, year - 12), month, 1));
|
||||||
const nextYearRange = () => setViewDate(new Date(year + 10, month, 1));
|
const nextYearRange = () => setViewDate(new Date(year + 12, month, 1));
|
||||||
|
|
||||||
const selectDate = (day) => {
|
const selectDate = (day) => {
|
||||||
const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||||
|
|
@ -57,7 +59,6 @@ function CustomDatePicker({ value, onChange, placeholder = '날짜 선택', show
|
||||||
|
|
||||||
const selectYear = (y) => {
|
const selectYear = (y) => {
|
||||||
setViewDate(new Date(y, month, 1));
|
setViewDate(new Date(y, month, 1));
|
||||||
setViewMode('months');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectMonth = (m) => {
|
const selectMonth = (m) => {
|
||||||
|
|
@ -124,7 +125,8 @@ function CustomDatePicker({ value, onChange, placeholder = '날짜 선택', show
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={viewMode === 'years' ? prevYearRange : prevMonth}
|
onClick={viewMode === 'years' ? prevYearRange : prevMonth}
|
||||||
className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors"
|
disabled={viewMode === 'years' && !canGoPrevYearRange}
|
||||||
|
className={`p-1.5 rounded-lg transition-colors ${viewMode === 'years' && !canGoPrevYearRange ? 'opacity-30' : 'hover:bg-gray-100'}`}
|
||||||
>
|
>
|
||||||
<ChevronLeft size={20} className="text-gray-600" />
|
<ChevronLeft size={20} className="text-gray-600" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1239,8 +1239,10 @@ function CalendarPicker({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [yearRangeStart, setYearRangeStart] = useState(Math.floor(year / 12) * 12);
|
const MIN_YEAR = 2025;
|
||||||
|
const [yearRangeStart, setYearRangeStart] = useState(MIN_YEAR);
|
||||||
const yearRange = Array.from({ length: 12 }, (_, i) => yearRangeStart + i);
|
const yearRange = Array.from({ length: 12 }, (_, i) => yearRangeStart + i);
|
||||||
|
const canGoPrevYearRange = yearRangeStart > MIN_YEAR;
|
||||||
|
|
||||||
// 배경 스크롤 막기
|
// 배경 스크롤 막기
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -1364,16 +1366,17 @@ function CalendarPicker({
|
||||||
>
|
>
|
||||||
{/* 년도 범위 헤더 */}
|
{/* 년도 범위 헤더 */}
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<button
|
<button
|
||||||
onClick={() => setYearRangeStart(yearRangeStart - 12)}
|
onClick={() => canGoPrevYearRange && setYearRangeStart(Math.max(MIN_YEAR, yearRangeStart - 12))}
|
||||||
className="p-1"
|
disabled={!canGoPrevYearRange}
|
||||||
|
className={`p-1 ${canGoPrevYearRange ? '' : 'opacity-30'}`}
|
||||||
>
|
>
|
||||||
<ChevronLeft size={18} />
|
<ChevronLeft size={18} />
|
||||||
</button>
|
</button>
|
||||||
<span className="font-semibold text-sm">
|
<span className="font-semibold text-sm">
|
||||||
{yearRangeStart} - {yearRangeStart + 11}
|
{yearRangeStart} - {yearRangeStart + 11}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => setYearRangeStart(yearRangeStart + 12)}
|
onClick={() => setYearRangeStart(yearRangeStart + 12)}
|
||||||
className="p-1"
|
className="p-1"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -273,9 +273,11 @@ function AdminSchedule() {
|
||||||
const days = ['일', '월', '화', '수', '목', '금', '토'];
|
const days = ['일', '월', '화', '수', '목', '금', '토'];
|
||||||
const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
|
const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
|
||||||
|
|
||||||
// 년도 범위 (현재 년도 기준 10년 단위 - Schedule.jsx와 동일)
|
// 년도 범위 (2025년부터 시작, 12년 단위)
|
||||||
const startYear = Math.floor(year / 10) * 10 - 1;
|
const MIN_YEAR = 2025;
|
||||||
|
const startYear = Math.max(MIN_YEAR, Math.floor(year / 12) * 12 - 1);
|
||||||
const yearRange = Array.from({ length: 12 }, (_, i) => startYear + i);
|
const yearRange = Array.from({ length: 12 }, (_, i) => startYear + i);
|
||||||
|
const canGoPrevYearRange = startYear > MIN_YEAR;
|
||||||
|
|
||||||
// 현재 년도/월 확인 함수
|
// 현재 년도/월 확인 함수
|
||||||
const isCurrentYear = (y) => new Date().getFullYear() === y;
|
const isCurrentYear = (y) => new Date().getFullYear() === y;
|
||||||
|
|
@ -482,14 +484,13 @@ function AdminSchedule() {
|
||||||
setSchedules([]); // 이전 달 데이터 즉시 초기화
|
setSchedules([]); // 이전 달 데이터 즉시 초기화
|
||||||
};
|
};
|
||||||
|
|
||||||
// 년도 범위 이동
|
// 년도 범위 이동 (12년 단위, 2025년 이전 불가)
|
||||||
const prevYearRange = () => setCurrentDate(new Date(year - 10, month, 1));
|
const prevYearRange = () => canGoPrevYearRange && setCurrentDate(new Date(Math.max(MIN_YEAR, year - 12), month, 1));
|
||||||
const nextYearRange = () => setCurrentDate(new Date(year + 10, month, 1));
|
const nextYearRange = () => setCurrentDate(new Date(year + 12, month, 1));
|
||||||
|
|
||||||
// 년도 선택 시 월 선택 모드로 전환
|
// 년도 선택
|
||||||
const selectYear = (newYear) => {
|
const selectYear = (newYear) => {
|
||||||
setCurrentDate(new Date(newYear, month, 1));
|
setCurrentDate(new Date(newYear, month, 1));
|
||||||
setViewMode('months');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 월 선택 시 적용 후 닫기
|
// 월 선택 시 적용 후 닫기
|
||||||
|
|
@ -730,7 +731,8 @@ function AdminSchedule() {
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<button
|
<button
|
||||||
onClick={prevYearRange}
|
onClick={prevYearRange}
|
||||||
className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors"
|
disabled={!canGoPrevYearRange}
|
||||||
|
className={`p-1.5 rounded-lg transition-colors ${canGoPrevYearRange ? 'hover:bg-gray-100' : 'opacity-30'}`}
|
||||||
>
|
>
|
||||||
<ChevronLeft size={20} className="text-gray-600" />
|
<ChevronLeft size={20} className="text-gray-600" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -613,7 +613,7 @@ function AdminScheduleDict() {
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto max-h-[500px] overflow-y-auto">
|
<div className="overflow-x-auto max-h-[500px] overflow-y-auto">
|
||||||
<table className="w-full">
|
<table className="w-full">
|
||||||
<thead className="bg-gray-50 sticky top-0">
|
<thead className="bg-gray-50 sticky top-0 z-30">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-16">#</th>
|
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-16">#</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">단어</th>
|
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">단어</th>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ const formVariants = {
|
||||||
visible: {
|
visible: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
y: 0,
|
y: 0,
|
||||||
transition: { duration: 0.3, ease: "easeOut" },
|
transition: { duration: 0.3, ease: "easeOut", delay: 0.3 },
|
||||||
},
|
},
|
||||||
exit: {
|
exit: {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,6 @@ function Schedule() {
|
||||||
|
|
||||||
const selectYear = (newYear) => {
|
const selectYear = (newYear) => {
|
||||||
setCurrentDate(new Date(newYear, month, 1));
|
setCurrentDate(new Date(newYear, month, 1));
|
||||||
setViewMode('months');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectMonth = (newMonth) => {
|
const selectMonth = (newMonth) => {
|
||||||
|
|
@ -568,12 +567,14 @@ function Schedule() {
|
||||||
return year === now.getFullYear() && m === now.getMonth();
|
return year === now.getFullYear() && m === now.getMonth();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 연도 선택 범위
|
// 연도 선택 범위 (2025년부터 시작)
|
||||||
const [yearRangeStart, setYearRangeStart] = useState(currentYear - 1);
|
const MIN_YEAR = 2025;
|
||||||
|
const [yearRangeStart, setYearRangeStart] = useState(MIN_YEAR);
|
||||||
const yearRange = Array.from({ length: 12 }, (_, i) => yearRangeStart + i);
|
const yearRange = Array.from({ length: 12 }, (_, i) => yearRangeStart + i);
|
||||||
const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
|
const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
|
||||||
const prevYearRange = () => setYearRangeStart(prev => prev - 3);
|
const canGoPrevYearRange = yearRangeStart > MIN_YEAR;
|
||||||
const nextYearRange = () => setYearRangeStart(prev => prev + 3);
|
const prevYearRange = () => canGoPrevYearRange && setYearRangeStart(prev => Math.max(MIN_YEAR, prev - 12));
|
||||||
|
const nextYearRange = () => setYearRangeStart(prev => prev + 12);
|
||||||
|
|
||||||
// 선택된 카테고리 이름
|
// 선택된 카테고리 이름
|
||||||
const getSelectedCategoryNames = () => {
|
const getSelectedCategoryNames = () => {
|
||||||
|
|
@ -680,7 +681,7 @@ function Schedule() {
|
||||||
className="absolute top-20 left-8 right-8 mx-auto w-80 bg-white rounded-xl shadow-lg border border-gray-200 p-4 z-10"
|
className="absolute top-20 left-8 right-8 mx-auto w-80 bg-white rounded-xl shadow-lg border border-gray-200 p-4 z-10"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<button onClick={prevYearRange} className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors">
|
<button onClick={prevYearRange} disabled={!canGoPrevYearRange} className={`p-1.5 rounded-lg transition-colors ${canGoPrevYearRange ? 'hover:bg-gray-100' : 'opacity-30'}`}>
|
||||||
<ChevronLeft size={20} className="text-gray-600" />
|
<ChevronLeft size={20} className="text-gray-600" />
|
||||||
</button>
|
</button>
|
||||||
<span className="font-medium text-gray-900">
|
<span className="font-medium text-gray-900">
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue