(null)
diff --git a/apps/dashboard/modules/expenses/expense-form.tsx b/apps/dashboard/modules/expenses/expense-form.tsx
index a293406..f0e7cd3 100644
--- a/apps/dashboard/modules/expenses/expense-form.tsx
+++ b/apps/dashboard/modules/expenses/expense-form.tsx
@@ -19,6 +19,7 @@ import { useAuthApi } from "@/shared/useApi"
import { useResourceForm } from "@/shared/hooks/use-resource-form"
import { useFormMutation } from "@/shared/hooks/use-form-mutation"
import { getTodayDate, toRelation, toId } from "@/shared/lib/utils"
+import { formatTaxLabel } from "@/shared/utils/formatters"
import {
expenseFormSchema,
@@ -89,7 +90,7 @@ function mapToFormValues(data: unknown): ExpenseFormValues {
category: toRelation(d.category_id, d.category?.name ?? d.category?.title ?? d.category_name),
vendor: toRelation(d.vendor_id, d.vendor?.company_name ?? d.vendor?.name ?? d.vendor_name),
department: toRelation(d.department_id, d.department?.name ?? d.department_name),
- tax: toRelation(d.tax_id, d.tax?.title ? `${d.tax.title} (${d.tax.rate}%)` : undefined),
+ tax: toRelation(d.tax_id, formatTaxLabel(d.tax, d.tax_title ?? "") || undefined),
title: d.title || "",
invoice_number: d.invoice_number || "",
expense_date: d.expense_date ? d.expense_date.split("T")[0] : "",
diff --git a/apps/dashboard/modules/expenses/expense-general-info.tsx b/apps/dashboard/modules/expenses/expense-general-info.tsx
index 0ca08c9..00aaebe 100644
--- a/apps/dashboard/modules/expenses/expense-general-info.tsx
+++ b/apps/dashboard/modules/expenses/expense-general-info.tsx
@@ -20,8 +20,7 @@ import {
} from "@/shared/components/ui/card"
import { Badge } from "@/shared/components/ui/badge"
import { cn } from "@/shared/lib/utils"
-import { formatDate, formatCurrency, formatEnum } from "@/shared/utils/formatters"
-import { getFullName } from "@/shared/utils/getFullName"
+import { formatDate, formatCurrency, formatEnum, formatTaxLabel } from "@/shared/utils/formatters"
import { useExpense } from "./expense-context"
function InfoItem({
@@ -68,9 +67,7 @@ export function ExpenseGeneralInfo() {
const labels = (expense as any)?.labels || []
const balanceDue = expense.balance_due ?? null
const paymentsM = expense.payments_made ?? null
- const taxLabel = expense.tax?.title
- ? `${expense.tax.title} (${expense.tax.rate}%)`
- : "Tax"
+ const taxLabel = formatTaxLabel(expense.tax, "Tax")
return (
@@ -122,7 +119,7 @@ export function ExpenseGeneralInfo() {
{/* ── Vendor ── */}
Vendor
- {vendor.company_name || getFullName(vendor as any) || "—"}
+ {vendor.name || "—"}
{/* ── Expense Details ── */}
diff --git a/apps/dashboard/modules/invoices/invoice-form.tsx b/apps/dashboard/modules/invoices/invoice-form.tsx
index 34c6c76..f62ca65 100644
--- a/apps/dashboard/modules/invoices/invoice-form.tsx
+++ b/apps/dashboard/modules/invoices/invoice-form.tsx
@@ -21,6 +21,7 @@ import { useAuthApi } from "@/shared/useApi"
import { useResourceForm } from "@/shared/hooks/use-resource-form"
import { useFormMutation } from "@/shared/hooks/use-form-mutation"
import { toRelation, toId } from "@/shared/lib/utils"
+import { formatTaxLabel } from "@/shared/utils/formatters"
import { useFormContext } from "react-hook-form"
import {
@@ -120,7 +121,7 @@ function mapToFormValues(data: unknown): InvoiceFormValues {
has_insurance: d.has_insurance ?? false,
discount: d.discount || "no",
discount_amount: d.discount_amount != null ? Number(d.discount_amount) : undefined,
- tax: toRelation(d.tax_id, d.tax_title ?? d.tax?.title),
+ tax: toRelation(d.tax_id, d.tax_title ?? (formatTaxLabel(d.tax, "") || undefined)),
deposit_to: d.deposit_to || "",
notes: d.notes || "",
terms_and_conditions: d.terms_and_conditions || "",
diff --git a/apps/dashboard/modules/invoices/invoice-sequence-form.tsx b/apps/dashboard/modules/invoices/invoice-sequence-form.tsx
index 4793723..0de503e 100644
--- a/apps/dashboard/modules/invoices/invoice-sequence-form.tsx
+++ b/apps/dashboard/modules/invoices/invoice-sequence-form.tsx
@@ -36,7 +36,7 @@ export function InvoiceSequenceForm({ resourceId, initialData, onSuccess }: Invo
const isEditing = !!resourceId
const form = useForm({
- resolver: zodResolver(invoiceSequenceSchema),
+ resolver: zodResolver(invoiceSequenceSchema) as any,
defaultValues: {
title: "",
sequence_title: "",
diff --git a/apps/dashboard/modules/job-cards/job-card-expense-item-form.tsx b/apps/dashboard/modules/job-cards/job-card-expense-item-form.tsx
index 4d0f2d5..a1dd230 100644
--- a/apps/dashboard/modules/job-cards/job-card-expense-item-form.tsx
+++ b/apps/dashboard/modules/job-cards/job-card-expense-item-form.tsx
@@ -16,6 +16,7 @@ import {
import { DepartmentInlineForm } from "@/modules/services/inline-forms/department-inline-form"
import { toast } from "sonner"
import { useAuthApi } from "@/shared/useApi"
+import { formatTaxLabel } from "@/shared/utils/formatters"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { DEPARTMENT_ROUTES, TAX_ROUTES } from "@garage/api"
@@ -71,7 +72,7 @@ function mapToFormValues(data: unknown): JobCardExpenseItemFormValues {
? { value: String(d.department.id), label: d.department.name ?? String(d.department.id) }
: null,
tax: d.tax_id != null
- ? { value: String(d.tax_id), label: d.tax ? `${d.tax.title} (${d.tax.rate}%)` : String(d.tax_id) }
+ ? { value: String(d.tax_id), label: formatTaxLabel(d.tax, String(d.tax_id)) }
: null,
quantity: d.quantity ?? 1,
rate: d.rate != null ? Number(d.rate) : 0,
diff --git a/apps/dashboard/modules/job-cards/job-card-form.tsx b/apps/dashboard/modules/job-cards/job-card-form.tsx
index e10ea10..656626c 100644
--- a/apps/dashboard/modules/job-cards/job-card-form.tsx
+++ b/apps/dashboard/modules/job-cards/job-card-form.tsx
@@ -20,6 +20,7 @@ import { useAuthApi } from "@/shared/useApi"
import { useResourceForm } from "@/shared/hooks/use-resource-form"
import { useFormMutation } from "@/shared/hooks/use-form-mutation"
import { toRelation, toId } from "@/shared/lib/utils"
+import { formatTaxLabel } from "@/shared/utils/formatters"
import {
jobCardFormSchema,
@@ -106,7 +107,7 @@ function mapToFormValues(data: unknown): JobCardFormValues {
sales_person: toRelation(d.sales_person_id, d.sales_person ? `${d.sales_person.first_name} ${d.sales_person.last_name}` : undefined),
insurance_type: toRelation(d.insurance_type_id, d.insurance_type?.name),
insurer: toRelation(d.insurer_id, d.insurer ? `${d.insurer.first_name} ${d.insurer.last_name}`.trim() || d.insurer.company_name : undefined),
- tax: toRelation(d.tax_id, d.tax ? `${d.tax.title} (${d.tax.rate}%)` : undefined),
+ tax: toRelation(d.tax_id, formatTaxLabel(d.tax, "") || undefined),
order_number: d.order_number || "",
estimate_number: d.estimate_number || "",
status: d.status || "draft",
diff --git a/apps/dashboard/modules/job-cards/job-card-part-form.tsx b/apps/dashboard/modules/job-cards/job-card-part-form.tsx
index d2a267b..35206f4 100644
--- a/apps/dashboard/modules/job-cards/job-card-part-form.tsx
+++ b/apps/dashboard/modules/job-cards/job-card-part-form.tsx
@@ -16,6 +16,7 @@ import {
import { DepartmentInlineForm } from "@/modules/services/inline-forms/department-inline-form"
import { toast } from "sonner"
import { useAuthApi } from "@/shared/useApi"
+import { formatTaxLabel } from "@/shared/utils/formatters"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { DEPARTMENT_ROUTES, TAX_ROUTES } from "@garage/api"
@@ -71,7 +72,7 @@ function mapToFormValues(data: unknown): JobCardPartFormValues {
? { value: String(d.department.id), label: d.department.name ?? String(d.department.id) }
: null,
tax: d.tax_id != null
- ? { value: String(d.tax_id), label: d.tax ? `${d.tax.title} (${d.tax.rate}%)` : String(d.tax_id) }
+ ? { value: String(d.tax_id), label: formatTaxLabel(d.tax, String(d.tax_id)) }
: null,
quantity: d.quantity ?? 1,
rate: d.rate != null ? Number(d.rate) : 0,
diff --git a/apps/dashboard/modules/job-cards/job-card-service-form.tsx b/apps/dashboard/modules/job-cards/job-card-service-form.tsx
index e5db3c5..2faa41c 100644
--- a/apps/dashboard/modules/job-cards/job-card-service-form.tsx
+++ b/apps/dashboard/modules/job-cards/job-card-service-form.tsx
@@ -17,6 +17,7 @@ import {
import { DepartmentInlineForm } from "@/modules/services/inline-forms/department-inline-form"
import { toast } from "sonner"
import { useAuthApi } from "@/shared/useApi"
+import { formatTaxLabel } from "@/shared/utils/formatters"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { RateType, DEPARTMENT_ROUTES, TAX_ROUTES } from "@garage/api"
@@ -80,7 +81,7 @@ function mapToFormValues(data: unknown): JobCardServiceFormValues {
? { value: String(d.department.id), label: d.department.name ?? String(d.department.id) }
: undefined as any,
tax: d.tax_id != null
- ? { value: String(d.tax_id), label: d.tax ? `${d.tax.title} (${d.tax.rate}%)` : String(d.tax_id) }
+ ? { value: String(d.tax_id), label: formatTaxLabel(d.tax, String(d.tax_id)) }
: null,
rate_type: d.rate_type ?? "flat_rate",
labor_rate: d.labor_rate
diff --git a/apps/dashboard/shared/utils/formatters.ts b/apps/dashboard/shared/utils/formatters.ts
index c0f7bb9..aed158c 100644
--- a/apps/dashboard/shared/utils/formatters.ts
+++ b/apps/dashboard/shared/utils/formatters.ts
@@ -56,6 +56,25 @@ export function formatEnum(value?: string | null): string {
.join(" ")
}
+type TaxLabelValue = {
+ name?: string | null
+ title?: string | null
+ rate?: string | number | null
+}
+
+export function formatTaxLabel(
+ value?: TaxLabelValue | null,
+ emptyFallback = "—",
+): string {
+ const label = value?.name ?? value?.title
+ const rate = value?.rate
+
+ if (!label) return emptyFallback
+ if (rate == null || rate === "") return label
+
+ return `${label} (${rate}%)`
+}
+
/**
* Format a number with locale-aware thousand separators.
* e.g. 150000 → "150,000"
diff --git a/apps/dashboard/shared/utils/getFullName.ts b/apps/dashboard/shared/utils/getFullName.ts
index eacf6c0..6eaf643 100644
--- a/apps/dashboard/shared/utils/getFullName.ts
+++ b/apps/dashboard/shared/utils/getFullName.ts
@@ -1,4 +1,9 @@
-export const getFullName = (user?: T) => {
+type NameLike = {
+ first_name?: string | null
+ last_name?: string | null
+}
+
+export const getFullName = (user?: T | null) => {
const firstName = user?.first_name ?? ""
const lastName = user?.last_name ?? ""
return `${firstName} ${lastName}`.trim()