From 225292af7f1a1f447b8bb730a4470b5bae794640 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 00:33:04 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/components/protected-layout.tsx | 8 +- src/components/timer/start-timer-button.tsx | 57 ------- src/components/timer/timer-widget.tsx | 173 -------------------- 3 files changed, 1 insertion(+), 237 deletions(-) delete mode 100644 src/components/timer/start-timer-button.tsx delete mode 100644 src/components/timer/timer-widget.tsx diff --git a/src/components/protected-layout.tsx b/src/components/protected-layout.tsx index 5ac716c..96b2bc6 100644 --- a/src/components/protected-layout.tsx +++ b/src/components/protected-layout.tsx @@ -2,7 +2,6 @@ import { useEffect, type ReactNode } from "react"; import { useNavigate } from "@tanstack/react-router"; import { useAuth } from "@/lib/auth"; import { AppShell } from "@/components/app-shell"; -import { TimerWidget } from "@/components/timer/timer-widget"; import { Loader2 } from "lucide-react"; export function ProtectedLayout({ children, adminOnly }: { children: ReactNode; adminOnly?: boolean }) { @@ -26,10 +25,5 @@ export function ProtectedLayout({ children, adminOnly }: { children: ReactNode; ); } - return ( - - {children} - - - ); + return {children}; } diff --git a/src/components/timer/start-timer-button.tsx b/src/components/timer/start-timer-button.tsx deleted file mode 100644 index da1e588..0000000 --- a/src/components/timer/start-timer-button.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { useState } from "react"; -import { Button, type ButtonProps } from "@/components/ui/button"; -import { useTimer, type TimerTarget } from "@/lib/timer"; -import { Play, Timer as TimerIcon } from "lucide-react"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui/alert-dialog"; -import { formatHMS } from "@/lib/timer"; - -interface Props extends Omit { - target: TimerTarget; - label?: string; -} - -export function StartTimerButton({ target, label = "Start timer", ...rest }: Props) { - const { active, elapsedMs, start } = useTimer(); - const [confirmOpen, setConfirmOpen] = useState(false); - - const handleClick = () => { - if (active) setConfirmOpen(true); - else start(target); - }; - - return ( - <> - - - - - - Replace running timer? - - - A timer is already running ({formatHMS(elapsedMs)}) on{" "} - {active?.caseNumber ?? active?.clientName}. Starting a new - one will discard it without saving. - - - - Keep current - start(target)}>Discard & start new - - - - - ); -} diff --git a/src/components/timer/timer-widget.tsx b/src/components/timer/timer-widget.tsx deleted file mode 100644 index 9ec855c..0000000 --- a/src/components/timer/timer-widget.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { useState } from "react"; -import { useTimer, formatHMS } from "@/lib/timer"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"; -import { Pause, Play, Square, Timer as TimerIcon, Loader2 } from "lucide-react"; -import { useAuth } from "@/lib/auth"; -import { supabase } from "@/integrations/supabase/client"; -import { toast } from "sonner"; -import { Link } from "@tanstack/react-router"; - -export function TimerWidget() { - const { active, elapsedMs, isRunning, pause, resume, setDescription, stopAndConsume, reset } = useTimer(); - const { user, session } = useAuth(); - const [stopOpen, setStopOpen] = useState(false); - const [saving, setSaving] = useState(false); - const [form, setForm] = useState({ hours: "", rate: "", description: "", billable: true, work_date: "" }); - - if (!session || !active) return null; - - const openStop = () => { - if (isRunning) pause(); - const snapshot = { - hours: (elapsedMs / 3600000).toFixed(2), - rate: active.defaultHourlyRate?.toString() ?? "", - description: active.description, - billable: true, - work_date: new Date().toISOString().slice(0, 10), - }; - setForm(snapshot); - setStopOpen(true); - }; - - const cancelStop = () => setStopOpen(false); - - const discard = () => { - if (!confirm("Discard this timer without saving?")) return; - reset(); - setStopOpen(false); - }; - - const saveEntry = async (e: React.FormEvent) => { - e.preventDefault(); - if (!user?.id) return; - if (!active.caseId) { - toast.error("Attach the timer to a case before saving a time entry."); - return; - } - const hours = parseFloat(form.hours); - const rate = parseFloat(form.rate || "0"); - if (!hours || hours <= 0) return toast.error("Hours must be greater than 0"); - if (!form.description.trim()) return toast.error("Description required"); - setSaving(true); - const { error } = await supabase.from("time_entries").insert({ - case_id: active.caseId, - user_id: user.id, - work_date: form.work_date, - hours, - hourly_rate: rate, - description: form.description.trim(), - billable: form.billable, - }); - setSaving(false); - if (error) return toast.error(error.message); - toast.success("Time entry saved"); - stopAndConsume(); - setStopOpen(false); - }; - - const subtitle = active.caseId - ? `${active.caseNumber ?? ""} ยท ${active.caseTitle ?? ""}` - : `${active.clientName} (no case)`; - - return ( - <> -
-
- - Active timer - -
-
-
{formatHMS(elapsedMs)}
-
- {active.caseId ? ( - - {subtitle} - - ) : ( - - {subtitle} - - )} -
- {!active.caseId && ( -
Attach to a case to save as a time entry.
- )} -
-
- {isRunning ? ( - - ) : ( - - )} - -
-
- - - - - Save time entry - {subtitle} - -
-
-
- - setForm({ ...form, work_date: e.target.value })} required /> -
-
- - setForm({ ...form, hours: e.target.value })} required /> -
-
-
- - setForm({ ...form, rate: e.target.value })} required /> -
-
- -