1import { r as reactExports, t as trpc, j as jsxRuntimeExports, S as Select, a as SelectTrigger, b as SelectValue, c as SelectContent, d as SelectItem, l as Car, m as Clock, P as Package, n as Truck, T as TrendingUp, f as Badge } from "./index-DZUVA1tZ.js"; 2import { C as Card, a as CardHeader, b as CardTitle, c as CardContent } from "./card-C2_jI6O8.js"; 3import { L as LoaderCircle } from "./loader-circle-BREGMqfZ.js"; 4import { D as DollarSign } from "./dollar-sign-DMAmZp43.js"; 5import { A as ArrowUpDown } from "./arrow-up-down-C3BnUTuy.js"; 6import { R as ResponsiveContainer, C as Cell, T as Tooltip, B as Bar } from "./generateCategoricalChart-DS6b7n0z.js"; 7import { P as PieChart, a as Pie } from "./PieChart-BBtnzD7d.js"; 8import { B as BarChart } from "./BarChart-nELobgLf.js"; 9import { C as CartesianGrid } from "./CartesianGrid-Dju2DZc9.js"; 10import { X as XAxis, Y as YAxis } from "./YAxis-vcBlqPnO.js"; 11import { A as AreaChart } from "./AreaChart-DyJHC5E8.js"; 12import { A as Area } from "./Area-B_6roka7.js"; 13import "./string-Cl2mTqJp.js"; 14import "./band-CR10Xg6M.js"; 15import "./init-CTO7spbL.js"; 16import "./ordinal-B3yiDjK9.js"; 17import "./linear-CiVNRwv0.js"; 18import "./defaultLocale-DVr69WTU.js"; 19import "./time-ByDv7dtv.js"; 20import "./PolarAngleAxis-qObgFf3t.js"; 21const COLORS = ["#6366f1", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#06b6d4", "#f97316", "#84cc16"]; 22const STOCK_COLORS = { 23 NEW: "#6366f1", 24 USED: "#10b981", 25 SPECIAL: "#f59e0b", 26 CPO: "#8b5cf6" 27}; 28function formatCurrency(n) { 29 if (n >= 1e6) return `$${(n / 1e6).toFixed(1)}M`; 30 if (n >= 1e3) return `$${(n / 1e3).toFixed(0)}K`; 31 return `$${n.toFixed(0)}`; 32} 33function AcquisitionSource() { 34 const [dealerId, setDealerId] = reactExports.useState(""); 35 const { data: dealerships } = trpc.dealerships.list.useQuery(); 36 const activeDealerId = dealerId || dealerships?.[0]?.id || ""; 37 const { data, isLoading } = trpc.acquisition.metrics.useQuery( 38 { dealerId: activeDealerId }, 39 { enabled: !!activeDealerId } 40 ); 41 if (isLoading || !data) { 42 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-96", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-8 h-8 animate-spin text-indigo-500" }) }); 43 } 44 const { kpis, stockTypeBreakdown, statusBreakdown, acquisitionTrend, topMakes, ageDistribution, tradeInTrend } = data; 45 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 p-6", children: [ 46 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
47 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 48 /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-3xl font-bold tracking-tight", children: "Vehicle Acquisition" }), 49 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground mt-1", children: "Inventory sourcing, aging, and trade-in analytics" }) 50 ] }), 51 /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: activeDealerId, onValueChange: setDealerId, children: [ 52 /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "w-[220px]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "Select dealership" }) }), 53 /* @__PURE__ */ jsxRuntimeExports.jsx(SelectContent, { children: dealerships?.map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: d.id, children: d.shortName || d.name }, d.id)) }) 54 ] }) 55 ] }), 56 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4", children: [
57 /* @__PURE__ */ jsxRuntimeExports.jsx( 58 KpiCard, 59 { 60 title: "Total Inventory", 61 value: kpis.totalVehicles.toLocaleString(), 62 subtitle: `${kpis.newCount} New / ${kpis.usedCount} Used`, 63 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Car, { className: "w-5 h-5" }), 64 color: "indigo" 65 } 66 ), 67 /* @__PURE__ */ jsxRuntimeExports.jsx( 68 KpiCard, 69 { 70 title: "Avg Days (New)", 71 value: `${kpis.avgDaysNew}`, 72 subtitle: `Used: ${kpis.avgDaysUsed} days`, 73 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "w-5 h-5" }), 74 color: kpis.avgDaysNew < 45 ? "emerald" : kpis.avgDaysNew < 75 ? "amber" : "red" 75 } 76 ), 77 /* @__PURE__ */ jsxRuntimeExports.jsx( 78 KpiCard, 79 { 80 title: "Inventory Value", 81 value: formatCurrency(kpis.newInventoryValue + kpis.usedInventoryValue), 82 subtitle: `New: ${formatCurrency(kpis.newInventoryValue)}`, 83 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(DollarSign, { className: "w-5 h-5" }), 84 color: "violet" 85 } 86 ),
87 /* @__PURE__ */ jsxRuntimeExports.jsx( 88 KpiCard, 89 { 90 title: "Trade-Ins", 91 value: kpis.totalTradeIns.toLocaleString(), 92 subtitle: `Avg ACV: ${formatCurrency(kpis.avgAcv)}`, 93 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpDown, { className: "w-5 h-5" }), 94 color: "emerald" 95 } 96 ) 97 ] }), 98 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-6", children: [ 99 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [ 100 /* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, { className: "text-lg flex items-center gap-2", children: [ 101 /* @__PURE__ */ jsxRuntimeExports.jsx(Package, { className: "w-4 h-4 text-indigo-500" }), 102 "Stock Type Mix" 103 ] }) }), 104 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ResponsiveContainer, { width: "100%", height: 250, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(PieChart, { children: [
105 /* @__PURE__ */ jsxRuntimeExports.jsx( 106 Pie, 107 { 108 data: stockTypeBreakdown, 109 cx: "50%", 110 cy: "50%", 111 innerRadius: 50, 112 outerRadius: 90, 113 dataKey: "value", 114 nameKey: "name", 115 label: ({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`, 116 labelLine: false, 117 children: stockTypeBreakdown.map((entry, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(Cell, { fill: STOCK_COLORS[entry.name] || COLORS[i % COLORS.length] }, i)) 118 } 119 ), 120 /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, {}) 121 ] }) }) }) 122 ] }), 123 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [ 124 /* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, { className: "text-lg flex items-center gap-2", children: [ 125 /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "w-4 h-4 text-amber-500" }), 126 "Inventory Age Distribution" 127 ] }) }),
128 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ResponsiveContainer, { width: "100%", height: 250, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(BarChart, { data: ageDistribution, children: [ 129 /* @__PURE__ */ jsxRuntimeExports.jsx(CartesianGrid, { strokeDasharray: "3 3", opacity: 0.1 }), 130 /* @__PURE__ */ jsxRuntimeExports.jsx(XAxis, { dataKey: "label", tick: { fontSize: 11 } }), 131 /* @__PURE__ */ jsxRuntimeExports.jsx(YAxis, { tick: { fontSize: 12 } }), 132 /* @__PURE__ */ jsxRuntimeExports.jsx( 133 Tooltip, 134 { 135 contentStyle: { background: "hsl(var(--card))", border: "1px solid hsl(var(--border))", borderRadius: 8 } 136 } 137 ), 138 /* @__PURE__ */ jsxRuntimeExports.jsx(Bar, { dataKey: "count", radius: [4, 4, 0, 0], children: ageDistribution.map((_, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(Cell, { fill: i < 2 ? "#10b981" : i < 3 ? "#f59e0b" : "#ef4444" }, i)) }) 139 ] }) }) }) 140 ] }),
141 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [ 142 /* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, { className: "text-lg flex items-center gap-2", children: [ 143 /* @__PURE__ */ jsxRuntimeExports.jsx(Truck, { className: "w-4 h-4 text-violet-500" }), 144 "Top Makes" 145 ] }) }), 146 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: topMakes.map((make, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 147 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground w-4", children: i + 1 }), 148 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [ 149 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1", children: [ 150 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium", children: make.name || "Unknown" }),
151 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: make.value }) 152 ] }), 153 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-1.5 bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 154 "div", 155 { 156 className: "h-full rounded-full", 157 style: { 158 width: `${make.value / (topMakes[0]?.value || 1) * 100}%`, 159 backgroundColor: COLORS[i % COLORS.length] 160 } 161 } 162 ) }) 163 ] }) 164 ] }, make.name)) }) }) 165 ] }) 166 ] }), 167 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: [ 168 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [ 169 /* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, { className: "text-lg flex items-center gap-2", children: [
170 /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpDown, { className: "w-4 h-4 text-emerald-500" }), 171 "Trade-In Equity Analysis" 172 ] }) }), 173 /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { children: [ 174 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4 mb-4", children: [ 175 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 rounded-lg bg-emerald-500/10 border border-emerald-500/20", children: [ 176 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "Positive Equity" }), 177 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-2xl font-bold text-emerald-500", children: kpis.positiveEquityCount }), 178 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "ACV > Payoff" }) 179 ] }), 180 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 rounded-lg bg-red-500/10 border border-red-500/20", children: [
181 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "Negative Equity" }), 182 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-2xl font-bold text-red-500", children: kpis.negativeEquityCount }), 183 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "Payoff > ACV" }) 184 ] }) 185 ] }), 186 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [ 187 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center py-2 border-b border-border/30", children: [ 188 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-muted-foreground", children: "Avg ACV" }), 189 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold", children: formatCurrency(kpis.avgAcv) }) 190 ] }), 191 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center py-2 border-b border-border/30", children: [
192 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-muted-foreground", children: "Avg Payoff" }), 193 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold", children: formatCurrency(kpis.avgPayoff) }) 194 ] }), 195 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center py-2", children: [ 196 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-muted-foreground", children: "Avg Equity" }), 197 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `font-semibold ${kpis.avgAcv - kpis.avgPayoff >= 0 ? "text-emerald-500" : "text-red-500"}`, children: [ 198 formatCurrency(Math.abs(kpis.avgAcv - kpis.avgPayoff)), 199 kpis.avgAcv - kpis.avgPayoff >= 0 ? " +" : " -" 200 ] }) 201 ] }) 202 ] }) 203 ] }) 204 ] }), 205 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [ 206 /* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, { className: "text-lg flex items-center gap-2", children: [
207 /* @__PURE__ */ jsxRuntimeExports.jsx(TrendingUp, { className: "w-4 h-4 text-indigo-500" }), 208 "Monthly Trade-In Volume" 209 ] }) }), 210 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ResponsiveContainer, { width: "100%", height: 280, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(AreaChart, { data: tradeInTrend, children: [ 211 /* @__PURE__ */ jsxRuntimeExports.jsx(CartesianGrid, { strokeDasharray: "3 3", opacity: 0.1 }), 212 /* @__PURE__ */ jsxRuntimeExports.jsx(XAxis, { dataKey: "date", tick: { fontSize: 11 } }), 213 /* @__PURE__ */ jsxRuntimeExports.jsx(YAxis, { tick: { fontSize: 12 } }), 214 /* @__PURE__ */ jsxRuntimeExports.jsx( 215 Tooltip, 216 { 217 contentStyle: { background: "hsl(var(--card))", border: "1px solid hsl(var(--border))", borderRadius: 8 } 218 } 219 ), 220 /* @__PURE__ */ jsxRuntimeExports.jsx(Area, { type: "monotone", dataKey: "value", stroke: "#6366f1", fill: "#6366f133", strokeWidth: 2 }) 221 ] }) }) }) 222 ] }) 223 ] }), 224 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
225 /* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, { className: "text-lg flex items-center gap-2", children: [ 226 /* @__PURE__ */ jsxRuntimeExports.jsx(Car, { className: "w-4 h-4 text-indigo-500" }), 227 "Inventory Status" 228 ] }) }), 229 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-3", children: statusBreakdown.map((status) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-4 py-2 rounded-lg bg-muted/50 border border-border/50", children: [ 230 /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: status.name === "STOCKED_IN" ? "border-emerald-500/50 text-emerald-500" : status.name === "SOLD" ? "border-indigo-500/50 text-indigo-500" : "border-muted-foreground/50", children: status.name || "Unknown" }), 231 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold", children: status.value }) 232 ] }, status.name)) }) }) 233 ] }) 234 ] }); 235} 236function KpiCard({ title, value, subtitle, icon, color }) { 237 const colorMap = { 238 indigo: "from-indigo-500/10 to-indigo-500/5 border-indigo-500/20", 239 emerald: "from-emerald-500/10 to-emerald-500/5 border-emerald-500/20", 240 amber: "from-amber-500/10 to-amber-500/5 border-amber-500/20", 241 red: "from-red-500/10 to-red-500/5 border-red-500/20", 242 violet: "from-violet-500/10 to-violet-500/5 border-violet-500/20" 243 }; 244 const iconColorMap = { 245 indigo: "text-indigo-500", 246 emerald: "text-emerald-500", 247 amber: "text-amber-500", 248 red: "text-red-500", 249 violet: "text-violet-500" 250 }; 251 return /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { className: `bg-gradient-to-br ${colorMap[color] || colorMap.indigo} border`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-5 pb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between", children: [ 252 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
253 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground font-medium", children: title }), 254 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-2xl font-bold mt-1", children: value }), 255 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-1", children: subtitle }) 256 ] }), 257 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `p-2 rounded-lg bg-background/50 ${iconColorMap[color] || iconColorMap.indigo}`, children: icon }) 258 ] }) }) }); 259} 260export { 261 AcquisitionSource as default 262};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.