From 91d410cbce6d08792b76f7eeaacf94c300bbc608 Mon Sep 17 00:00:00 2001 From: Sean O'Connor Date: Sun, 14 Dec 2025 02:16:21 -0500 Subject: [PATCH] refactor: Remove `env.example`, optimize invoice calendar item selection with derived state, and enhance invoice form's default hourly rate initialization and save button loading state. --- env.example | 24 ------------------- .../forms/invoice-calendar-view.tsx | 22 ++++++++--------- src/components/forms/invoice-form.tsx | 7 ++++-- 3 files changed, 16 insertions(+), 37 deletions(-) delete mode 100644 env.example diff --git a/env.example b/env.example deleted file mode 100644 index d932dee..0000000 --- a/env.example +++ /dev/null @@ -1,24 +0,0 @@ -# Base application env (example) -NODE_ENV=production -PORT=3000 -HOSTNAME=0.0.0.0 - -# NextAuth -AUTH_SECRET=__GENERATE__ - -# Database (Postgres) -POSTGRES_USER=beenvoice -POSTGRES_PASSWORD=__GENERATE__ -POSTGRES_DB=beenvoice -DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} -# Disable SSL for Docker local Postgres; set to false or remove for managed Postgres -DB_DISABLE_SSL=true - -# Email (Resend). Replace with real keys in production -RESEND_API_KEY=replace-or-remove -RESEND_DOMAIN= - -# Build tweaks -SKIP_ENV_VALIDATION=1 - - diff --git a/src/components/forms/invoice-calendar-view.tsx b/src/components/forms/invoice-calendar-view.tsx index df2d982..7b07f85 100644 --- a/src/components/forms/invoice-calendar-view.tsx +++ b/src/components/forms/invoice-calendar-view.tsx @@ -52,9 +52,18 @@ export function InvoiceCalendarView({ const [viewDate, setViewDate] = React.useState(new Date()); // Controls the view (month/week) const [view, setView] = React.useState<"month" | "week">("month"); const [dialogOpen, setDialogOpen] = React.useState(false); - const [selectedDateItems, setSelectedDateItems] = React.useState<{ item: InvoiceItem; index: number }[]>([]); + // Derived state for selected date items - solves cursor jumping + const selectedDateItems = React.useMemo(() => { + if (!date) return []; + return items + .map((item, index) => ({ item, index })) + .filter((wrapper) => { + const itemDate = new Date(wrapper.item.date); + return isSameDay(itemDate, date); + }); + }, [items, date]); - // Function to get items for the selected date + // Helper to get items for any date (for calendar view) const getItemsForDate = React.useCallback((targetDate: Date) => { return items .map((item, index) => ({ item, index })) @@ -69,18 +78,9 @@ export function InvoiceCalendarView({ setDate(newDate); // Optionally update viewDate to match selection if desired, but user wants them decoupled during nav // setViewDate(newDate); - const dateItems = getItemsForDate(newDate); - setSelectedDateItems(dateItems); setDialogOpen(true); }; - // refresh selected items when main items change - React.useEffect(() => { - if (date && dialogOpen) { - setSelectedDateItems(getItemsForDate(date)); - } - }, [items, date, dialogOpen, getItemsForDate]); - const handleAddNewItem = () => { if (date) { onAddItem(date); diff --git a/src/components/forms/invoice-form.tsx b/src/components/forms/invoice-form.tsx index 83b6e67..c8339f8 100644 --- a/src/components/forms/invoice-form.tsx +++ b/src/components/forms/invoice-form.tsx @@ -114,7 +114,7 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { status: existingInvoice.status as "draft" | "sent" | "paid", notes: existingInvoice.notes ?? "", taxRate: existingInvoice.taxRate, - defaultHourlyRate: null, + defaultHourlyRate: existingInvoice.client?.defaultHourlyRate ?? null, items: mappedItems.length > 0 ? mappedItems : [{ id: crypto.randomUUID(), date: new Date(), description: "", hours: 1, rate: 0, amount: 0 }], }); setInitialized(true); @@ -251,7 +251,10 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) {
{invoiceId !== "new" && } - +