import { Badge } from "@/common/components/ui/badge"; import { Button } from "@/common/components/ui/button"; import { Card, CardContent } from "@/common/components/ui/card"; import { Input } from "@/common/components/ui/input"; import { useToast } from "@/common/components/ui/use-toast"; import DashboardLayout from "@/features/layouts/DashboardLayout"; import { useQueryClient } from "@tanstack/react-query"; import { BarChart3, Briefcase, DollarSign, Download, FileText, Filter, MapPin, Pencil, Plus, Search, Shield, Sparkles, Trash2 } from "lucide-react"; import { useMemo, useState } from "react"; import { useSelector } from "react-redux"; import { useListVendorRates, useListCustomRateCards, useCreateCustomRateCard, useUpdateCustomRateCard, useDeleteCustomRateCard, useUpdateVendorRate, useGetVendorByUserId } from "@/dataconnect-generated/react"; import RateCardModal from "./components/RateCardModal"; // --- Constants & Helper Functions --- function fmtCurrency(v: number | undefined | null) { if (typeof v !== "number" || Number.isNaN(v)) return "—"; return v.toLocaleString(undefined, { style: "currency", currency: "USD" }); } function downloadCSV(rows: any[], regionName: string, vendorName: string) { const headers = [ "Role", "Category", "Employee Wage", "Markup %", "Vendor Fee %", "Client Rate", ]; const lines = [headers.join(",")]; for (const r of rows) { const cells = [ r.role_name, r.category, r.employee_wage, r.markup_percentage, r.vendor_fee_percentage, r.client_rate, ]; lines.push(cells.join(",")); } const blob = new Blob([lines.join("\n")], { type: "text/csv;charset=utf-8;", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${vendorName}_${regionName}_Rates_${new Date().toISOString().slice(0, 10)}.csv`; a.click(); URL.revokeObjectURL(url); } const parseRoleName = (roleName: string) => { if (!roleName) return { position: "", region: "" }; if (roleName.includes(" - ")) { const parts = roleName.split(" - "); return { position: parts[0].trim(), region: parts[1].trim(), }; } return { position: roleName, region: "", }; }; // --- Sub-Components --- function VendorCompanyPricebookView({ vendorName, }: { vendorName: string; }) { const { toast } = useToast(); const queryClient = useQueryClient(); const { data: vendorRatesData } = useListVendorRates(); const vendorRates = vendorRatesData?.vendorRates || []; const { data: customRateCardsData } = useListCustomRateCards(); const customRateCards = customRateCardsData?.customRateCards || []; const { mutate: createCustomRateCard } = useCreateCustomRateCard(); const { mutate: updateCustomRateCard } = useUpdateCustomRateCard(); const { mutate: deleteCustomRateCard } = useDeleteCustomRateCard(); const { mutate: updateVendorRate } = useUpdateVendorRate(); const handleUpdateVendorRate = (vars: any) => { updateVendorRate(vars, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["listVendorRates"] }); } }); }; const [pricebook, setPricebook] = useState("Standard"); const [search, setSearch] = useState(""); const [activeRegion, setActiveRegion] = useState("All"); const [activeCategory, setActiveCategory] = useState("All"); const [editing, setEditing] = useState(null); const [analyzingCompetitiveness, setAnalyzingCompetitiveness] = useState(false); const [competitivenessData, setCompetitivenessData] = useState( null, ); const [showRateCardModal, setShowRateCardModal] = useState(false); const [editingRateCard, setEditingRateCard] = useState(null); const [renamingCard, setRenamingCard] = useState(null); const [renameValue, setRenameValue] = useState(""); const RATE_CARDS = customRateCards.map((c: any) => c.name); const rates = useMemo(() => { return vendorRates.filter((r) => r.vendor?.companyName === vendorName && r.isActive); }, [vendorRates, vendorName]); const CATEGORIES = useMemo(() => { const categories = vendorRates.reduce((acc, r) => { if (r.category) { acc.push(String(r.category)); } return acc; }, []); return Array.from(new Set(categories)); }, [vendorRates]); const handleSaveRateCard = (cardData: any) => { if (editingRateCard) { updateCustomRateCard({ id: editingRateCard.id, name: cardData.name, baseBook: cardData.baseBook, discount: cardData.discount, isDefault: editingRateCard.isDefault }, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["listCustomRateCards"] }); toast({ title: "Rate card updated successfully" }); } }); } else { createCustomRateCard({ name: cardData.name, baseBook: cardData.baseBook, discount: cardData.discount, isDefault: false }, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["listCustomRateCards"] }); toast({ title: "Rate card saved successfully" }); } }); } setPricebook(cardData.name); setEditingRateCard(null); }; const handleDeleteRateCard = (cardId: string, cardName: string) => { if (customRateCards.length <= 1) { toast({ title: "Cannot delete the last rate card", variant: "destructive", }); return; } deleteCustomRateCard({ id: cardId }, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["listCustomRateCards"] }); toast({ title: "Rate card deleted" }); if (pricebook === cardName) { const nextCard = customRateCards.find((c: any) => c.id !== cardId); setPricebook(nextCard ? nextCard.name : "Standard"); } } }); }; const handleRenameCard = (cardId: string, oldName: string) => { if (!renameValue.trim() || renameValue === oldName) { setRenamingCard(null); return; } const currentCard = customRateCards.find((c: any) => c.id === cardId); if (currentCard) { updateCustomRateCard({ id: cardId, name: renameValue.trim(), baseBook: currentCard.baseBook, discount: currentCard.discount, isDefault: currentCard.isDefault }, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["listCustomRateCards"] }); if (pricebook === oldName) { setPricebook(renameValue.trim()); } } }); } setRenamingCard(null); }; const scopedByBook = useMemo(() => { const isCustomRateCard = customRateCards.find(c => c.name === pricebook); const discount = isCustomRateCard?.discount || 0; return rates.map((r: any) => { const parsed = parseRoleName(r.roleName || ""); // Apply discount if it's a custom rate card const proposedRate = r.clientRate * (1 - discount / 100); const assignedRegion = r.vendor?.region || parsed.region || (r.notes?.includes("Bay Area") ? "Bay Area" : "LA"); return { ...r, client: pricebook, region: assignedRegion, approvedCap: r.clientRate, proposedRate: proposedRate, position: r.roleName, markupPct: r.markupPercentage, volDiscountPct: r.vendorFeePercentage, overtime8Multiplier: r.overtime8Multiplier || 1.5, overtime12Multiplier: r.overtime12Multiplier || 2.0, holidayRate: r.holidayRate || proposedRate * 1.5 }; }); }, [rates, pricebook, customRateCards]); const APPROVED_RATES = useMemo(() => { return customRateCards.filter(c => c.isDefault).map(c => c.name); }, [customRateCards]); const isApprovedRate = APPROVED_RATES.includes(pricebook) || pricebook === "Standard"; const filtered = useMemo(() => { return (scopedByBook as any[]).filter((r) => { const regionMatch = !isApprovedRate || activeRegion === "All" || r.region === activeRegion || parseRoleName(r.position).region === activeRegion; const categoryMatch = activeCategory === "All" || r.category === activeCategory; const searchMatch = search.trim() === "" || parseRoleName(r.position) .position.toLowerCase() .includes(search.toLowerCase()); return regionMatch && categoryMatch && searchMatch; }); }, [scopedByBook, activeRegion, activeCategory, search, isApprovedRate]); const kpis = useMemo(() => { const rateValues = filtered.map((r) => r.proposedRate); const avg = rateValues.length ? rateValues.reduce((a, b) => a + b, 0) / rateValues.length : 0; const min = rateValues.length ? Math.min(...rateValues) : 0; const max = rateValues.length ? Math.max(...rateValues) : 0; const total = filtered.length; return { avg, min, max, total }; }, [filtered]); async function analyzeCompetitiveness() { setAnalyzingCompetitiveness(true); try { await new Promise((resolve) => setTimeout(resolve, 1500)); const mockAnalysis = filtered.slice(0, 20).map((r) => ({ position: parseRoleName(r.position).position, marketRate: r.proposedRate * (0.9 + Math.random() * 0.2), score: Math.floor(60 + Math.random() * 40), status: ["Highly Competitive", "Competitive", "Average", "Above Market"][ Math.floor(Math.random() * 4) ], recommendation: "Consider slight adjustment.", })); setCompetitivenessData(mockAnalysis); toast({ title: "Competitive analysis complete" }); } catch (error) { console.error("Analysis error:", error); toast({ title: "Analysis failed. Try again.", variant: "destructive" }); } finally { setAnalyzingCompetitiveness(false); } } return ( } >
{/* AI Analysis Result Block - Styled like Dispute Alert */} {competitivenessData && competitivenessData.length > 0 && (

Market Intelligence Analysis Complete

Results based on current market data for {pricebook}.

{[{ "status": "Highly Competitive", "color": "bg-emerald-500", "textColor": "text-emerald-700" }, { "status": "Competitive", "color": "bg-blue-500", "textColor": "text-blue-700" }, { "status": "Average", "color": "bg-yellow-500", "textColor": "text-yellow-700" }, { "status": "Above Market", "color": "bg-red-500", "textColor": "text-red-700" }].map(({ status, color, textColor }) => { const count = competitivenessData.filter((d) => d.status === status).length; return (
{status}

{count}

); })}
)} {/* Rate Books Section Header */}

