diff --git a/src/admin/components/PlanCellModal.tsx b/src/admin/components/PlanCellModal.tsx index f524b1e..d791bf8 100644 --- a/src/admin/components/PlanCellModal.tsx +++ b/src/admin/components/PlanCellModal.tsx @@ -64,7 +64,8 @@ export type PlanCellModalMode = note: string; }; } - | { kind: "view"; userId: number; date: string; cell: ResolvedCell }; + | { kind: "view"; userId: number; date: string; cell: ResolvedCell } + | { kind: "day"; userId: number; date: string; cells: ResolvedCell[] }; interface Props { open: boolean; @@ -84,6 +85,10 @@ interface Props { date: string, ) => Promise; onSwitchToEditRange: (entryId: number, userId: number, date: string) => void; + /** Open the create form for a brand-new record on (userId, date). */ + onAddRecord: (userId: number, date: string) => void; + /** Open the right editor for one of the day's existing records. */ + onEditRecord: (cell: ResolvedCell, userId: number, date: string) => void; } const TrashIcon = ( @@ -112,6 +117,7 @@ export default function PlanCellModal(props: Props) { if (mode.kind === "view") return ; if (mode.kind === "day-in-range") return ; + if (mode.kind === "day") return ; return ; } @@ -506,3 +512,105 @@ function ViewModal(props: Props) { ); } + +const EditIcon = ( + + + + +); + +function DayPanel( + props: Props & { mode: Extract }, +) { + const { mode, onClose, onAddRecord, onEditRecord, categories } = props; + const catMap = Object.fromEntries(categories.map((x) => [x.key, x])); + const atCap = mode.cells.length >= 3; + const free = 3 - mode.cells.length; + return ( + + + {mode.cells.map((c, i) => { + const color = + catMap[c.category]?.color || "var(--mui-palette-divider)"; + const projectLabel = cellProjectLabel(c); + return ( + + + + + + {planCategoryLabel(c.category, catMap)} + {projectLabel ? ` · ${projectLabel}` : ""} + + {c.note && ( + + {c.note} + + )} + + + + + ); + })} + + + + + {atCap + ? "Maximum 3 záznamy na den" + : `${free} ${free === 1 ? "volné místo" : "volná místa"}`} + + + + ); +} diff --git a/src/admin/hooks/usePlanWork.ts b/src/admin/hooks/usePlanWork.ts index 65af092..11bfab8 100644 --- a/src/admin/hooks/usePlanWork.ts +++ b/src/admin/hooks/usePlanWork.ts @@ -15,6 +15,7 @@ export type ModalMode = | { kind: "edit-range"; entryId: number; userId: number; date: string } | { kind: "edit-override"; overrideId: number; userId: number; date: string } | { kind: "day-in-range"; userId: number; date: string; entryId: number } + | { kind: "day"; userId: number; date: string } | { kind: "view"; userId: number; date: string }; function isoDate(d: Date): string { diff --git a/src/admin/pages/PlanWork.tsx b/src/admin/pages/PlanWork.tsx index 6db53e2..f22055f 100644 --- a/src/admin/pages/PlanWork.tsx +++ b/src/admin/pages/PlanWork.tsx @@ -8,6 +8,7 @@ import { useAlert } from "../context/AlertContext"; import { usePlanWork, getCell, + getCells, type ModalMode as HookModalMode, } from "../hooks/usePlanWork"; import type { PlanCellModalMode } from "../components/PlanCellModal"; @@ -206,76 +207,68 @@ export default function PlanWork() { ); const openCell = useCallback( - (userId: number, date: string, _cells: ResolvedCell[]) => { - void _cells; - const cell = getCell(grid, userId, date); + (userId: number, date: string, cells: ResolvedCell[]) => { + const primary = cells[0] ?? null; // Past-date guard: the server rejects create/edit on past dates with // a 403 (see assertNotPastDate in plan.service.ts). To keep the user // out of the "click → modal opens → error toast" state, route any // past-day click to view mode (or to no-op if there's no record). if (isPastDate(date, todayIsoLocal())) { - if (!cell) { - // Empty past cell — nothing to show or edit. - return; - } - setModalCell(cell); + if (!primary) return; + setModalCell(primary); setHookModal({ kind: "view", userId, date }); return; } - if (!cell) { - // Empty cell — open the create modal if the user can edit, otherwise - // there's nothing to view. + if (cells.length === 0) { if (canEdit) openCreate(userId, date); return; } + if (!canEdit) { + setModalCell(primary); + setHookModal({ kind: "view", userId, date }); + return; + } + setHookModal({ kind: "day", userId, date }); + }, + [canEdit, openCreate, setHookModal], + ); + + // Open the correct editor for one record from the day panel. This is the + // per-record version of the old openCell branch logic: a multi-day entry + // routes through the day-in-range chooser; a single-day entry → edit-range; + // an override → edit-override. + const editRecord = useCallback( + (cell: ReturnType, userId: number, date: string) => { + if (!cell) return; setModalCell(cell); if (cell.source === "entry" && cell.entryId !== null) { - if (canEdit) { - // A multi-day entry clicked on a single day → "day-in-range" so the - // user can decide between editing the range or creating a one-day - // override. - if ( - cell.rangeFrom && - cell.rangeTo && - cell.rangeFrom !== cell.rangeTo - ) { - setHookModal({ - kind: "day-in-range", - entryId: cell.entryId, - userId, - date, - }); - return; - } + if (cell.rangeFrom && cell.rangeTo && cell.rangeFrom !== cell.rangeTo) { + setHookModal({ + kind: "day-in-range", + entryId: cell.entryId, + userId, + date, + }); + } else { setHookModal({ kind: "edit-range", entryId: cell.entryId, userId, date, }); - return; } - // Read-only view - setHookModal({ kind: "view", userId, date }); return; } if (cell.source === "override" && cell.overrideId !== null) { - if (canEdit) { - setHookModal({ - kind: "edit-override", - overrideId: cell.overrideId, - userId, - date, - }); - } else { - setHookModal({ kind: "view", userId, date }); - } - return; + setHookModal({ + kind: "edit-override", + overrideId: cell.overrideId, + userId, + date, + }); } - // No source — treat as empty - if (canEdit) openCreate(userId, date); }, - [grid, canEdit, openCreate, setHookModal], + [setHookModal], ); const closeModal = useCallback(() => { @@ -545,8 +538,16 @@ export default function PlanWork() { }, } as PlanCellModalMode; } + if (hookModal.kind === "day") { + return { + kind: "day", + userId: hookModal.userId, + date: hookModal.date, + cells: getCells(grid, hookModal.userId, hookModal.date), + }; + } return { kind: "closed" }; - }, [hookModal, modalCell]); + }, [hookModal, modalCell, grid]); if (!canEdit && !hasPermission("attendance.record")) { // Hard block for users who have neither manage nor record permission. @@ -700,6 +701,8 @@ export default function PlanWork() { onDeleteOverride={deleteOverrideFn} onCreateOverrideFromRange={createOverrideFromRange} onSwitchToEditRange={switchToEditRange} + onAddRecord={openCreate} + onEditRecord={editRecord} />