From 3faf312c471df9a949af46d2ce560fe5ac520b54 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 17:04:32 +0000
Subject: [PATCH 01/13] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/components/ui/searchable-select.tsx | 127 ++++++++++++++++++++++++
1 file changed, 127 insertions(+)
create mode 100644 src/components/ui/searchable-select.tsx
diff --git a/src/components/ui/searchable-select.tsx b/src/components/ui/searchable-select.tsx
new file mode 100644
index 0000000..de7a756
--- /dev/null
+++ b/src/components/ui/searchable-select.tsx
@@ -0,0 +1,127 @@
+import * as React from "react";
+import { Check, ChevronsUpDown } from "lucide-react";
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+
+export interface SearchableSelectOption {
+ value: string;
+ /** Visible label in the selected trigger */
+ label: string;
+ /** Custom render for the option row (defaults to label) */
+ render?: React.ReactNode;
+ /** Extra text used for fuzzy matching (case number, email, etc.) */
+ keywords?: string;
+ disabled?: boolean;
+}
+
+interface SearchableSelectProps {
+ value?: string | null;
+ onValueChange: (value: string) => void;
+ options: SearchableSelectOption[];
+ placeholder?: string;
+ searchPlaceholder?: string;
+ emptyText?: string;
+ disabled?: boolean;
+ className?: string;
+ triggerClassName?: string;
+ /** Width of popover content. Default: trigger width via "w-[--radix-popover-trigger-width]" */
+ contentClassName?: string;
+ /** Optional element rendered above the list (e.g. "Add new") */
+ footer?: React.ReactNode;
+}
+
+export function SearchableSelect({
+ value,
+ onValueChange,
+ options,
+ placeholder = "Select…",
+ searchPlaceholder = "Search…",
+ emptyText = "No results.",
+ disabled,
+ className,
+ triggerClassName,
+ contentClassName,
+ footer,
+}: SearchableSelectProps) {
+ const [open, setOpen] = React.useState(false);
+ const selected = options.find((o) => o.value === value);
+
+ return (
+
-
+ placeholder="Select a client"
+ searchPlaceholder="Search clients…"
+ emptyText="No clients found."
+ options={clients.map((c) => ({ value: c.id, label: c.name, keywords: c.name }))}
+ />
{showHomeowner && (
-
+ placeholder={clientId ? "Select homeowner" : "Pick client first"}
+ searchPlaceholder="Search homeowners…"
+ emptyText="No homeowners found."
+ options={homeowners.map((h) => {
+ const label = `${h.last_name}, ${h.first_name}${h.unit_number ? ` — Unit ${h.unit_number}` : ""}`;
+ return { value: h.id, label, keywords: `${h.first_name} ${h.last_name} ${h.unit_number ?? ""}` };
+ })}
+ />
)}
From f5404ea7e6c1c0795c9ed1117c1fbd50c7e90f66 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 17:05:42 +0000
Subject: [PATCH 03/13] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/components/timer/header-timer.tsx | 90 ++++++++++++---------------
1 file changed, 39 insertions(+), 51 deletions(-)
diff --git a/src/components/timer/header-timer.tsx b/src/components/timer/header-timer.tsx
index 98031ba..efbe94c 100644
--- a/src/components/timer/header-timer.tsx
+++ b/src/components/timer/header-timer.tsx
@@ -12,13 +12,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
+import { SearchableSelect } from "@/components/ui/searchable-select";
import { Pause, Play, Square, Timer as TimerIcon, Loader2, X, PlusCircle } from "lucide-react";
import { toast } from "sonner";
import { NewFeeItemDialog } from "@/components/fees/new-fee-item-dialog";
@@ -209,44 +203,37 @@ export function HeaderTimer() {
-
+ placeholder="Select client"
+ searchPlaceholder="Search clients…"
+ emptyText="No clients found."
+ options={clients.map((c) => ({ value: c.id, label: c.name, keywords: c.name }))}
+ />
-
+ placeholder={timer.clientId ? "Select case" : "Pick a client first"}
+ searchPlaceholder="Search cases…"
+ emptyText="No cases for this client."
+ options={filteredCases.map((c) => ({
+ value: c.id,
+ label: `${c.case_number} — ${c.title}`,
+ keywords: `${c.case_number} ${c.title}`,
+ render: (
+
+ {c.case_number}
+ {c.title}
+
+ ),
+ }))}
+ />
@@ -263,26 +250,27 @@ export function HeaderTimer() {
{feeItems.length > 0 ? (
-
+
+ ),
+ }))}
+ />
) : (
No saved items yet. Click "Add new item" to save one for reuse.
From 5454180a2017a5164253cacd81a4ca3189f08312 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 17:05:57 +0000
Subject: [PATCH 04/13] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/components/quick-add/quick-add.tsx | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/src/components/quick-add/quick-add.tsx b/src/components/quick-add/quick-add.tsx
index d65b38e..ab84d80 100644
--- a/src/components/quick-add/quick-add.tsx
+++ b/src/components/quick-add/quick-add.tsx
@@ -15,13 +15,7 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
+import { SearchableSelect } from "@/components/ui/searchable-select";
import { Clock, Loader2, Plus, Receipt as ReceiptIcon } from "lucide-react";
import { toast } from "sonner";
import { roundToTenth } from "@/lib/timer";
From 23b6bf36490fec08e9e27661baa0092c49ec197b Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 17:06:20 +0000
Subject: [PATCH 05/13] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/components/quick-add/quick-add.tsx | 63 ++++++++++++--------------
1 file changed, 30 insertions(+), 33 deletions(-)
diff --git a/src/components/quick-add/quick-add.tsx b/src/components/quick-add/quick-add.tsx
index ab84d80..023be27 100644
--- a/src/components/quick-add/quick-add.tsx
+++ b/src/components/quick-add/quick-add.tsx
@@ -175,40 +175,41 @@ export function QuickAddTime() {
-
+ placeholder="Select"
+ searchPlaceholder="Search clients…"
+ emptyText="No clients found."
+ options={clients.map((c) => ({ value: c.id, label: c.name, keywords: c.name }))}
+ />
-
+
+ ),
+ }))}
+ />
{fees.length > 0 && (
-
+ placeholder="Pick from fee schedule…"
+ searchPlaceholder="Search fee items…"
+ emptyText="No fee items."
+ options={fees.map((f) => ({
+ value: f.id,
+ label: `${f.name}${Number(f.amount) > 0 ? ` — $${Number(f.amount).toFixed(2)}/hr` : " — uses your default rate"}`,
+ keywords: `${f.name} ${f.description ?? ""}`,
+ }))}
+ />
)}
From 3821928699578cd951e9d79d34ddfd849e772515 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 17:06:47 +0000
Subject: [PATCH 06/13] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/components/quick-add/quick-add.tsx | 62 +++++++++++++-------------
1 file changed, 31 insertions(+), 31 deletions(-)
diff --git a/src/components/quick-add/quick-add.tsx b/src/components/quick-add/quick-add.tsx
index 023be27..5e88b6c 100644
--- a/src/components/quick-add/quick-add.tsx
+++ b/src/components/quick-add/quick-add.tsx
@@ -231,7 +231,7 @@ export function QuickAddTime() {
options={fees.map((f) => ({
value: f.id,
label: `${f.name}${Number(f.amount) > 0 ? ` — $${Number(f.amount).toFixed(2)}/hr` : " — uses your default rate"}`,
- keywords: `${f.name} ${f.description ?? ""}`,
+ keywords: f.name,
}))}
/>
@@ -357,40 +357,41 @@ export function QuickAddExpense() {
-
+ placeholder="Select"
+ searchPlaceholder="Search clients…"
+ emptyText="No clients found."
+ options={clients.map((c) => ({ value: c.id, label: c.name, keywords: c.name }))}
+ />
-
+
+ ),
+ }))}
+ />
{fees.length > 0 && (
-
+ placeholder="Pick from fee schedule…"
+ searchPlaceholder="Search fee items…"
+ emptyText="No fee items."
+ options={fees.map((f) => ({
+ value: f.id,
+ label: `${f.name} — $${Number(f.amount).toFixed(2)}`,
+ keywords: f.name,
+ }))}
+ />
)}
From 838eb5ae1984a9dbc1c6a4d7cbd8a34b41245df5 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 17:07:09 +0000
Subject: [PATCH 07/13] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/components/cases/expenses-tab.tsx | 23 ++++++++++------------
src/components/cases/time-tab.tsx | 28 ++++++++++-----------------
2 files changed, 20 insertions(+), 31 deletions(-)
diff --git a/src/components/cases/expenses-tab.tsx b/src/components/cases/expenses-tab.tsx
index 193b6b4..f56b681 100644
--- a/src/components/cases/expenses-tab.tsx
+++ b/src/components/cases/expenses-tab.tsx
@@ -173,7 +173,7 @@ export function CaseExpensesTab({ caseId }: { caseId: string }) {
{fees.length > 0 && (
-
+ placeholder="Pick from the fee schedule…"
+ searchPlaceholder="Search fee items…"
+ emptyText="No fee items found."
+ options={fees.map((f) => ({
+ value: f.id,
+ label: `${f.name} — ${formatCurrency(Number(f.amount))}`,
+ keywords: `${f.name} ${f.description ?? ""}`,
+ }))}
+ />
)}
{fees.length > 0 && (
-
+ placeholder="Pick a rate from the fee schedule…"
+ searchPlaceholder="Search fee items…"
+ emptyText="No fee items found."
+ options={fees.map((f) => ({
+ value: f.id,
+ label: `${f.name}${Number(f.amount) > 0 ? ` — ${formatCurrency(Number(f.amount))}/hr` : " — uses your default rate"}`,
+ keywords: `${f.name} ${f.description ?? ""}`,
+ }))}
+ />
)}