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

@@ -56,7 +56,7 @@ export default function AddClient() {
const { mutateAsync: createBusiness, isPending: isCreatingBusiness } = useCreateBusiness(dataConnect);
const { mutateAsync: createHub, isPending: isCreatingHub } = useCreateTeamHub(dataConnect);
const { mutateAsync: createTeam, isPending: isCreatingTeam } = useCreateTeam(dataConnect);
const { mutateAsync: createTeam } = useCreateTeam(dataConnect);
const handleChange = (field: string, value: any) => {
setFormData(prev => ({ ...prev, [field]: value }));

View File

@@ -28,9 +28,7 @@ import {
useCreateCustomRateCard,
useUpdateCustomRateCard,
useDeleteCustomRateCard,
useCreateVendorRate,
useUpdateVendorRate,
useDeleteVendorRate,
useGetVendorByUserId
} from "@/dataconnect-generated/react";
import RateCardModal from "./components/RateCardModal";
@@ -111,9 +109,7 @@ function VendorCompanyPricebookView({
const { mutate: updateCustomRateCard } = useUpdateCustomRateCard();
const { mutate: deleteCustomRateCard } = useDeleteCustomRateCard();
const { mutate: createVendorRate } = useCreateVendorRate();
const { mutate: updateVendorRate } = useUpdateVendorRate();
const { mutate: deleteVendorRate } = useDeleteVendorRate();
const handleUpdateVendorRate = (vars: any) => {
updateVendorRate(vars, {
@@ -146,8 +142,13 @@ function VendorCompanyPricebookView({
}, [vendorRates, vendorName]);
const CATEGORIES = useMemo(() => {
const cats = new Set(vendorRates.map(r => r.category).filter(Boolean));
return Array.from(cats);
const categories = vendorRates.reduce<string[]>((acc, r) => {
if (r.category) {
acc.push(String(r.category));
}
return acc;
}, []);
return Array.from(new Set(categories));
}, [vendorRates]);
const handleSaveRateCard = (cardData: any) => {
@@ -233,7 +234,6 @@ function VendorCompanyPricebookView({
return rates.map((r: any) => {
const parsed = parseRoleName(r.roleName || "");
const position = parsed.position;
// Apply discount if it's a custom rate card
const proposedRate = r.clientRate * (1 - discount / 100);
@@ -470,6 +470,9 @@ function VendorCompanyPricebookView({
<div className="flex flex-wrap gap-2">
{RATE_CARDS.map((tab) => {
const cardData = customRateCards.find((c) => c.name === tab);
if (!cardData) {
return null;
}
const isRenaming = renamingCard === tab;
if (isRenaming) {

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

View File

@@ -113,7 +113,7 @@ export default function EditOrder() {
requested: totalRequested,
total: eventData.total,
poReference: eventData.po_reference,
});
} as any);
}
};
@@ -146,7 +146,7 @@ export default function EditOrder() {
staffName: s.staffName || s.staff_name,
role: s.role
}))
});
} as any);
setShowReductionAlert(false);
setPendingUpdate(null);

View File

@@ -105,7 +105,6 @@ export default function OrderDetail() {
// Fetch real shift roles to get IDs and accurate counts
const {
data: shiftRolesData,
isLoading: isLoadingShifts,
refetch: refetchShifts
} = useListShiftRolesByBusinessAndOrder(
dataConnect,
@@ -145,7 +144,7 @@ export default function OrderDetail() {
cancelMutation.mutate({
id,
status: OrderStatus.CANCELLED,
});
} as any);
};
const handleEdit = () => {

View File

@@ -13,21 +13,19 @@ import DashboardLayout from "@/features/layouts/DashboardLayout";
import {
Search,
Calendar,
Filter,
ArrowRight,
Clock,
CheckCircle,
AlertTriangle,
XCircle,
FileText
} from "lucide-react";
import React, { useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux";
import type { RootState } from "@/store/store";
import { useListOrders, useListBusinesses } from "@/dataconnect-generated/react";
import { dataConnect } from "@/features/auth/firebase";
import { format, isWithinInterval, parseISO, startOfDay, endOfDay } from "date-fns";
import { format, isWithinInterval, startOfDay, endOfDay } from "date-fns";
import { OrderStatus } from "@/dataconnect-generated";
export default function OrderList() {

View File

@@ -1,6 +1,5 @@
import React, { useState, useMemo } from "react";
import { Search, Check, X, UserPlus, Star, Clock, AlertTriangle } from "lucide-react";
import { format, isSameDay, parseISO } from "date-fns";
import { useState, useMemo } from "react";
import { Search, Check, Star, Clock, AlertTriangle } from "lucide-react";
import {
Dialog,
@@ -20,7 +19,6 @@ import {
useCreateAssignment,
useUpdateShiftRole,
useListAssignments,
useListStaffAvailabilitiesByDay
} from "@/dataconnect-generated/react";
import { dataConnect } from "@/features/auth/firebase";
import { AssignmentStatus } from "@/dataconnect-generated";
@@ -38,7 +36,6 @@ export default function AssignStaffModal({ isOpen, onClose, shift, onSuccess }:
const [selectedStaff, setSelectedStaff] = useState<any>(null);
const vendorId = shift.shift?.order?.vendorId || shift.order?.vendorId;
const shiftDate = shift.shift?.date || shift.date;
const shiftStartTime = shift.startTime || shift.start;
const shiftEndTime = shift.endTime || shift.end;

View File

@@ -207,7 +207,7 @@ export default function Schedule() {
updateOrderMutation.mutate({
id: rescheduleData.order.id,
date: rescheduleData.newDate.toISOString()
});
} as any);
}
};

View File

@@ -39,7 +39,7 @@ export default function TaskBoard() {
const [conditionalColoring, setConditionalColoring] = useState(true);
// Queries
const { data: shiftsData, isLoading: shiftsLoading } = useListShifts(dataConnect);
const { data: shiftsData } = useListShifts(dataConnect);
const { data: clientsData } = useListBusinesses(dataConnect);
const shifts = useMemo(() => shiftsData?.shifts || [], [shiftsData]);

View File

@@ -31,7 +31,7 @@ export default function TaskCard({
provided,
onClick,
itemHeight = "normal",
conditionalColoring = true
conditionalColoring: _conditionalColoring = true
}: TaskCardProps) {
const heightClasses = {
compact: "p-3",

View File

@@ -164,7 +164,8 @@ export default function AddStaff() {
<Checkbox
id={skill}
checked={field.value?.includes(skill)}
onCheckedChange={(checked) => {
onChange={(e) => {
const checked = e.target.checked;
const updatedSkills = checked
? [...(field.value || []), skill]
: field.value?.filter((s: string) => s !== skill);

View File

@@ -18,7 +18,7 @@ const ITEMS_PER_PAGE = 10;
function StaffActiveStatus({ staffId }: { staffId: string }) {
const { data: staffDetail, isLoading } = useGetStaffById(dataConnect, { id: staffId });
const getLastActiveText = (lastActive?: string) => {
const getLastActiveText = (lastActive?: string | null) => {
if (!lastActive) return 'Never';
try {
const date = new Date(lastActive);

View File

@@ -74,7 +74,7 @@ export default function EmployeeCard({ staff }: EmployeeCardProps) {
const coveragePercentage = staff.shift_coverage_percentage || 0;
const cancellationCount = staff.cancellation_count || 0;
const noShowCount = staff.no_show_count || 0;
const rating = staff.rating || 0;
const rating = staff.averageRating || 0;
const reliabilityScore = staff.reliability_score || 0;
const reliability = getReliabilityColor(reliabilityScore);

View File

@@ -107,7 +107,7 @@ export default function DocumentVault() {
const filteredStaff = useMemo(() => {
let result = [...staff];
if (isVendor && user?.uid) {
result = result.filter(s => s.ownerId === user.uid || s.createdBy === user.email);
result = result.filter(s => s.ownerId === user.uid);
}
if (searchTerm) {
result = result.filter(s =>
@@ -472,7 +472,7 @@ export default function DocumentVault() {
<thead>
<tr className="border-b border-gray-100">
<th className="text-left py-6 px-8 font-bold text-gray-400 text-[10px] uppercase tracking-wider min-w-[280px]">Employees</th>
{availableDocTypes.map((type, idx) => (
{availableDocTypes.map((type) => (
<th key={type.documentType} className="p-4 min-w-[160px]">
<div className="flex flex-col items-center gap-2">
<span className="font-bold text-gray-600 text-[10px] uppercase tracking-wider">{type.name}</span>