feat(plan): day panel — list/add/edit up to 3 records per cell
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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<typeof getCell>, 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}
|
||||
/>
|
||||
<PlanCategoriesModal
|
||||
isOpen={catModalOpen}
|
||||
|
||||
Reference in New Issue
Block a user