diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index a254c0c..2fddfe5 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -24,12 +24,13 @@ import { RevenueChart } from "~/app/dashboard/_components/revenue-chart"; import { InvoiceStatusChart } from "~/app/dashboard/_components/invoice-status-chart"; import { MonthlyMetricsChart } from "~/app/dashboard/_components/monthly-metrics-chart"; import { AnimatedStatsCard } from "~/app/dashboard/_components/animated-stats-card"; +import type { DashboardStats, RecentInvoice } from "./types"; // Hero section with clean mono design // Enhanced stats cards with better visuals -function DashboardStats({ stats }: { stats: any }) { // TODO: Import RouterOutput type +function DashboardStats({ stats }: { stats: DashboardStats }) { // TODO: Import RouterOutput type const formatTrend = (value: number, isCount = false) => { if (isCount) { return value > 0 ? `+${value}` : value.toString(); @@ -101,7 +102,7 @@ function DashboardStats({ stats }: { stats: any }) { // TODO: Import RouterOutpu } // Charts section -async function ChartsSection({ stats }: { stats: any }) { +async function ChartsSection({ stats }: { stats: DashboardStats }) { // We still fetch all invoices for the status chart for now, or we could aggregate that too. // For now, let's keep status chart as is (fetching all) but use aggregated for revenue. // Actually, let's fetch invoices here for the status chart to keep it working. @@ -309,7 +310,7 @@ async function CurrentWork() { } // Enhanced recent activity -async function RecentActivity({ recentInvoices }: { recentInvoices: any[] }) { +async function RecentActivity({ recentInvoices }: { recentInvoices: RecentInvoice[] }) { // Use passed recentInvoices instead of fetching all const getStatusStyle = (status: string) => { diff --git a/src/app/dashboard/types.ts b/src/app/dashboard/types.ts new file mode 100644 index 0000000..e70aa01 --- /dev/null +++ b/src/app/dashboard/types.ts @@ -0,0 +1,13 @@ +import { type RouterOutputs } from "~/trpc/react"; + +// Dashboard stats type from the dashboard router +export type DashboardStats = RouterOutputs["dashboard"]["getStats"]; + +// Individual invoice type from the invoices router +export type Invoice = RouterOutputs["invoices"]["getAll"][number]; + +// Recent invoice type (includes client relation) +export type RecentInvoice = DashboardStats["recentInvoices"][number]; + +// Revenue chart data point +export type RevenueChartDataPoint = DashboardStats["revenueChartData"][number]; diff --git a/src/components/forms/invoice-form.tsx b/src/components/forms/invoice-form.tsx index ba00bd7..83b6e67 100644 --- a/src/components/forms/invoice-form.tsx +++ b/src/components/forms/invoice-form.tsx @@ -111,7 +111,7 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { clientId: existingInvoice.clientId, issueDate: new Date(existingInvoice.issueDate), dueDate: new Date(existingInvoice.dueDate), - status: existingInvoice.status as any, + status: existingInvoice.status as "draft" | "sent" | "paid", notes: existingInvoice.notes ?? "", taxRate: existingInvoice.taxRate, defaultHourlyRate: null, @@ -119,7 +119,7 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { }); setInitialized(true); } else if ((!invoiceId || invoiceId === "new") && businesses && !initialized) { - const defaultBusiness = businesses.find((b) => b.isDefault) || businesses[0]; + const defaultBusiness = businesses.find((b) => b.isDefault) ?? businesses[0]; if (defaultBusiness) setFormData((prev) => ({ ...prev, businessId: defaultBusiness.id })); setInitialized(true); } @@ -140,14 +140,16 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { })); }; const removeItem = (idx: number) => { if (formData.items.length > 1) setFormData((prev) => ({ ...prev, items: prev.items.filter((_, i) => i !== idx) })); }; - const updateItem = (idx: number, field: string, value: any) => { + const updateItem = (idx: number, field: string, value: string | number | Date) => { setFormData((prev) => ({ ...prev, items: prev.items.map((item, i) => { if (i === idx) { - const u: any = { ...item, [field]: value }; - if (field === "hours" || field === "rate") u.amount = u.hours * u.rate; - return u; + const updated = { ...item, [field]: value }; + if (field === "hours" || field === "rate") { + updated.amount = updated.hours * updated.rate; + } + return updated; } return item; }) @@ -238,7 +240,7 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { } finally { setLoading(false); } }; - const updateField = (field: keyof InvoiceFormData, value: any) => setFormData(p => ({ ...p, [field]: value })); + const updateField = (field: K, value: InvoiceFormData[K]) => setFormData(p => ({ ...p, [field]: value })); const handleDelete = () => setDeleteDialogOpen(true); const confirmDelete = () => { if (invoiceId) deleteInvoice.mutate({ id: invoiceId }); }; @@ -277,7 +279,7 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { // Explicitly prioritize client rate, then business rate, then 0 const clientRate = selectedClient && 'defaultHourlyRate' in selectedClient ? selectedClient.defaultHourlyRate : null; const businessRate = currentBusiness && 'defaultHourlyRate' in currentBusiness ? currentBusiness.defaultHourlyRate : null; - const rateToSet = clientRate ?? businessRate ?? 0; + const rateToSet: number = (clientRate ?? businessRate ?? 0) as number; updateField("defaultHourlyRate", rateToSet); }} @@ -294,8 +296,8 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { Invoice Config
-
updateField("issueDate", d || new Date())} className="w-full" />
-
updateField("dueDate", d || new Date())} className="w-full" />
+
updateField("issueDate", d ?? new Date())} className="w-full" />
+
updateField("dueDate", d ?? new Date())} className="w-full" />
updateField("taxRate", v)} suffix="%" className="w-full" />