Rate Books

Manage standard and custom client pricebooks

{/* Book Selector Grid - Using bg-muted/20 like Client Selection */}
{/* Approved Enterprise Rates */}

Enterprise Books

{APPROVED_RATES.map((tab) => ( ))}
{/* Custom Rate Cards */}

Custom Cards

{RATE_CARDS.map((tab) => { const cardData = customRateCards.find((c) => c.name === tab); if (!cardData) { return null; } const isRenaming = renamingCard === tab; if (isRenaming) { return (
setRenameValue(e.target.value)} onBlur={() => handleRenameCard(cardData.id, tab)} onKeyDown={(e) => { if (e.key === "Enter") handleRenameCard(cardData.id, tab); if (e.key === "Escape") setRenamingCard(null); }} autoFocus className="px-3 py-2 rounded-lg text-sm font-semibold border border-primary bg-white focus:outline-none w-40" />
); } return (
{/* Hover actions simplified */}
); })}
{/* KPIs Section */}

Average Rate

{fmtCurrency(kpis.avg)}

Across {kpis.total} positions active in book

Coverage

{kpis.total} roles

Total defined positions

Spread

{fmtCurrency(kpis.min)} – {fmtCurrency(kpis.max)}

Rate range (Min - Max)

{/* Filters - styled like the Search/Filter blocks */}
Filters
{/* Categories */}
{["All", ...CATEGORIES].map((cat) => ( ))}
setSearch(e.target.value)} placeholder="Search positions..." className="pl-9 h-9 bg-white border-border/50" />
{/* Main Rates Table */}
{/* Section Header */}
💰

