Fix edit invoice initialization and routing

The form initialization logic for editing invoices was improved to
handle route changes correctly. The edit link path was fixed and cache
invalidation was added to ensure fresh data on navigation.
This commit is contained in:
2025-08-01 03:33:19 -04:00
parent 5e30d338af
commit 9de86df070
2 changed files with 48 additions and 37 deletions

View File

@@ -131,7 +131,7 @@ function InvoiceViewContent({ invoiceId }: { invoiceId: string }) {
>
<PDFDownloadButton invoiceId={invoice.id} variant="outline" />
<Button asChild variant="default">
<Link href={`/dashboard/invoices/${invoice.id}`}>
<Link href={`/dashboard/invoices/${invoice.id}/edit`}>
<Edit className="h-5 w-5" />
<span>Edit</span>
</Link>

View File

@@ -143,19 +143,26 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) {
},
});
// Single initialization effect - only runs once when data is ready
// Reset initialization when invoiceId changes
useEffect(() => {
if (initialized) return;
setInitialized(false);
}, [invoiceId]);
const dataReady =
!loadingClients &&
!loadingBusinesses &&
(!invoiceId || invoiceId === "new" || !loadingInvoice);
if (!dataReady) return;
if (invoiceId && invoiceId !== "new" && existingInvoice) {
// Initialize form data when invoice data is loaded
useEffect(() => {
if (invoiceId && invoiceId !== "new" && existingInvoice && !initialized) {
// Initialize with existing invoice data
const formDataToSet = {
const mappedItems =
existingInvoice.items?.map((item) => ({
id: crypto.randomUUID(),
date: new Date(item.date),
description: item.description,
hours: item.hours,
rate: item.rate,
amount: item.amount,
})) || [];
setFormData({
invoiceNumber: existingInvoice.invoiceNumber,
businessId: existingInvoice.businessId ?? "",
clientId: existingInvoice.clientId,
@@ -166,43 +173,43 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) {
taxRate: existingInvoice.taxRate,
defaultHourlyRate: null,
items:
existingInvoice.items?.map((item) => ({
id: crypto.randomUUID(),
date: new Date(item.date),
description: item.description,
hours: item.hours,
rate: item.rate,
amount: item.amount,
})) || [],
};
setFormData(formDataToSet);
} else if ((!invoiceId || invoiceId === "new") && businesses) {
mappedItems.length > 0
? mappedItems
: [
{
id: crypto.randomUUID(),
date: new Date(),
description: "",
hours: 1,
rate: 0,
amount: 0,
},
],
});
firstItemEditedRef.current = false;
setInitialized(true);
} else if (
(!invoiceId || invoiceId === "new") &&
businesses &&
!initialized
) {
// New invoice - set default business
const defaultBusiness = businesses.find((b) => b.isDefault);
if (defaultBusiness) {
setFormData((prev) => ({ ...prev, businessId: defaultBusiness.id }));
} else if (businesses.length > 0) {
// If no default business, use the first one
setFormData((prev) => ({ ...prev, businessId: businesses[0]!.id }));
}
setInitialized(true);
}
// Reset the first item edited flag when initializing
firstItemEditedRef.current = false;
setInitialized(true);
}, [
loadingClients,
loadingBusinesses,
loadingInvoice,
existingInvoice,
businesses,
invoiceId,
initialized,
]);
}, [invoiceId, existingInvoice, businesses, initialized]);
// Update the first line item when defaultHourlyRate changes (if it hasn't been manually edited)
// Only for new invoices, not existing ones being edited
useEffect(() => {
if (
(!invoiceId || invoiceId === "new") &&
!firstItemEditedRef.current &&
formData.items.length === 1 &&
formData.items[0]?.description === "" &&
@@ -220,7 +227,7 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) {
],
}));
}
}, [formData.defaultHourlyRate, formData.items]);
}, [formData.defaultHourlyRate]);
// Update default hourly rate when client changes
useEffect(() => {
@@ -353,6 +360,10 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) {
onSuccess: async () => {
toast.success("Invoice updated successfully");
await utils.invoices.getAll.invalidate();
// Invalidate the specific invoice cache to ensure fresh data on navigation
if (invoiceId && invoiceId !== "new") {
await utils.invoices.getById.invalidate({ id: invoiceId });
}
// The update mutation returns { success: true }, so we use the current invoiceId
if (invoiceId && invoiceId !== "new") {
router.push(`/dashboard/invoices/${invoiceId}`);