PageSourceSearch

https://lottrax.com/assets/AcquisitionSource-Draoh-r8.js

js lottrax.com collected 2026-10-09 22:54:39 UTC 17,519 bytes, 262 lines download raw bytes

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.