You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
3.3 KiB
TypeScript

// frontend/components/calendar/WeekView.tsx — 주 뷰 (실제 날짜 기반 시간 그리드)
"use client";
import type { MouseEvent } from "react";
import { HOURS, dayNum, timeOfPx, topOf } from "@/lib/calendar/time";
import type { CalEvent, Calendar, FocusBlock as FocusBlockT } from "@/lib/calendar/types";
import { EvBlock, type EvCommit } from "./EvBlock";
import { FocusBlock } from "./FocusBlock";
function nowTopPx(): number {
const d = new Date();
return topOf(`${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`);
}
export function WeekView({
events,
focusBlocks,
calMap,
weekDates,
weekdays,
todayDate,
onOpen,
onSlotClick,
onCommit,
}: {
events: CalEvent[];
focusBlocks: FocusBlockT[];
calMap: Record<string, Calendar>;
weekDates: string[]; // ISO 7개(일~토)
weekdays: string[];
todayDate: string;
onOpen: (ev: CalEvent, e: MouseEvent) => void;
onSlotClick: (dateIso: string, time: string) => void;
onCommit?: EvCommit;
}) {
const nowTop = nowTopPx();
// 빈 시간 슬롯 클릭 → 생성. 이벤트/집중 블록은 stopPropagation 으로 제외됨.
const slotClick = (iso: string) => (e: MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect();
onSlotClick(iso, timeOfPx(e.clientY - rect.top));
};
return (
<div className="cpanel">
<div className="wk-head">
<div className="wk-corner" />
{weekDates.map((iso, i) => (
<div
key={iso}
className={
"wk-dh" + (iso === todayDate ? " today" : "") + (i === 0 || i === 6 ? " wknd" : "")
}
>
<div className="wd">{weekdays[i]}</div>
<div className="dn">{dayNum(iso)}</div>
</div>
))}
</div>
<div className="wk-body" role="region" aria-label="주간 시간표" tabIndex={0}>
<div className="wk-times">
{HOURS.map((h) => (
<div key={h} className="wk-thour">
<span>{h}:00</span>
</div>
))}
</div>
{weekDates.map((iso, i) => {
const dayEvents = events.filter((e) => e.date === iso);
const dayAllDay = dayEvents.filter((e) => !e.start);
const dayTimed = dayEvents.filter((e) => e.start);
const dayFocus = focusBlocks.filter((b) => b.date === iso);
return (
<div
key={iso}
data-date={iso}
className={"wk-col" + (i === 0 || i === 6 ? " wknd" : "")}
onClick={slotClick(iso)}
>
{HOURS.map((h) => (
<div key={h} className="wk-hr" />
))}
{iso === todayDate && <div className="wk-now" style={{ top: nowTop + "px" }} />}
{dayFocus.map((b) => (
<FocusBlock key={b.id} b={b} />
))}
{dayAllDay.map((ev, ai) => (
<EvBlock key={ev.id} ev={ev} cal={calMap[ev.cal]} onClick={onOpen} idx={ai} />
))}
{dayTimed.map((ev) => (
<EvBlock
key={ev.id}
ev={ev}
cal={calMap[ev.cal]}
onClick={onOpen}
onCommit={onCommit}
/>
))}
</div>
);
})}
</div>
</div>
);
}