Fix web typecheck errors across core feature modules
This commit is contained in:
@@ -56,7 +56,7 @@ export default function AddClient() {
|
|||||||
|
|
||||||
const { mutateAsync: createBusiness, isPending: isCreatingBusiness } = useCreateBusiness(dataConnect);
|
const { mutateAsync: createBusiness, isPending: isCreatingBusiness } = useCreateBusiness(dataConnect);
|
||||||
const { mutateAsync: createHub, isPending: isCreatingHub } = useCreateTeamHub(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) => {
|
const handleChange = (field: string, value: any) => {
|
||||||
setFormData(prev => ({ ...prev, [field]: value }));
|
setFormData(prev => ({ ...prev, [field]: value }));
|
||||||
|
|||||||
@@ -28,9 +28,7 @@ import {
|
|||||||
useCreateCustomRateCard,
|
useCreateCustomRateCard,
|
||||||
useUpdateCustomRateCard,
|
useUpdateCustomRateCard,
|
||||||
useDeleteCustomRateCard,
|
useDeleteCustomRateCard,
|
||||||
useCreateVendorRate,
|
|
||||||
useUpdateVendorRate,
|
useUpdateVendorRate,
|
||||||
useDeleteVendorRate,
|
|
||||||
useGetVendorByUserId
|
useGetVendorByUserId
|
||||||
} from "@/dataconnect-generated/react";
|
} from "@/dataconnect-generated/react";
|
||||||
import RateCardModal from "./components/RateCardModal";
|
import RateCardModal from "./components/RateCardModal";
|
||||||
@@ -111,9 +109,7 @@ function VendorCompanyPricebookView({
|
|||||||
const { mutate: updateCustomRateCard } = useUpdateCustomRateCard();
|
const { mutate: updateCustomRateCard } = useUpdateCustomRateCard();
|
||||||
const { mutate: deleteCustomRateCard } = useDeleteCustomRateCard();
|
const { mutate: deleteCustomRateCard } = useDeleteCustomRateCard();
|
||||||
|
|
||||||
const { mutate: createVendorRate } = useCreateVendorRate();
|
|
||||||
const { mutate: updateVendorRate } = useUpdateVendorRate();
|
const { mutate: updateVendorRate } = useUpdateVendorRate();
|
||||||
const { mutate: deleteVendorRate } = useDeleteVendorRate();
|
|
||||||
|
|
||||||
const handleUpdateVendorRate = (vars: any) => {
|
const handleUpdateVendorRate = (vars: any) => {
|
||||||
updateVendorRate(vars, {
|
updateVendorRate(vars, {
|
||||||
@@ -146,8 +142,13 @@ function VendorCompanyPricebookView({
|
|||||||
}, [vendorRates, vendorName]);
|
}, [vendorRates, vendorName]);
|
||||||
|
|
||||||
const CATEGORIES = useMemo(() => {
|
const CATEGORIES = useMemo(() => {
|
||||||
const cats = new Set(vendorRates.map(r => r.category).filter(Boolean));
|
const categories = vendorRates.reduce<string[]>((acc, r) => {
|
||||||
return Array.from(cats);
|
if (r.category) {
|
||||||
|
acc.push(String(r.category));
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
return Array.from(new Set(categories));
|
||||||
}, [vendorRates]);
|
}, [vendorRates]);
|
||||||
|
|
||||||
const handleSaveRateCard = (cardData: any) => {
|
const handleSaveRateCard = (cardData: any) => {
|
||||||
@@ -233,7 +234,6 @@ function VendorCompanyPricebookView({
|
|||||||
|
|
||||||
return rates.map((r: any) => {
|
return rates.map((r: any) => {
|
||||||
const parsed = parseRoleName(r.roleName || "");
|
const parsed = parseRoleName(r.roleName || "");
|
||||||
const position = parsed.position;
|
|
||||||
|
|
||||||
// Apply discount if it's a custom rate card
|
// Apply discount if it's a custom rate card
|
||||||
const proposedRate = r.clientRate * (1 - discount / 100);
|
const proposedRate = r.clientRate * (1 - discount / 100);
|
||||||
@@ -470,6 +470,9 @@ function VendorCompanyPricebookView({
|
|||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{RATE_CARDS.map((tab) => {
|
{RATE_CARDS.map((tab) => {
|
||||||
const cardData = customRateCards.find((c) => c.name === tab);
|
const cardData = customRateCards.find((c) => c.name === tab);
|
||||||
|
if (!cardData) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const isRenaming = renamingCard === tab;
|
const isRenaming = renamingCard === tab;
|
||||||
|
|
||||||
if (isRenaming) {
|
if (isRenaming) {
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import { InvoiceStatus } from "@/dataconnect-generated";
|
|||||||
import { useGetInvoiceById, useUpdateInvoice, useListRecentPaymentsByInvoiceId } from "@/dataconnect-generated/react";
|
import { useGetInvoiceById, useUpdateInvoice, useListRecentPaymentsByInvoiceId } from "@/dataconnect-generated/react";
|
||||||
import { dataConnect } from "@/features/auth/firebase";
|
import { dataConnect } from "@/features/auth/firebase";
|
||||||
import DashboardLayout from "@/features/layouts/DashboardLayout";
|
import DashboardLayout from "@/features/layouts/DashboardLayout";
|
||||||
import type { RootState } from "@/store/store";
|
|
||||||
import { format, parseISO } from "date-fns";
|
import { format, parseISO } from "date-fns";
|
||||||
import { ArrowLeft, Download, Mail, CheckCircle, FileText, User, Calendar, MapPin, DollarSign } from "lucide-react";
|
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";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
|
||||||
const statusConfig: Record<string, { label: string; className: string }> = {
|
const statusConfig: Record<string, { label: string; className: string }> = {
|
||||||
@@ -25,14 +23,13 @@ const statusConfig: Record<string, { label: string; className: string }> = {
|
|||||||
export default function InvoiceDetail() {
|
export default function InvoiceDetail() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { id: invoiceId } = useParams<{ id: string }>();
|
const { id: invoiceId } = useParams<{ id: string }>();
|
||||||
const { user } = useSelector((state: RootState) => state.auth);
|
|
||||||
|
|
||||||
// Fetch Invoice Data
|
// Fetch Invoice Data
|
||||||
const { data: invoiceData, isLoading: loadingInvoice } = useGetInvoiceById(dataConnect, { id: invoiceId! });
|
const { data: invoiceData, isLoading: loadingInvoice } = useGetInvoiceById(dataConnect, { id: invoiceId! });
|
||||||
const invoice = invoiceData?.invoice;
|
const invoice = invoiceData?.invoice;
|
||||||
|
|
||||||
// Fetch Payment History
|
// Fetch Payment History
|
||||||
const { data: paymentsData, isLoading: loadingPayments } = useListRecentPaymentsByInvoiceId(dataConnect, { invoiceId: invoiceId! });
|
const { data: paymentsData } = useListRecentPaymentsByInvoiceId(dataConnect, { invoiceId: invoiceId! });
|
||||||
const payments = paymentsData?.recentPayments || [];
|
const payments = paymentsData?.recentPayments || [];
|
||||||
|
|
||||||
// Mutations
|
// Mutations
|
||||||
|
|||||||
@@ -15,12 +15,9 @@ import { useToast } from "@/common/components/ui/use-toast";
|
|||||||
import { InvoiceStatus, InovicePaymentTerms } from "@/dataconnect-generated";
|
import { InvoiceStatus, InovicePaymentTerms } from "@/dataconnect-generated";
|
||||||
import {
|
import {
|
||||||
useCreateInvoice,
|
useCreateInvoice,
|
||||||
useCreateInvoiceTemplate,
|
|
||||||
useDeleteInvoiceTemplate,
|
|
||||||
useGetInvoiceById,
|
useGetInvoiceById,
|
||||||
useListBusinesses,
|
useListBusinesses,
|
||||||
useListInvoices,
|
useListInvoices,
|
||||||
useListInvoiceTemplates,
|
|
||||||
useListOrders,
|
useListOrders,
|
||||||
useListStaff,
|
useListStaff,
|
||||||
useListVendorRates,
|
useListVendorRates,
|
||||||
@@ -71,9 +68,6 @@ export default function InvoiceEditor() {
|
|||||||
const { data: staffData } = useListStaff(dataConnect);
|
const { data: staffData } = useListStaff(dataConnect);
|
||||||
const staffDirectory = staffData?.staffs || [];
|
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 { data: currentInvoiceData } = useGetInvoiceById(dataConnect, { id: effectiveInvoiceId || "" }, { enabled: isEdit && !!effectiveInvoiceId });
|
||||||
const existingInvoice = currentInvoiceData?.invoice;
|
const existingInvoice = currentInvoiceData?.invoice;
|
||||||
|
|
||||||
@@ -176,7 +170,7 @@ export default function InvoiceEditor() {
|
|||||||
phone: existingInvoice.business?.phone || "",
|
phone: existingInvoice.business?.phone || "",
|
||||||
email: existingInvoice.business?.email || "",
|
email: existingInvoice.business?.email || "",
|
||||||
address: existingInvoice.business?.address || "",
|
address: existingInvoice.business?.address || "",
|
||||||
manager_name: existingInvoice.business?.contactName || "",
|
manager_name: existingInvoice.managerName || "",
|
||||||
hub_name: existingInvoice.hub || "",
|
hub_name: existingInvoice.hub || "",
|
||||||
vendor_id: existingInvoice.vendorNumber || ""
|
vendor_id: existingInvoice.vendorNumber || ""
|
||||||
},
|
},
|
||||||
@@ -198,21 +192,6 @@ export default function InvoiceEditor() {
|
|||||||
const selectedBusiness = businesses.find(b => b.id === selectedClientId);
|
const selectedBusiness = businesses.find(b => b.id === selectedClientId);
|
||||||
if (!selectedBusiness || !position) return 0;
|
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 =>
|
const defaultRate = vendorRates.find(rate =>
|
||||||
rate.roleName?.toLowerCase() === position.toLowerCase()
|
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) => {
|
const parseTimeToMinutes = (timeStr: string) => {
|
||||||
if (!timeStr || timeStr === "hh:mm") return null;
|
if (!timeStr || timeStr === "hh:mm") return null;
|
||||||
const match = timeStr.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);
|
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>
|
<h3 className="font-bold text-red-800">Disputed Items Highlighted</h3>
|
||||||
<p className="text-sm text-red-700 mt-1">
|
<p className="text-sm text-red-700 mt-1">
|
||||||
{disputedIndices.length} line item(s) have been disputed by the client.
|
{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>
|
</p>
|
||||||
{existingInvoice.dispute_details && (
|
{existingInvoice.disputeDetails && (
|
||||||
<p className="text-sm text-red-600 mt-2 bg-white p-2 rounded border border-red-200">
|
<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>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-red-600 mt-2">
|
<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>
|
<p className="text-xs text-muted-text">Quickly fill invoice from a completed event's shifts</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Select onValueChange={(val) => {
|
<Select onValueChange={(val: string) => {
|
||||||
const event = events.find(e => e.id === val);
|
const event = events.find(e => e.id === val);
|
||||||
if (event) handleImportFromEvent(event);
|
if (event) handleImportFromEvent(event);
|
||||||
}}>
|
}}>
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ export default function InvoiceList() {
|
|||||||
// If user is client, they should see their invoices. If admin, they see all.
|
// If user is client, they should see their invoices. If admin, they see all.
|
||||||
const userRole = user?.userRole?.toUpperCase();
|
const userRole = user?.userRole?.toUpperCase();
|
||||||
const isClient = userRole === "CLIENT";
|
const isClient = userRole === "CLIENT";
|
||||||
const isVendor = userRole === "VENDOR";
|
|
||||||
|
|
||||||
if (isClient && inv.businessId !== user?.uid) return false;
|
if (isClient && inv.businessId !== user?.uid) return false;
|
||||||
// In a real scenario, we'd match vendorId for vendor users
|
// In a real scenario, we'd match vendorId for vendor users
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export default function EditOrder() {
|
|||||||
requested: totalRequested,
|
requested: totalRequested,
|
||||||
total: eventData.total,
|
total: eventData.total,
|
||||||
poReference: eventData.po_reference,
|
poReference: eventData.po_reference,
|
||||||
});
|
} as any);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ export default function EditOrder() {
|
|||||||
staffName: s.staffName || s.staff_name,
|
staffName: s.staffName || s.staff_name,
|
||||||
role: s.role
|
role: s.role
|
||||||
}))
|
}))
|
||||||
});
|
} as any);
|
||||||
|
|
||||||
setShowReductionAlert(false);
|
setShowReductionAlert(false);
|
||||||
setPendingUpdate(null);
|
setPendingUpdate(null);
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ export default function OrderDetail() {
|
|||||||
// Fetch real shift roles to get IDs and accurate counts
|
// Fetch real shift roles to get IDs and accurate counts
|
||||||
const {
|
const {
|
||||||
data: shiftRolesData,
|
data: shiftRolesData,
|
||||||
isLoading: isLoadingShifts,
|
|
||||||
refetch: refetchShifts
|
refetch: refetchShifts
|
||||||
} = useListShiftRolesByBusinessAndOrder(
|
} = useListShiftRolesByBusinessAndOrder(
|
||||||
dataConnect,
|
dataConnect,
|
||||||
@@ -145,7 +144,7 @@ export default function OrderDetail() {
|
|||||||
cancelMutation.mutate({
|
cancelMutation.mutate({
|
||||||
id,
|
id,
|
||||||
status: OrderStatus.CANCELLED,
|
status: OrderStatus.CANCELLED,
|
||||||
});
|
} as any);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = () => {
|
const handleEdit = () => {
|
||||||
|
|||||||
@@ -13,21 +13,19 @@ import DashboardLayout from "@/features/layouts/DashboardLayout";
|
|||||||
import {
|
import {
|
||||||
Search,
|
Search,
|
||||||
Calendar,
|
Calendar,
|
||||||
Filter,
|
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
Clock,
|
Clock,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
XCircle,
|
|
||||||
FileText
|
FileText
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React, { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import type { RootState } from "@/store/store";
|
import type { RootState } from "@/store/store";
|
||||||
import { useListOrders, useListBusinesses } from "@/dataconnect-generated/react";
|
import { useListOrders, useListBusinesses } from "@/dataconnect-generated/react";
|
||||||
import { dataConnect } from "@/features/auth/firebase";
|
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";
|
import { OrderStatus } from "@/dataconnect-generated";
|
||||||
|
|
||||||
export default function OrderList() {
|
export default function OrderList() {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useMemo } from "react";
|
import { useState, useMemo } from "react";
|
||||||
import { Search, Check, X, UserPlus, Star, Clock, AlertTriangle } from "lucide-react";
|
import { Search, Check, Star, Clock, AlertTriangle } from "lucide-react";
|
||||||
import { format, isSameDay, parseISO } from "date-fns";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -20,7 +19,6 @@ import {
|
|||||||
useCreateAssignment,
|
useCreateAssignment,
|
||||||
useUpdateShiftRole,
|
useUpdateShiftRole,
|
||||||
useListAssignments,
|
useListAssignments,
|
||||||
useListStaffAvailabilitiesByDay
|
|
||||||
} from "@/dataconnect-generated/react";
|
} from "@/dataconnect-generated/react";
|
||||||
import { dataConnect } from "@/features/auth/firebase";
|
import { dataConnect } from "@/features/auth/firebase";
|
||||||
import { AssignmentStatus } from "@/dataconnect-generated";
|
import { AssignmentStatus } from "@/dataconnect-generated";
|
||||||
@@ -38,7 +36,6 @@ export default function AssignStaffModal({ isOpen, onClose, shift, onSuccess }:
|
|||||||
const [selectedStaff, setSelectedStaff] = useState<any>(null);
|
const [selectedStaff, setSelectedStaff] = useState<any>(null);
|
||||||
|
|
||||||
const vendorId = shift.shift?.order?.vendorId || shift.order?.vendorId;
|
const vendorId = shift.shift?.order?.vendorId || shift.order?.vendorId;
|
||||||
const shiftDate = shift.shift?.date || shift.date;
|
|
||||||
const shiftStartTime = shift.startTime || shift.start;
|
const shiftStartTime = shift.startTime || shift.start;
|
||||||
const shiftEndTime = shift.endTime || shift.end;
|
const shiftEndTime = shift.endTime || shift.end;
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ export default function Schedule() {
|
|||||||
updateOrderMutation.mutate({
|
updateOrderMutation.mutate({
|
||||||
id: rescheduleData.order.id,
|
id: rescheduleData.order.id,
|
||||||
date: rescheduleData.newDate.toISOString()
|
date: rescheduleData.newDate.toISOString()
|
||||||
});
|
} as any);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default function TaskBoard() {
|
|||||||
const [conditionalColoring, setConditionalColoring] = useState(true);
|
const [conditionalColoring, setConditionalColoring] = useState(true);
|
||||||
|
|
||||||
// Queries
|
// Queries
|
||||||
const { data: shiftsData, isLoading: shiftsLoading } = useListShifts(dataConnect);
|
const { data: shiftsData } = useListShifts(dataConnect);
|
||||||
const { data: clientsData } = useListBusinesses(dataConnect);
|
const { data: clientsData } = useListBusinesses(dataConnect);
|
||||||
|
|
||||||
const shifts = useMemo(() => shiftsData?.shifts || [], [shiftsData]);
|
const shifts = useMemo(() => shiftsData?.shifts || [], [shiftsData]);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default function TaskCard({
|
|||||||
provided,
|
provided,
|
||||||
onClick,
|
onClick,
|
||||||
itemHeight = "normal",
|
itemHeight = "normal",
|
||||||
conditionalColoring = true
|
conditionalColoring: _conditionalColoring = true
|
||||||
}: TaskCardProps) {
|
}: TaskCardProps) {
|
||||||
const heightClasses = {
|
const heightClasses = {
|
||||||
compact: "p-3",
|
compact: "p-3",
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ export default function AddStaff() {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
id={skill}
|
id={skill}
|
||||||
checked={field.value?.includes(skill)}
|
checked={field.value?.includes(skill)}
|
||||||
onCheckedChange={(checked) => {
|
onChange={(e) => {
|
||||||
|
const checked = e.target.checked;
|
||||||
const updatedSkills = checked
|
const updatedSkills = checked
|
||||||
? [...(field.value || []), skill]
|
? [...(field.value || []), skill]
|
||||||
: field.value?.filter((s: string) => s !== skill);
|
: field.value?.filter((s: string) => s !== skill);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const ITEMS_PER_PAGE = 10;
|
|||||||
function StaffActiveStatus({ staffId }: { staffId: string }) {
|
function StaffActiveStatus({ staffId }: { staffId: string }) {
|
||||||
const { data: staffDetail, isLoading } = useGetStaffById(dataConnect, { id: staffId });
|
const { data: staffDetail, isLoading } = useGetStaffById(dataConnect, { id: staffId });
|
||||||
|
|
||||||
const getLastActiveText = (lastActive?: string) => {
|
const getLastActiveText = (lastActive?: string | null) => {
|
||||||
if (!lastActive) return 'Never';
|
if (!lastActive) return 'Never';
|
||||||
try {
|
try {
|
||||||
const date = new Date(lastActive);
|
const date = new Date(lastActive);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function EmployeeCard({ staff }: EmployeeCardProps) {
|
|||||||
const coveragePercentage = staff.shift_coverage_percentage || 0;
|
const coveragePercentage = staff.shift_coverage_percentage || 0;
|
||||||
const cancellationCount = staff.cancellation_count || 0;
|
const cancellationCount = staff.cancellation_count || 0;
|
||||||
const noShowCount = staff.no_show_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 reliabilityScore = staff.reliability_score || 0;
|
||||||
const reliability = getReliabilityColor(reliabilityScore);
|
const reliability = getReliabilityColor(reliabilityScore);
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default function DocumentVault() {
|
|||||||
const filteredStaff = useMemo(() => {
|
const filteredStaff = useMemo(() => {
|
||||||
let result = [...staff];
|
let result = [...staff];
|
||||||
if (isVendor && user?.uid) {
|
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) {
|
if (searchTerm) {
|
||||||
result = result.filter(s =>
|
result = result.filter(s =>
|
||||||
@@ -472,7 +472,7 @@ export default function DocumentVault() {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-gray-100">
|
<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>
|
<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]">
|
<th key={type.documentType} className="p-4 min-w-[160px]">
|
||||||
<div className="flex flex-col items-center gap-2">
|
<div className="flex flex-col items-center gap-2">
|
||||||
<span className="font-bold text-gray-600 text-[10px] uppercase tracking-wider">{type.name}</span>
|
<span className="font-bold text-gray-600 text-[10px] uppercase tracking-wider">{type.name}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user