Improve time selection with interactive popover and hour options

Replaces the simple click-to-cycle hour selection with a Popover component in `artifacts/cra-app/src/pages/timesheet-detail.tsx`, allowing users to select from a wider range of predefined hour options.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 55837015-10e9-4be9-b857-7f5e6be73772
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 96602b33-5bd8-4421-9962-7e11ed0421c8
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1cc377db-7ea0-49f2-97ce-c3e87e0228cc/55837015-10e9-4be9-b857-7f5e6be73772/0xBkjKi
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
SylvainP1 2026-04-14 08:37:28 +00:00
parent 147ae0f4c7
commit 7b2caa45fd

View File

@ -1,4 +1,4 @@
import { useState, useMemo, useEffect, useRef, useCallback } from "react"; import { useState, useMemo, useEffect, useRef, useCallback, type ReactNode } from "react";
import { useParams, Link } from "wouter"; import { useParams, Link } from "wouter";
import { import {
useGetTimesheet, useGetTimesheet,
@ -30,6 +30,7 @@ import {
SelectValue SelectValue
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { formatMonthYear, STATUS_LABELS, STATUS_COLORS, cn } from "@/lib/utils"; import { formatMonthYear, STATUS_LABELS, STATUS_COLORS, cn } from "@/lib/utils";
import { getDaysInMonth, isWeekend, format } from "date-fns"; import { getDaysInMonth, isWeekend, format } from "date-fns";
@ -103,18 +104,11 @@ export default function TimesheetDetailPage() {
}; };
}, [timesheet]); }, [timesheet]);
const handleCellClick = (lineId: number, dateStr: string) => { const HOUR_OPTIONS = [0, 0.5, 1, 2, 3, 4, 5, 6, 7, 7.7];
if (!isEditable) return;
const handleSetHours = (lineId: number, dateStr: string, hours: number) => {
const key = `${lineId}-${dateStr}`; const key = `${lineId}-${dateStr}`;
const current = localEntries[key] || 0; setLocalEntries(prev => ({ ...prev, [key]: hours }));
// Cycle: 0 -> 0.5 -> 1 -> 0
let next = 0.5;
if (current === 0.5) next = 1;
if (current === 1) next = 0;
setLocalEntries(prev => ({ ...prev, [key]: next }));
setHasUnsavedChanges(true); setHasUnsavedChanges(true);
}; };
@ -367,10 +361,15 @@ export default function TimesheetDetailPage() {
const key = `${line.id}-${day.dateStr}`; const key = `${line.id}-${day.dateStr}`;
const val = localEntries[key] || 0; const val = localEntries[key] || 0;
const cellContent = (
<div className="flex items-center justify-center h-9 w-full text-xs">
{val === 0 ? "" : val}
</div>
);
return ( return (
<td <td
key={day.dateStr} key={day.dateStr}
onClick={() => handleCellClick(line.id, day.dateStr)}
className={cn( className={cn(
"p-0 text-center border-r transition-colors select-none", "p-0 text-center border-r transition-colors select-none",
day.isWeekendDay ? "bg-muted/30" : "", day.isWeekendDay ? "bg-muted/30" : "",
@ -378,9 +377,33 @@ export default function TimesheetDetailPage() {
val > 0 ? "bg-primary/5 text-primary font-semibold" : "" val > 0 ? "bg-primary/5 text-primary font-semibold" : ""
)} )}
> >
<div className="flex items-center justify-center h-9 w-full text-xs"> {isEditable ? (
{val === 0 ? "" : val} <Popover>
<PopoverTrigger asChild>
{cellContent}
</PopoverTrigger>
<PopoverContent className="w-auto p-1.5" side="bottom" align="center">
<div className="grid grid-cols-5 gap-1">
{HOUR_OPTIONS.map(h => (
<button
key={h}
onClick={() => handleSetHours(line.id, day.dateStr, h)}
className={cn(
"px-2 py-1.5 rounded text-xs font-medium transition-colors",
val === h
? "bg-primary text-primary-foreground"
: h === 0
? "bg-muted/50 hover:bg-destructive/10 hover:text-destructive"
: "bg-muted/50 hover:bg-primary/10"
)}
>
{h === 0 ? "✕" : `${h}h`}
</button>
))}
</div> </div>
</PopoverContent>
</Popover>
) : cellContent}
</td> </td>
); );
})} })}