Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
co-authored by
renee-png
parent
d7c6f8eeee
commit
1bdd0afec9
@@ -9,7 +9,7 @@ import { supabase } from "@/integrations/supabase/client";
|
||||
import { useAuth } from "@/lib/auth";
|
||||
import { Plus, Trash2, Loader2, FilePlus } from "lucide-react";
|
||||
import { formatCurrency, formatDate } from "@/lib/format";
|
||||
import { roundToTenth } from "@/lib/timer";
|
||||
import { roundToSixth } from "@/lib/timer";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
Select,
|
||||
@@ -41,11 +41,16 @@ export function CaseTimeTab({ caseRecord, onInvoice }: CaseTimeTabProps) {
|
||||
const [form, setForm] = useState({
|
||||
work_date: new Date().toISOString().slice(0, 10),
|
||||
hours: "",
|
||||
minutes: "",
|
||||
hourly_rate: caseRecord.default_hourly_rate?.toString() ?? "",
|
||||
description: "",
|
||||
billable: true,
|
||||
});
|
||||
|
||||
// Compute total raw hours from hours + minutes inputs, then round UP to 1/6.
|
||||
const rawHours = (parseFloat(form.hours || "0") || 0) + (parseFloat(form.minutes || "0") || 0) / 60;
|
||||
const roundedHours = roundToSixth(rawHours);
|
||||
|
||||
const [fees, setFees] = useState<FeeItem[]>([]);
|
||||
const [selectedFeeId, setSelectedFeeId] = useState<string>("");
|
||||
|
||||
@@ -96,10 +101,9 @@ export function CaseTimeTab({ caseRecord, onInvoice }: CaseTimeTabProps) {
|
||||
|
||||
const submit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const rawHours = parseFloat(form.hours);
|
||||
const hours = roundToTenth(rawHours);
|
||||
const hours = roundedHours;
|
||||
const rate = parseFloat(form.hourly_rate || "0");
|
||||
if (!hours || hours <= 0) { toast.error("Hours must be greater than 0"); return; }
|
||||
if (!hours || hours <= 0) { toast.error("Time must be greater than 0"); return; }
|
||||
if (!form.description.trim()) { toast.error("Description required"); return; }
|
||||
if (!user?.id) { toast.error("Not signed in"); return; }
|
||||
setSubmitting(true);
|
||||
@@ -115,9 +119,9 @@ export function CaseTimeTab({ caseRecord, onInvoice }: CaseTimeTabProps) {
|
||||
setSubmitting(false);
|
||||
if (error) toast.error(error.message);
|
||||
else {
|
||||
toast.success("Time entry added");
|
||||
toast.success(`Time entry added (${hours.toFixed(2)} hrs)`);
|
||||
setShowForm(false);
|
||||
setForm({ ...form, hours: "", description: "" });
|
||||
setForm({ ...form, hours: "", minutes: "", description: "" });
|
||||
load();
|
||||
}
|
||||
};
|
||||
@@ -191,14 +195,23 @@ export function CaseTimeTab({ caseRecord, onInvoice }: CaseTimeTabProps) {
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={submit} className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<form onSubmit={submit} className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Date</Label>
|
||||
<Input type="date" value={form.work_date} onChange={(e) => setForm({ ...form, work_date: e.target.value })} required />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Hours</Label>
|
||||
<Input type="number" step="0.1" min="0.1" value={form.hours} onChange={(e) => setForm({ ...form, hours: e.target.value })} required />
|
||||
<Input type="number" step="1" min="0" placeholder="0" value={form.hours} onChange={(e) => setForm({ ...form, hours: e.target.value })} />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Minutes</Label>
|
||||
<Input type="number" step="1" min="0" placeholder="0" value={form.minutes} onChange={(e) => setForm({ ...form, minutes: e.target.value })} />
|
||||
{rawHours > 0 && (
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
Billed as {roundedHours.toFixed(2)} hrs (1/6 hr)
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Hourly rate ($)</Label>
|
||||
@@ -210,11 +223,11 @@ export function CaseTimeTab({ caseRecord, onInvoice }: CaseTimeTabProps) {
|
||||
Billable
|
||||
</label>
|
||||
</div>
|
||||
<div className="md:col-span-4 space-y-1.5">
|
||||
<div className="md:col-span-5 space-y-1.5">
|
||||
<Label className="text-xs">Description</Label>
|
||||
<Textarea rows={2} value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} required maxLength={1000} />
|
||||
</div>
|
||||
<div className="md:col-span-4 flex justify-end">
|
||||
<div className="md:col-span-5 flex justify-end">
|
||||
<Button type="submit" disabled={submitting}>
|
||||
{submitting && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
|
||||
Save entry
|
||||
|
||||
Reference in New Issue
Block a user