Rate Breakdown

Detailed pricing for {pricebook}

{filtered.map((r: any, idx) => { const parsed = parseRoleName(r.position); const competitive = competitivenessData?.find( (c) => c.position === parsed.position, ); const isEditing = editing === r.id; return ( ); })}
Position Category Region Base Wage {pricebook} Rate OT 8h OT 12h Holiday Market Rate Comp. Score Actions
{/* Icon for position */}
{parsed.position.substring(0, 2).toUpperCase()}

{parsed.position}

{r.category} {r.region !== '—' && } {r.region} {fmtCurrency(r.employee_wage)} {isEditing ? ( { handleUpdateVendorRate({ id: r.id, clientRate: parseFloat(e.target.value) }); setEditing(null); toast({ title: "Rate updated successfully" }); }} autoFocus /> ) : ( {fmtCurrency(r.proposedRate)} )} {isEditing ? ( { handleUpdateVendorRate({ id: r.id, overtime8Multiplier: parseFloat(e.target.value) }); }} /> ) : ( {r.overtime8Multiplier}x )} {isEditing ? ( { handleUpdateVendorRate({ id: r.id, overtime12Multiplier: parseFloat(e.target.value) }); }} /> ) : ( {r.overtime12Multiplier}x )} {isEditing ? ( { handleUpdateVendorRate({ id: r.id, holidayRate: parseFloat(e.target.value) }); }} /> ) : ( {fmtCurrency(r.holidayRate)} )} {competitive ? (
{fmtCurrency(competitive.marketRate)} {r.proposedRate < competitive.marketRate ? "▼ Below" : "▲ Above"}
) : }
{competitive ? (
{competitive.score}/100
) : }
{ setShowRateCardModal(false); setEditingRateCard(null); }} onSave={handleSaveRateCard} editingCard={editingRateCard} />
); } // NOTE: Creating a dummy FilesIcon component to suppress errors if it's missing, or assumelucide import const FilesIcon = FileText; // The user asked to match InvoiceEditor design. InvoiceEditor is primarily a form/management view. // I will ensure the VendorCompanyPricebookView is the primary export and fully fleshed out. export default function ServiceRates() { const { user } = useSelector((state: any) => state.auth); const { data: vendorData } = useGetVendorByUserId({ userId: user?.uid || "" }, { enabled: !!user?.uid }); const vendorName = vendorData?.vendors[0]?.companyName || "Vendor"; return ; }