Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-19 15:44:38 +00:00
co-authored by renee-png
parent 3e1aaed513
commit 07fe4df9b3
+36 -2
View File
@@ -34,6 +34,13 @@ interface CaseOption {
default_hourly_rate: number | null;
}
interface FeeItem {
id: string;
name: string;
description: string | null;
amount: number;
}
export function HeaderTimer() {
const { user } = useAuth();
const { timer, elapsedMs, isRunning, hasStarted, setClient, setCase, setDescription, start, pause, resume, reset, consume } = useTimer();
@@ -44,22 +51,33 @@ export function HeaderTimer() {
const [profileRate, setProfileRate] = useState<number | null>(null);
const [saving, setSaving] = useState(false);
const [billable, setBillable] = useState(true);
const [feeItems, setFeeItems] = useState<FeeItem[]>([]);
const [feeItemId, setFeeItemId] = useState<string>("");
// Load clients + cases the user can access (RLS handles filtering)
useEffect(() => {
if (!user?.id) return;
(async () => {
const [{ data: cs }, { data: ks }, { data: prof }] = await Promise.all([
const [{ data: cs }, { data: ks }, { data: prof }, { data: fees }] = await Promise.all([
supabase.from("clients").select("id, name").order("name"),
supabase
.from("cases")
.select("id, case_number, title, client_id, default_hourly_rate")
.order("case_number", { ascending: false }),
supabase.from("profiles").select("hourly_rate").eq("id", user.id).maybeSingle(),
supabase
.from("fee_schedule_items")
.select("id, name, description, amount")
.eq("active", true)
.eq("billable", true)
.eq("category", "time")
.order("sort_order")
.order("name"),
]);
setClients(cs ?? []);
setCases(ks ?? []);
setProfileRate((prof as any)?.hourly_rate ?? null);
setFeeItems((fees ?? []) as FeeItem[]);
})();
}, [user?.id]);
@@ -73,7 +91,21 @@ export function HeaderTimer() {
[cases, timer.caseId],
);
const effectiveRate = activeCase?.default_hourly_rate ?? profileRate ?? 0;
const selectedFee = useMemo(
() => feeItems.find((f) => f.id === feeItemId) ?? null,
[feeItems, feeItemId],
);
const effectiveRate =
selectedFee?.amount ?? activeCase?.default_hourly_rate ?? profileRate ?? 0;
const handleSelectFee = (id: string) => {
setFeeItemId(id);
const fee = feeItems.find((f) => f.id === id);
if (fee && !timer.description.trim()) {
setDescription(fee.description?.trim() || fee.name);
}
};
const handleSave = async () => {
if (!user?.id) return toast.error("Not signed in");
@@ -100,6 +132,7 @@ export function HeaderTimer() {
description: `${formatCurrency(hours * effectiveRate)} at ${formatCurrency(effectiveRate)}/hr`,
});
setBillable(true);
setFeeItemId("");
setOpen(false);
};
@@ -107,6 +140,7 @@ export function HeaderTimer() {
if (hasStarted && !confirm("Discard the running timer?")) return;
reset();
setBillable(true);
setFeeItemId("");
};
const dotClass = isRunning