diff --git a/frontend/src/components/admin/CustomDatePicker.jsx b/frontend/src/components/admin/CustomDatePicker.jsx
new file mode 100644
index 0000000..98abb0c
--- /dev/null
+++ b/frontend/src/components/admin/CustomDatePicker.jsx
@@ -0,0 +1,261 @@
+/**
+ * 커스텀 데이트픽커 컴포넌트
+ * 연/월/일 선택이 가능한 드롭다운 형태의 날짜 선택기
+ */
+import { useState, useEffect, useRef } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import { Calendar, ChevronLeft, ChevronRight, ChevronDown } from 'lucide-react';
+
+function CustomDatePicker({ value, onChange, placeholder = '날짜 선택', showDayOfWeek = false }) {
+ const [isOpen, setIsOpen] = useState(false);
+ const [viewMode, setViewMode] = useState('days');
+ const [viewDate, setViewDate] = useState(() => {
+ if (value) return new Date(value);
+ return new Date();
+ });
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const handleClickOutside = (e) => {
+ if (ref.current && !ref.current.contains(e.target)) {
+ setIsOpen(false);
+ setViewMode('days');
+ }
+ };
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => document.removeEventListener('mousedown', handleClickOutside);
+ }, []);
+
+ const year = viewDate.getFullYear();
+ const month = viewDate.getMonth();
+
+ const firstDay = new Date(year, month, 1).getDay();
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
+
+ const days = [];
+ for (let i = 0; i < firstDay; i++) {
+ days.push(null);
+ }
+ for (let i = 1; i <= daysInMonth; i++) {
+ days.push(i);
+ }
+
+ const startYear = Math.floor(year / 10) * 10 - 1;
+ const years = Array.from({ length: 12 }, (_, i) => startYear + i);
+
+ const prevMonth = () => 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 nextYearRange = () => setViewDate(new Date(year + 10, month, 1));
+
+ const selectDate = (day) => {
+ const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
+ onChange(dateStr);
+ setIsOpen(false);
+ setViewMode('days');
+ };
+
+ const selectYear = (y) => {
+ setViewDate(new Date(y, month, 1));
+ setViewMode('months');
+ };
+
+ const selectMonth = (m) => {
+ setViewDate(new Date(year, m, 1));
+ setViewMode('days');
+ };
+
+ // 날짜 표시 포맷 (요일 포함 옵션)
+ const formatDisplayDate = (dateStr) => {
+ if (!dateStr) return '';
+ const [y, m, d] = dateStr.split('-');
+ if (showDayOfWeek) {
+ const dayNames = ['일', '월', '화', '수', '목', '금', '토'];
+ const date = new Date(parseInt(y), parseInt(m) - 1, parseInt(d));
+ const dayOfWeek = dayNames[date.getDay()];
+ return `${y}년 ${parseInt(m)}월 ${parseInt(d)}일 (${dayOfWeek})`;
+ }
+ return `${y}년 ${parseInt(m)}월 ${parseInt(d)}일`;
+ };
+
+ const isSelected = (day) => {
+ if (!value || !day) return false;
+ const [y, m, d] = value.split('-');
+ return parseInt(y) === year && parseInt(m) === month + 1 && parseInt(d) === day;
+ };
+
+ const isToday = (day) => {
+ if (!day) return false;
+ const today = new Date();
+ return today.getFullYear() === year && today.getMonth() === month && today.getDate() === day;
+ };
+
+ const isCurrentYear = (y) => new Date().getFullYear() === y;
+ const isCurrentMonth = (m) => {
+ const today = new Date();
+ return today.getFullYear() === year && today.getMonth() === m;
+ };
+
+ const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
+
+ return (
+
+
+
+
+ {isOpen && (
+
+
+
+
+
+
+
+
+ {viewMode === 'years' && (
+
+ 년도
+
+ {years.map((y) => (
+
+ ))}
+
+ 월
+
+ {monthNames.map((m, i) => (
+
+ ))}
+
+
+ )}
+
+ {viewMode === 'months' && (
+
+ 월 선택
+
+ {monthNames.map((m, i) => (
+
+ ))}
+
+
+ )}
+
+ {viewMode === 'days' && (
+
+
+ {['일', '월', '화', '수', '목', '금', '토'].map((d, i) => (
+
+ {d}
+
+ ))}
+
+
+ {days.map((day, i) => {
+ const dayOfWeek = i % 7;
+ return (
+
+ );
+ })}
+
+
+ )}
+
+
+ )}
+
+
+ );
+}
+
+export default CustomDatePicker;
diff --git a/frontend/src/pages/pc/admin/AdminAlbumForm.jsx b/frontend/src/pages/pc/admin/AdminAlbumForm.jsx
index 3131f83..e19ddff 100644
--- a/frontend/src/pages/pc/admin/AdminAlbumForm.jsx
+++ b/frontend/src/pages/pc/admin/AdminAlbumForm.jsx
@@ -3,9 +3,10 @@ import { useNavigate, useParams, Link } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import {
Save, Home, ChevronRight, LogOut, Music, Trash2, Plus, Image, Star,
- ChevronDown, ChevronLeft, Calendar
+ ChevronDown
} from 'lucide-react';
import Toast from '../../../components/Toast';
+import CustomDatePicker from '../../../components/admin/CustomDatePicker';
// 커스텀 드롭다운 컴포넌트
function CustomSelect({ value, onChange, options, placeholder }) {
@@ -71,280 +72,6 @@ function CustomSelect({ value, onChange, options, placeholder }) {
);
}
-// 커스텀 데이트픽커 컴포넌트
-function CustomDatePicker({ value, onChange }) {
- const [isOpen, setIsOpen] = useState(false);
- const [viewMode, setViewMode] = useState('days'); // 'days' | 'months' | 'years'
- const [viewDate, setViewDate] = useState(() => {
- if (value) return new Date(value);
- return new Date();
- });
- const ref = useRef(null);
-
- useEffect(() => {
- const handleClickOutside = (e) => {
- if (ref.current && !ref.current.contains(e.target)) {
- setIsOpen(false);
- setViewMode('days');
- }
- };
- document.addEventListener('mousedown', handleClickOutside);
- return () => document.removeEventListener('mousedown', handleClickOutside);
- }, []);
-
- const year = viewDate.getFullYear();
- const month = viewDate.getMonth();
-
- const firstDay = new Date(year, month, 1).getDay();
- const daysInMonth = new Date(year, month + 1, 0).getDate();
-
- const days = [];
- for (let i = 0; i < firstDay; i++) {
- days.push(null);
- }
- for (let i = 1; i <= daysInMonth; i++) {
- days.push(i);
- }
-
- // 년도 범위 (현재 년도 기준 -10 ~ +10)
- const startYear = Math.floor(year / 10) * 10 - 1;
- const years = Array.from({ length: 12 }, (_, i) => startYear + i);
-
- const prevMonth = () => 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 nextYearRange = () => setViewDate(new Date(year + 10, month, 1));
-
- const selectDate = (day) => {
- const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
- onChange(dateStr);
- setIsOpen(false);
- setViewMode('days');
- };
-
- const selectYear = (y) => {
- setViewDate(new Date(y, month, 1));
- setViewMode('months');
- };
-
- const selectMonth = (m) => {
- setViewDate(new Date(year, m, 1));
- setViewMode('days');
- };
-
- const formatDisplayDate = (dateStr) => {
- if (!dateStr) return '';
- const [y, m, d] = dateStr.split('-');
- return `${y}년 ${parseInt(m)}월 ${parseInt(d)}일`;
- };
-
- const isSelected = (day) => {
- if (!value || !day) return false;
- const [y, m, d] = value.split('-');
- return parseInt(y) === year && parseInt(m) === month + 1 && parseInt(d) === day;
- };
-
- const isToday = (day) => {
- if (!day) return false;
- const today = new Date();
- return today.getFullYear() === year && today.getMonth() === month && today.getDate() === day;
- };
-
- const isCurrentYear = (y) => {
- return new Date().getFullYear() === y;
- };
-
- const isCurrentMonth = (m) => {
- const today = new Date();
- return today.getFullYear() === year && today.getMonth() === m;
- };
-
- const months = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
-
- return (
-
-
-
-
- {isOpen && (
-
- {/* 헤더 */}
-
-
-
-
-
-
-
- {viewMode === 'years' && (
-
- {/* 년도 라벨 */}
- 년도
-
- {/* 년도 그리드 */}
-
- {years.map((y) => (
-
- ))}
-
-
- {/* 월 라벨 */}
- 월
-
- {/* 월 그리드 */}
-
- {months.map((m, i) => (
-
- ))}
-
-
- )}
-
- {viewMode === 'months' && (
-
- {/* 월 라벨 */}
- 월 선택
-
- {/* 월 그리드 */}
-
- {months.map((m, i) => (
-
- ))}
-
-
- )}
-
- {viewMode === 'days' && (
-
- {/* 요일 */}
-
- {['일', '월', '화', '수', '목', '금', '토'].map((d, i) => (
-
- {d}
-
- ))}
-
-
- {/* 날짜 */}
-
- {days.map((day, i) => (
-
- ))}
-
-
- )}
-
-
- )}
-
-
- );
-}
-
function AdminAlbumForm() {
const navigate = useNavigate();
const { id } = useParams();
diff --git a/frontend/src/pages/pc/admin/AdminMemberEdit.jsx b/frontend/src/pages/pc/admin/AdminMemberEdit.jsx
index f26e3d1..a98a34d 100644
--- a/frontend/src/pages/pc/admin/AdminMemberEdit.jsx
+++ b/frontend/src/pages/pc/admin/AdminMemberEdit.jsx
@@ -3,249 +3,13 @@ import { useNavigate, useParams, Link } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import {
Save, Upload, LogOut,
- Home, ChevronRight, ChevronLeft, ChevronDown, User, Instagram, Calendar, Briefcase
+ Home, ChevronRight, User, Instagram, Calendar, Briefcase
} from 'lucide-react';
import Toast from '../../../components/Toast';
+import CustomDatePicker from '../../../components/admin/CustomDatePicker';
import * as authApi from '../../../api/admin/auth';
import * as membersApi from '../../../api/admin/members';
-// 커스텀 데이트픽커 컴포넌트
-function CustomDatePicker({ value, onChange }) {
- const [isOpen, setIsOpen] = useState(false);
- const [viewMode, setViewMode] = useState('days');
- const [viewDate, setViewDate] = useState(() => {
- if (value) return new Date(value);
- return new Date();
- });
- const ref = useRef(null);
-
- useEffect(() => {
- const handleClickOutside = (e) => {
- if (ref.current && !ref.current.contains(e.target)) {
- setIsOpen(false);
- setViewMode('days');
- }
- };
- document.addEventListener('mousedown', handleClickOutside);
- return () => document.removeEventListener('mousedown', handleClickOutside);
- }, []);
-
- const year = viewDate.getFullYear();
- const month = viewDate.getMonth();
-
- const firstDay = new Date(year, month, 1).getDay();
- const daysInMonth = new Date(year, month + 1, 0).getDate();
-
- const days = [];
- for (let i = 0; i < firstDay; i++) {
- days.push(null);
- }
- for (let i = 1; i <= daysInMonth; i++) {
- days.push(i);
- }
-
- const startYear = Math.floor(year / 10) * 10 - 1;
- const years = Array.from({ length: 12 }, (_, i) => startYear + i);
-
- const prevMonth = () => 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 nextYearRange = () => setViewDate(new Date(year + 10, month, 1));
-
- const selectDate = (day) => {
- const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
- onChange(dateStr);
- setIsOpen(false);
- setViewMode('days');
- };
-
- const selectYear = (y) => {
- setViewDate(new Date(y, month, 1));
- setViewMode('months');
- };
-
- const selectMonth = (m) => {
- setViewDate(new Date(year, m, 1));
- setViewMode('days');
- };
-
- const formatDisplayDate = (dateStr) => {
- if (!dateStr) return '';
- const [y, m, d] = dateStr.split('-');
- return `${y}년 ${parseInt(m)}월 ${parseInt(d)}일`;
- };
-
- const isSelected = (day) => {
- if (!value || !day) return false;
- const [y, m, d] = value.split('-');
- return parseInt(y) === year && parseInt(m) === month + 1 && parseInt(d) === day;
- };
-
- const isToday = (day) => {
- if (!day) return false;
- const today = new Date();
- return today.getFullYear() === year && today.getMonth() === month && today.getDate() === day;
- };
-
- const isCurrentYear = (y) => new Date().getFullYear() === y;
- const isCurrentMonth = (m) => {
- const today = new Date();
- return today.getFullYear() === year && today.getMonth() === m;
- };
-
- const months = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
-
- return (
-
-
-
-
- {isOpen && (
-
-
-
-
-
-
-
-
- {viewMode === 'years' && (
-
- 년도
-
- {years.map((y) => (
-
- ))}
-
- 월
-
- {months.map((m, i) => (
-
- ))}
-
-
- )}
-
- {viewMode === 'months' && (
-
- 월 선택
-
- {months.map((m, i) => (
-
- ))}
-
-
- )}
-
- {viewMode === 'days' && (
-
-
- {['일', '월', '화', '수', '목', '금', '토'].map((d, i) => (
-
- {d}
-
- ))}
-
-
- {days.map((day, i) => (
-
- ))}
-
-
- )}
-
-
- )}
-
-
- );
-}
-
-
function AdminMemberEdit() {
const navigate = useNavigate();
const { name } = useParams();
diff --git a/frontend/src/pages/pc/admin/AdminScheduleForm.jsx b/frontend/src/pages/pc/admin/AdminScheduleForm.jsx
index b541fb0..d3131a0 100644
--- a/frontend/src/pages/pc/admin/AdminScheduleForm.jsx
+++ b/frontend/src/pages/pc/admin/AdminScheduleForm.jsx
@@ -26,359 +26,11 @@ import {
} from "lucide-react";
import Toast from "../../../components/Toast";
import Lightbox from "../../../components/common/Lightbox";
+import CustomDatePicker from "../../../components/admin/CustomDatePicker";
import * as authApi from "../../../api/admin/auth";
import * as categoriesApi from "../../../api/admin/categories";
import * as schedulesApi from "../../../api/admin/schedules";
import { getMembers } from "../../../api/public/members";
-// 커스텀 데이트픽커 컴포넌트 (AdminMemberEdit.jsx에서 가져옴)
-function CustomDatePicker({ value, onChange, placeholder = "날짜 선택" }) {
- const [isOpen, setIsOpen] = useState(false);
- const [viewMode, setViewMode] = useState("days");
- const [viewDate, setViewDate] = useState(() => {
- if (value) return new Date(value);
- return new Date();
- });
- const ref = useRef(null);
-
- useEffect(() => {
- const handleClickOutside = (e) => {
- if (ref.current && !ref.current.contains(e.target)) {
- setIsOpen(false);
- setViewMode("days");
- }
- };
- document.addEventListener("mousedown", handleClickOutside);
- return () => document.removeEventListener("mousedown", handleClickOutside);
- }, []);
-
- const year = viewDate.getFullYear();
- const month = viewDate.getMonth();
-
- const firstDay = new Date(year, month, 1).getDay();
- const daysInMonth = new Date(year, month + 1, 0).getDate();
-
- const days = [];
- for (let i = 0; i < firstDay; i++) {
- days.push(null);
- }
- for (let i = 1; i <= daysInMonth; i++) {
- days.push(i);
- }
-
- const startYear = Math.floor(year / 10) * 10 - 1;
- const years = Array.from({ length: 12 }, (_, i) => startYear + i);
-
- const prevMonth = () => 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 nextYearRange = () => setViewDate(new Date(year + 10, month, 1));
-
- const selectDate = (day) => {
- const dateStr = `${year}-${String(month + 1).padStart(2, "0")}-${String(
- day
- ).padStart(2, "0")}`;
- onChange(dateStr);
- setIsOpen(false);
- setViewMode("days");
- };
-
- const selectYear = (y) => {
- setViewDate(new Date(y, month, 1));
- setViewMode("months");
- };
-
- const selectMonth = (m) => {
- setViewDate(new Date(year, m, 1));
- setViewMode("days");
- };
-
- const formatDisplayDate = (dateStr) => {
- if (!dateStr) return "";
- const [y, m, d] = dateStr.split("-");
- const days = ['일', '월', '화', '수', '목', '금', '토'];
- const date = new Date(parseInt(y), parseInt(m) - 1, parseInt(d));
- const dayOfWeek = days[date.getDay()];
- return `${y}년 ${parseInt(m)}월 ${parseInt(d)}일 (${dayOfWeek})`;
- };
-
- const isSelected = (day) => {
- if (!value || !day) return false;
- const [y, m, d] = value.split("-");
- return (
- parseInt(y) === year && parseInt(m) === month + 1 && parseInt(d) === day
- );
- };
-
- const isToday = (day) => {
- if (!day) return false;
- const today = new Date();
- return (
- today.getFullYear() === year &&
- today.getMonth() === month &&
- today.getDate() === day
- );
- };
-
- const isCurrentYear = (y) => new Date().getFullYear() === y;
- const isCurrentMonth = (m) => {
- const today = new Date();
- return today.getFullYear() === year && today.getMonth() === m;
- };
-
- const months = [
- "1월",
- "2월",
- "3월",
- "4월",
- "5월",
- "6월",
- "7월",
- "8월",
- "9월",
- "10월",
- "11월",
- "12월",
- ];
-
- return (
-
-
-
-
- {isOpen && (
-
-
-
-
-
-
-
-
- {viewMode === "years" && (
-
-
- 년도
-
-
- {years.map((y) => (
-
- ))}
-
-
- 월
-
-
- {months.map((m, i) => (
-
- ))}
-
-
- )}
-
- {viewMode === "months" && (
-
-
- 월 선택
-
-
- {months.map((m, i) => (
-
- ))}
-
-
- )}
-
- {viewMode === "days" && (
-
-
- {["일", "월", "화", "수", "목", "금", "토"].map((d, i) => (
-
- {d}
-
- ))}
-
-
- {days.map((day, i) => {
- const dayOfWeek = i % 7;
- return (
-
- );
- })}
-
-
- )}
-
-
- )}
-
-
- );
-}
// 숫자 피커 컬럼 컴포넌트 (Vue 컴포넌트를 React로 변환)
function NumberPicker({ items, value, onChange }) {