fix(admin-schedule): 공용폼 토글 정렬·멤버 필터·날짜미정 연월 드롭다운
- 날짜미정 토글을 검증된 패턴(relative w-11 + absolute translate-x-5)으로 교체해 상하/좌우 여백 균일화 - 멤버 목록을 현재 활동 멤버만(is_former 제외)으로 필터 - 날짜미정일 때 달력 대신 연/월 드롭다운으로 입력(YYYY-MM-01 저장) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
72c65021fb
commit
71b678a90b
1 changed files with 64 additions and 24 deletions
|
|
@ -46,12 +46,14 @@ function ScheduleForm() {
|
||||||
queryFn: categoriesApi.getCategories,
|
queryFn: categoriesApi.getCategories,
|
||||||
staleTime: 10 * 60 * 1000,
|
staleTime: 10 * 60 * 1000,
|
||||||
});
|
});
|
||||||
const { data: members = [] } = useQuery({
|
const { data: allMembers = [] } = useQuery({
|
||||||
queryKey: ['members'],
|
queryKey: ['members'],
|
||||||
queryFn: getMembers,
|
queryFn: getMembers,
|
||||||
staleTime: 10 * 60 * 1000,
|
staleTime: 10 * 60 * 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 현재 활동 멤버만 (탈퇴 멤버 제외)
|
||||||
|
const members = useMemo(() => allMembers.filter((m) => !m.is_former), [allMembers]);
|
||||||
const categories = useMemo(
|
const categories = useMemo(
|
||||||
() => allCategories.filter((c) => SHARED_CATEGORIES.includes(c.name)),
|
() => allCategories.filter((c) => SHARED_CATEGORIES.includes(c.name)),
|
||||||
[allCategories]
|
[allCategories]
|
||||||
|
|
@ -101,7 +103,23 @@ function ScheduleForm() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const setPrecision = (month) =>
|
const setPrecision = (month) =>
|
||||||
setFormData((p) => ({ ...p, datePrecision: month ? 'month' : 'day', time: month ? '' : p.time }));
|
setFormData((p) => {
|
||||||
|
if (!month) return { ...p, datePrecision: 'day' };
|
||||||
|
// 월 모드: 날짜가 비었으면 이번 달 1일로 기본값
|
||||||
|
const hasMonthDate = /^\d{4}-\d{2}-01$/.test(p.date);
|
||||||
|
const now = new Date();
|
||||||
|
const date = hasMonthDate
|
||||||
|
? p.date
|
||||||
|
: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01`;
|
||||||
|
return { ...p, datePrecision: 'month', time: '', date };
|
||||||
|
});
|
||||||
|
|
||||||
|
// 연/월 드롭다운용
|
||||||
|
const yearNow = new Date().getFullYear();
|
||||||
|
const YEAR_OPTIONS = [yearNow - 1, yearNow, yearNow + 1, yearNow + 2];
|
||||||
|
const [selYear, selMonth] = (formData.date || '').split('-');
|
||||||
|
const setMonthDate = (year, monthNum) =>
|
||||||
|
setFormData((p) => ({ ...p, date: `${year}-${String(monthNum).padStart(2, '0')}-01` }));
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -240,13 +258,13 @@ function ScheduleForm() {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setPrecision(!isMonthPrecision)}
|
onClick={() => setPrecision(!isMonthPrecision)}
|
||||||
className={`relative w-12 h-6 rounded-full transition-colors ${
|
className={`relative flex-shrink-0 w-11 h-6 rounded-full transition-colors ${
|
||||||
isMonthPrecision ? 'bg-primary' : 'bg-gray-300'
|
isMonthPrecision ? 'bg-primary' : 'bg-gray-300'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`absolute top-0.5 w-5 h-5 bg-white rounded-full shadow transition-all ${
|
className={`absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform ${
|
||||||
isMonthPrecision ? 'left-6' : 'left-0.5'
|
isMonthPrecision ? 'translate-x-5' : ''
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -255,20 +273,41 @@ function ScheduleForm() {
|
||||||
|
|
||||||
{/* 날짜 + 시간 */}
|
{/* 날짜 + 시간 */}
|
||||||
<div className="grid grid-cols-2 gap-6">
|
<div className="grid grid-cols-2 gap-6">
|
||||||
|
{isMonthPrecision ? (
|
||||||
|
// 날짜 미정: 연 + 월 드롭다운
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">연 / 월 *</label>
|
||||||
{isMonthPrecision ? '월 *' : '날짜 *'}
|
<div className="flex gap-3">
|
||||||
</label>
|
<select
|
||||||
|
value={selYear || yearNow}
|
||||||
|
onChange={(e) => setMonthDate(e.target.value, parseInt(selMonth, 10))}
|
||||||
|
className="flex-1 px-4 py-3 border border-gray-200 rounded-xl bg-white focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||||
|
>
|
||||||
|
{YEAR_OPTIONS.map((y) => (
|
||||||
|
<option key={y} value={y}>{y}년</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
value={parseInt(selMonth, 10) || ''}
|
||||||
|
onChange={(e) => setMonthDate(selYear || yearNow, e.target.value)}
|
||||||
|
className="flex-1 px-4 py-3 border border-gray-200 rounded-xl bg-white focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
||||||
|
>
|
||||||
|
{Array.from({ length: 12 }, (_, i) => i + 1).map((m) => (
|
||||||
|
<option key={m} value={m}>{m}월</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">날짜 *</label>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={formData.date}
|
value={formData.date}
|
||||||
onChange={(date) => setFormData({ ...formData, date })}
|
onChange={(date) => setFormData({ ...formData, date })}
|
||||||
minYear={2017}
|
minYear={2017}
|
||||||
/>
|
/>
|
||||||
{isMonthPrecision && (
|
|
||||||
<p className="text-xs text-gray-400 mt-1">선택한 날짜의 "월"만 사용됩니다 (일자는 무시)</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{!isMonthPrecision && (
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">시간</label>
|
<label className="block text-sm font-medium text-gray-700 mb-2">시간</label>
|
||||||
<TimePicker
|
<TimePicker
|
||||||
|
|
@ -276,6 +315,7 @@ function ScheduleForm() {
|
||||||
onChange={(time) => setFormData({ ...formData, time })}
|
onChange={(time) => setFormData({ ...formData, time })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue