Fix error when deleting time entries from the timesheet

Update the timesheet detail page to correctly handle the deletion of time entries by ensuring that entries with zero hours are included in the save operation when they have been modified.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 55837015-10e9-4be9-b857-7f5e6be73772
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 5d5b4df6-50de-4be5-a63e-d635e929441e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1cc377db-7ea0-49f2-97ce-c3e87e0228cc/55837015-10e9-4be9-b857-7f5e6be73772/9KCnXCa
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
SylvainP1 2026-04-14 08:48:24 +00:00
parent b454523241
commit 0b0385df95
2 changed files with 8 additions and 3 deletions

View File

@ -58,10 +58,10 @@ export default function TimesheetDetailPage() {
const deleteLine = useDeleteTimesheetLine(); const deleteLine = useDeleteTimesheetLine();
const upsertEntries = useUpsertTimeEntries(); const upsertEntries = useUpsertTimeEntries();
// Local state for optimistic updates
const [localEntries, setLocalEntries] = useState<Record<string, number>>({}); const [localEntries, setLocalEntries] = useState<Record<string, number>>({});
const [isAddingLine, setIsAddingLine] = useState(false); const [isAddingLine, setIsAddingLine] = useState(false);
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
const dirtyKeysRef = useRef<Set<string>>(new Set());
const { data: projects } = useListProjects({ query: { queryKey: getListProjectsQueryKey() } }); const { data: projects } = useListProjects({ query: { queryKey: getListProjectsQueryKey() } });
@ -78,6 +78,7 @@ export default function TimesheetDetailPage() {
}); });
setLocalEntries(initial); setLocalEntries(initial);
setHasUnsavedChanges(false); setHasUnsavedChanges(false);
dirtyKeysRef.current.clear();
} }
}, [timesheet]); }, [timesheet]);
@ -114,10 +115,11 @@ export default function TimesheetDetailPage() {
const entries = localEntriesRef.current; const entries = localEntriesRef.current;
const entriesToSave: LocalEntry[] = []; const entriesToSave: LocalEntry[] = [];
const dirty = dirtyKeysRef.current;
Object.entries(entries).forEach(([key, hours]) => { Object.entries(entries).forEach(([key, hours]) => {
const lineIdStr = key.split("-")[0]; const lineIdStr = key.split("-")[0];
const fullDate = key.substring(lineIdStr.length + 1); const fullDate = key.substring(lineIdStr.length + 1);
if (hours > 0) { if (hours > 0 || dirty.has(key)) {
entriesToSave.push({ entriesToSave.push({
timesheetLineId: parseInt(lineIdStr), timesheetLineId: parseInt(lineIdStr),
date: fullDate, date: fullDate,
@ -134,6 +136,7 @@ export default function TimesheetDetailPage() {
onSuccess: () => { onSuccess: () => {
setSaveStatus("saved"); setSaveStatus("saved");
setHasUnsavedChanges(false); setHasUnsavedChanges(false);
dirtyKeysRef.current.clear();
queryClient.invalidateQueries({ queryKey: getGetTimesheetQueryKey(timesheetId) }); queryClient.invalidateQueries({ queryKey: getGetTimesheetQueryKey(timesheetId) });
setTimeout(() => setSaveStatus("idle"), 2000); setTimeout(() => setSaveStatus("idle"), 2000);
}, },
@ -148,6 +151,7 @@ export default function TimesheetDetailPage() {
const key = `${lineId}-${dateStr}`; const key = `${lineId}-${dateStr}`;
setLocalEntries(prev => ({ ...prev, [key]: hours })); setLocalEntries(prev => ({ ...prev, [key]: hours }));
setHasUnsavedChanges(true); setHasUnsavedChanges(true);
dirtyKeysRef.current.add(key);
if (saveTimerRef.current) clearTimeout(saveTimerRef.current); if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
saveTimerRef.current = setTimeout(() => doSave(), 800); saveTimerRef.current = setTimeout(() => doSave(), 800);
@ -181,11 +185,12 @@ export default function TimesheetDetailPage() {
if (hasUnsavedChanges) { if (hasUnsavedChanges) {
const entries = localEntriesRef.current; const entries = localEntriesRef.current;
const dirty = dirtyKeysRef.current;
const entriesToSave: LocalEntry[] = []; const entriesToSave: LocalEntry[] = [];
Object.entries(entries).forEach(([key, hours]) => { Object.entries(entries).forEach(([key, hours]) => {
const lineIdStr = key.split("-")[0]; const lineIdStr = key.split("-")[0];
const fullDate = key.substring(lineIdStr.length + 1); const fullDate = key.substring(lineIdStr.length + 1);
if (hours > 0) { if (hours > 0 || dirty.has(key)) {
entriesToSave.push({ timesheetLineId: parseInt(lineIdStr), date: fullDate, hours }); entriesToSave.push({ timesheetLineId: parseInt(lineIdStr), date: fullDate, hours });
} }
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB