feat: 일정 주간/월간 뷰 전환 추가

상단 토글(주간/월간)로 보기 전환. 월간은 전통 달력 그리드(주×요일).

- scheduleDate: firstOfMonth/addMonths/monthLabel/monthGridWeeks(+MonthDay) 추가
- schedule.store: viewMode + anchor 기반으로 주간/월간 범위·라벨·네비 일원화
  (goPrev/goNext 가 모드별로 주/월 이동, setViewMode 시 범위 재조회)
- ScheduleMonthGrid: 주 단위 다중일 막대(레인 패킹) + 초과 '+N',
  카테고리 색·유형 필터·오늘/주말/이전·다음달 표기, 막대 클릭 시 상세 패널
- SchedulePage: SegmentedTabs 토글 + 주간/월간 조건부 렌더

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-23 12:45:52 +09:00
parent 3f134ccca3
commit 4ff746638c
4 changed files with 447 additions and 26 deletions
+60
View File
@@ -92,3 +92,63 @@ export function eventDateText(start: string, end: string): string {
export function spanDays(start: string, end: string): number {
return Math.round((dateOf(end).getTime() - dateOf(start).getTime()) / 86400000) + 1
}
// ===== 월간(달력 그리드) =====
// 해당 월의 1일 ISO
export function firstOfMonth(iso: string): string {
const d = dateOf(iso)
return isoOf(new Date(d.getFullYear(), d.getMonth(), 1))
}
// iso + n개월 (말일 넘침 보정)
export function addMonths(iso: string, n: number): string {
const d = dateOf(iso)
const day = d.getDate()
const base = new Date(d.getFullYear(), d.getMonth() + n, 1)
const dim = new Date(base.getFullYear(), base.getMonth() + 1, 0).getDate()
base.setDate(Math.min(day, dim))
return isoOf(base)
}
// 월 라벨 — '2026년 6월'
export function monthLabel(iso: string): string {
const d = dateOf(iso)
return `${d.getFullYear()}${d.getMonth() + 1}`
}
// 달력 그리드의 하루(요일 + 이번 달 여부)
export interface MonthDay extends WeekDay {
inMonth: boolean
}
// 월 달력 그리드 — 1일이 속한 주의 월요일부터 말일이 속한 주의 일요일까지(4~6주)
export function monthGridWeeks(iso: string): MonthDay[][] {
const monthIdx = dateOf(firstOfMonth(iso)).getMonth()
const gridStart = mondayOf(firstOfMonth(iso))
const first = dateOf(firstOfMonth(iso))
const lastDayIso = isoOf(new Date(first.getFullYear(), first.getMonth() + 1, 0))
const gridEnd = addDays(mondayOf(lastDayIso), 6)
const today = todayIso()
const weeks: MonthDay[][] = []
let cur = gridStart
while (cur <= gridEnd) {
const week: MonthDay[] = []
for (let i = 0; i < 7; i++) {
const dayIso = addDays(cur, i)
const dt = dateOf(dayIso)
week.push({
iso: dayIso,
dow: DOW_KO[i] ?? '',
d: dt.getDate(),
weekend: i >= 5,
isToday: dayIso === today,
inMonth: dt.getMonth() === monthIdx,
})
}
weeks.push(week)
cur = addDays(cur, 7)
}
return weeks
}