Added staff to time entry

X-Lovable-Edit-ID: edt-f5f9f6be-cfe8-4998-8782-0c288c5dd1e0
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-27 23:53:43 +00:00
co-authored by renee-png
2 changed files with 53 additions and 2 deletions
+30 -1
View File
@@ -89,6 +89,8 @@ export function QuickAddTime() {
const [feeId, setFeeId] = useState("");
const [profileRate, setProfileRate] = useState<number | null>(null);
const [submitting, setSubmitting] = useState(false);
const [staff, setStaff] = useState<Array<{ id: string; full_name: string | null; email: string | null }>>([]);
const [enteredById, setEnteredById] = useState<string>("");
const [form, setForm] = useState({
clientId: "",
caseId: "",
@@ -107,6 +109,18 @@ export function QuickAddTime() {
})();
}, [open, user?.id]);
useEffect(() => {
if (!open) return;
(async () => {
const { data } = await supabase.from("profiles").select("id, full_name, email").order("full_name");
setStaff((data ?? []) as any);
})();
}, [open]);
useEffect(() => {
if (open && user?.id && !enteredById) setEnteredById(user.id);
}, [open, user?.id, enteredById]);
const filteredCases = useMemo(
() => (form.clientId ? cases.filter((c) => c.client_id === form.clientId) : []),
[cases, form.clientId],
@@ -144,7 +158,7 @@ export function QuickAddTime() {
setSubmitting(true);
const { error } = await supabase.from("time_entries").insert({
case_id: form.caseId,
user_id: user.id,
user_id: enteredById || user.id,
work_date: form.work_date,
hours,
hourly_rate: rate,
@@ -250,6 +264,21 @@ export function QuickAddTime() {
<Input type="number" step="0.01" min="0" value={form.rate} onChange={(e) => setForm({ ...form, rate: e.target.value })} required />
</div>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Entered by</Label>
<SearchableSelect
value={enteredById}
onValueChange={setEnteredById}
placeholder="Select staff…"
searchPlaceholder="Search staff…"
emptyText="No staff found."
options={staff.map((s) => ({
value: s.id,
label: (s.full_name || s.email || "Unknown") + (s.id === user?.id ? " (me)" : ""),
keywords: `${s.full_name ?? ""} ${s.email ?? ""}`,
}))}
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Description</Label>
<Textarea rows={3} value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} required maxLength={1000} />
+23 -1
View File
@@ -50,6 +50,8 @@ export function HeaderTimer() {
const [feeItems, setFeeItems] = useState<FeeItem[]>([]);
const [feeItemId, setFeeItemId] = useState<string>("");
const [showNewFee, setShowNewFee] = useState(false);
const [staff, setStaff] = useState<Array<{ id: string; full_name: string | null; email: string | null }>>([]);
const [enteredById, setEnteredById] = useState<string>("");
// Load clients + cases the user can access (RLS handles filtering)
useEffect(() => {
@@ -75,6 +77,9 @@ export function HeaderTimer() {
setCases(ks ?? []);
setProfileRate((prof as any)?.hourly_rate ?? null);
setFeeItems((fees ?? []) as FeeItem[]);
const { data: st } = await supabase.from("profiles").select("id, full_name, email").order("full_name");
setStaff((st ?? []) as any);
setEnteredById((prev) => prev || user.id);
})();
}, [user?.id]);
@@ -121,7 +126,7 @@ export function HeaderTimer() {
const rate = isFlatFee ? selectedFee!.amount : effectiveRate;
const { error } = await supabase.from("time_entries").insert({
case_id: timer.caseId,
user_id: user.id,
user_id: enteredById || user.id,
work_date: new Date().toISOString().slice(0, 10),
hours,
hourly_rate: rate,
@@ -143,6 +148,7 @@ export function HeaderTimer() {
);
setBillable(true);
setFeeItemId("");
setEnteredById(user.id);
setOpen(false);
};
@@ -289,6 +295,22 @@ export function HeaderTimer() {
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Entered by</Label>
<SearchableSelect
value={enteredById}
onValueChange={setEnteredById}
placeholder="Select staff…"
searchPlaceholder="Search staff…"
emptyText="No staff found."
options={staff.map((s) => ({
value: s.id,
label: (s.full_name || s.email || "Unknown") + (s.id === user?.id ? " (me)" : ""),
keywords: `${s.full_name ?? ""} ${s.email ?? ""}`,
}))}
/>
</div>
<div className="grid grid-cols-2 gap-3 pt-1">
<div>
<div className="text-[10px] uppercase tracking-wider text-muted-foreground">