SPB Git forge

spb/toveli

Public
14commits 1branches 0releases
1.1 MBsize
maindefault branch
2 h agolast push
TypeScript 50.6% JavaScript 46.1% SQL 2.1% Shell 0.6% CSS 0.5%
2.9 KB · 42 lines tsx
Raw Blame History
1"use client";2import { Area, AreaChart, CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";3import { fmtPrice, fmtDate } from "@/lib/format";45const RED = "#e0263c";6export function PriceHistoryChart({ events, rent = false }: { events: { ts: string; price: number }[]; rent?: boolean }) {7  if (events.length < 2) return null;8  const data = events.map((e) => ({ t: new Date(e.ts).getTime(), price: e.price }));9  return (10    <div className="h-44">11      <ResponsiveContainer>12        <LineChart data={data} margin={{ top: 8, right: 8, bottom: 0, left: 0 }}>13          <CartesianGrid stroke="#eef0f3" vertical={false} />14          <XAxis dataKey="t" type="number" domain={["dataMin", "dataMax"]} tickFormatter={(v) => fmtDate(new Date(v).toISOString())} tick={{ fontSize: 11, fill: "#7a8190" }} axisLine={false} tickLine={false} minTickGap={40} />15          <YAxis tickFormatter={(v) => fmtPrice(v, { compact: true })} tick={{ fontSize: 11, fill: "#7a8190" }} axisLine={false} tickLine={false} width={56} domain={["auto", "auto"]} />16          <Tooltip formatter={(v) => [fmtPrice(Number(v), { rent }), "Prix demandé"]} labelFormatter={(v) => fmtDate(new Date(Number(v)).toISOString(), "long")} contentStyle={{ borderRadius: 10, border: "1px solid #e6e8ec", fontSize: 12 }} />17          <Line type="stepAfter" dataKey="price" stroke={RED} strokeWidth={2} dot={{ r: 3, fill: RED }} isAnimationActive={false} />18        </LineChart>19      </ResponsiveContainer>20    </div>21  );22}2324export function IndexChart({ points, label = "Indice de prix" }: { points: { month: string; idx: number; ppm2?: number; n?: number }[]; label?: string }) {25  if (points.length < 2) return null;26  const data = points.map((p) => ({ m: p.month, v: Math.round(p.idx * 1000) / 10, ppm2: p.ppm2 }));27  return (28    <div className="h-52">29      <ResponsiveContainer>30        <AreaChart data={data} margin={{ top: 8, right: 8, bottom: 0, left: 0 }}>31          <defs><linearGradient id="tvg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor={RED} stopOpacity={0.25} /><stop offset="100%" stopColor={RED} stopOpacity={0} /></linearGradient></defs>32          <CartesianGrid stroke="#eef0f3" vertical={false} />33          <XAxis dataKey="m" tick={{ fontSize: 11, fill: "#7a8190" }} axisLine={false} tickLine={false} minTickGap={32} tickFormatter={(m) => m.slice(0, 4)} />34          <YAxis tick={{ fontSize: 11, fill: "#7a8190" }} axisLine={false} tickLine={false} width={40} domain={["auto", "auto"]} tickFormatter={(v) => `${v}`} />35          <Tooltip formatter={(v, _n, p) => [`${v} (base 100 = dernier mois)${p?.payload?.ppm2 ? ` · ${Math.round(p.payload.ppm2).toLocaleString("fr-CA")} $/m²` : ""}`, label]} contentStyle={{ borderRadius: 10, border: "1px solid #e6e8ec", fontSize: 12 }} />36          <Area type="monotone" dataKey="v" stroke={RED} strokeWidth={2} fill="url(#tvg)" isAnimationActive={false} />37        </AreaChart>38      </ResponsiveContainer>39    </div>40  );41}42