Fix web typecheck errors across core feature modules

This commit is contained in:
zouantchaw
2026-02-13 10:08:41 -05:00
parent 6502a2f983
commit 85532e96ac
16 changed files with 34 additions and 212 deletions

View File

@@ -6,10 +6,8 @@ import { InvoiceStatus } from "@/dataconnect-generated";
import { useGetInvoiceById, useUpdateInvoice, useListRecentPaymentsByInvoiceId } from "@/dataconnect-generated/react";
import { dataConnect } from "@/features/auth/firebase";
import DashboardLayout from "@/features/layouts/DashboardLayout";
import type { RootState } from "@/store/store";
import { format, parseISO } from "date-fns";
import { ArrowLeft, Download, Mail, CheckCircle, FileText, User, Calendar, MapPin, DollarSign } from "lucide-react";
import { useSelector } from "react-redux";
import { useNavigate, useParams } from "react-router-dom";
const statusConfig: Record<string, { label: string; className: string }> = {
@@ -25,14 +23,13 @@ const statusConfig: Record<string, { label: string; className: string }> = {
export default function InvoiceDetail() {
const navigate = useNavigate();
const { id: invoiceId } = useParams<{ id: string }>();
const { user } = useSelector((state: RootState) => state.auth);
// Fetch Invoice Data
const { data: invoiceData, isLoading: loadingInvoice } = useGetInvoiceById(dataConnect, { id: invoiceId! });
const invoice = invoiceData?.invoice;
// Fetch Payment History
const { data: paymentsData, isLoading: loadingPayments } = useListRecentPaymentsByInvoiceId(dataConnect, { invoiceId: invoiceId! });
const { data: paymentsData } = useListRecentPaymentsByInvoiceId(dataConnect, { invoiceId: invoiceId! });
const payments = paymentsData?.recentPayments || [];
// Mutations

View File

@@ -15,12 +15,9 @@ import { useToast } from "@/common/components/ui/use-toast";
import { InvoiceStatus, InovicePaymentTerms } from "@/dataconnect-generated";
import {
useCreateInvoice,
useCreateInvoiceTemplate,
useDeleteInvoiceTemplate,
useGetInvoiceById,
useListBusinesses,
useListInvoices,
useListInvoiceTemplates,
useListOrders,
useListStaff,
useListVendorRates,
@@ -71,9 +68,6 @@ export default function InvoiceEditor() {
const { data: staffData } = useListStaff(dataConnect);
const staffDirectory = staffData?.staffs || [];
const { data: templatesData, refetch: refetchTemplates } = useListInvoiceTemplates(dataConnect);
const templates = templatesData?.invoiceTemplates || [];
const { data: currentInvoiceData } = useGetInvoiceById(dataConnect, { id: effectiveInvoiceId || "" }, { enabled: isEdit && !!effectiveInvoiceId });
const existingInvoice = currentInvoiceData?.invoice;
@@ -176,7 +170,7 @@ export default function InvoiceEditor() {
phone: existingInvoice.business?.phone || "",
email: existingInvoice.business?.email || "",
address: existingInvoice.business?.address || "",
manager_name: existingInvoice.business?.contactName || "",
manager_name: existingInvoice.managerName || "",
hub_name: existingInvoice.hub || "",
vendor_id: existingInvoice.vendorNumber || ""
},
@@ -198,21 +192,6 @@ export default function InvoiceEditor() {
const selectedBusiness = businesses.find(b => b.id === selectedClientId);
if (!selectedBusiness || !position) return 0;
const businessName = selectedBusiness.businessName || "";
const extractCompanyName = (name: string) => {
if (!name) return '';
return name.split(/\s*[-]\s*/)[0].trim();
};
const mainCompanyName = extractCompanyName(businessName);
// Logic similar to MVP
const clientSpecificRate = vendorRates.find(rate =>
rate.roleName?.toLowerCase() === position.toLowerCase() &&
rate.client_name === businessName // This field might be different in Data Connect, checking listVendorRates output
);
if (clientSpecificRate) return clientSpecificRate.clientRate || 0;
const defaultRate = vendorRates.find(rate =>
rate.roleName?.toLowerCase() === position.toLowerCase()
);
@@ -314,157 +293,6 @@ export default function InvoiceEditor() {
});
};
const handleDuplicateInvoice = (invoice: any) => {
const newStaffEntries = (invoice.roles || []).map((entry: any) => ({
...entry,
date: format(new Date(), 'MM/dd/yyyy')
}));
const newInvoiceNumber = generateInvoiceNumber(invoice.business?.businessName || '', invoices);
setFormData({
invoice_number: newInvoiceNumber,
event_id: "",
event_name: invoice.order?.eventName || "",
invoice_date: format(new Date(), 'yyyy-MM-dd'),
due_date: format(addDays(new Date(), 45), 'yyyy-MM-dd'),
payment_terms: invoice.paymentTerms || "NET_45",
hub: invoice.hub || "",
manager: invoice.managerName || "",
vendor_id: invoice.vendorNumber || "",
department: invoice.order?.deparment || "",
po_reference: invoice.order?.poReference || "",
from_company: {
name: invoice.vendor?.companyName || formData.from_company.name,
address: invoice.vendor?.address || formData.from_company.address,
phone: invoice.vendor?.phone || formData.from_company.phone,
email: invoice.vendor?.email || formData.from_company.email,
},
to_company: {
name: invoice.business?.businessName || "",
phone: invoice.business?.phone || "",
email: invoice.business?.email || "",
address: invoice.business?.address || "",
manager_name: invoice.business?.contactName || "",
hub_name: invoice.hub || "",
vendor_id: invoice.vendorNumber || ""
},
staff_entries: newStaffEntries,
charges: invoice.charges || [],
other_charges: invoice.otherCharges || 0,
notes: invoice.notes || "",
});
if (invoice.businessId) {
setSelectedClientId(invoice.businessId);
}
toast({
title: "✅ Invoice Duplicated",
description: `Copied from ${invoice.invoiceNumber} - update dates and details as needed`,
});
};
const { mutate: createTemplate } = useCreateInvoiceTemplate(dataConnect);
const { mutate: deleteTemplate } = useDeleteInvoiceTemplate(dataConnect);
const handleUseTemplate = (template: any) => {
const newStaffEntries = (template.roles || []).map((entry: any) => ({
...entry,
name: "",
date: format(new Date(), 'MM/dd/yyyy'),
worked_hours: 0,
regular_hours: 0,
ot_hours: 0,
dt_hours: 0,
regular_value: 0,
ot_value: 0,
dt_value: 0,
total: 0
}));
const newInvoiceNumber = generateInvoiceNumber(template.business?.businessName || '', invoices);
setFormData((prev: any) => ({
...prev,
invoice_number: newInvoiceNumber,
invoice_date: format(new Date(), 'yyyy-MM-dd'),
due_date: format(addDays(new Date(), 45), 'yyyy-MM-dd'),
payment_terms: template.paymentTerms || "NET_45",
hub: template.hub || "",
department: "",
po_reference: template.order?.poReference || "",
from_company: {
name: template.vendor?.companyName || prev.from_company.name,
address: template.vendor?.address || prev.from_company.address,
phone: template.vendor?.phone || prev.from_company.phone,
email: template.vendor?.email || prev.from_company.email,
},
to_company: {
name: template.business?.businessName || "",
phone: template.business?.phone || "",
email: template.business?.email || "",
address: template.business?.address || "",
manager_name: template.business?.contactName || "",
hub_name: template.hub || "",
vendor_id: template.vendorNumber || ""
},
staff_entries: newStaffEntries,
charges: template.charges || [],
notes: template.notes || "",
}));
if (template.businessId) {
setSelectedClientId(template.businessId);
}
toast({
title: "✅ Template Applied",
description: `Applied "${template.name}" - fill in staff names and times`,
});
};
const handleSaveTemplate = async (templateName: string) => {
const selectedBusiness = businesses.find(b => b.id === selectedClientId);
await createTemplate({
name: templateName,
ownerId: "00000000-0000-0000-0000-000000000000", // placeholder, usually from auth
businessId: selectedClientId || undefined,
vendorId: "00000000-0000-0000-0000-000000000000", // placeholder
paymentTerms: formData.payment_terms as InovicePaymentTerms,
invoiceNumber: formData.invoice_number,
issueDate: new Date(formData.invoice_date).toISOString(),
dueDate: new Date(formData.due_date).toISOString(),
hub: formData.hub,
managerName: formData.manager,
roles: formData.staff_entries,
charges: formData.charges,
otherCharges: parseFloat(formData.other_charges) || 0,
subtotal: totals.subtotal,
amount: totals.grandTotal,
notes: formData.notes,
staffCount: formData.staff_entries.length,
chargesCount: formData.charges.length,
});
refetchTemplates();
toast({
title: "✅ Template Saved",
description: `"${templateName}" can now be reused for future invoices`,
});
};
const handleDeleteTemplate = async (templateId: string) => {
await deleteTemplate({ id: templateId });
refetchTemplates();
toast({
title: "Template Deleted",
description: "Template has been removed",
});
};
const parseTimeToMinutes = (timeStr: string) => {
if (!timeStr || timeStr === "hh:mm") return null;
const match = timeStr.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);
@@ -747,11 +575,11 @@ export default function InvoiceEditor() {
<h3 className="font-bold text-red-800">Disputed Items Highlighted</h3>
<p className="text-sm text-red-700 mt-1">
{disputedIndices.length} line item(s) have been disputed by the client.
<strong> Reason: </strong>{existingInvoice.dispute_reason || "Not specified"}
<strong> Reason: </strong>{existingInvoice.disputeReason || "Not specified"}
</p>
{existingInvoice.dispute_details && (
{existingInvoice.disputeDetails && (
<p className="text-sm text-red-600 mt-2 bg-white p-2 rounded border border-red-200">
"{existingInvoice.dispute_details}"
"{existingInvoice.disputeDetails}"
</p>
)}
<p className="text-xs text-red-600 mt-2">
@@ -775,7 +603,7 @@ export default function InvoiceEditor() {
<p className="text-xs text-muted-text">Quickly fill invoice from a completed event's shifts</p>
</div>
</div>
<Select onValueChange={(val) => {
<Select onValueChange={(val: string) => {
const event = events.find(e => e.id === val);
if (event) handleImportFromEvent(event);
}}>
@@ -1315,4 +1143,4 @@ export default function InvoiceEditor() {
</div>
</DashboardLayout>
);
}
}

View File

@@ -59,7 +59,6 @@ export default function InvoiceList() {
// If user is client, they should see their invoices. If admin, they see all.
const userRole = user?.userRole?.toUpperCase();
const isClient = userRole === "CLIENT";
const isVendor = userRole === "VENDOR";
if (isClient && inv.businessId !== user?.uid) return false;
// In a real scenario, we'd match vendorId for vendor users