SPB Git forge

spb/toveli

Public
14commits 1branches 0releases
1.1 MBsize
maindefault branch
3 h agolast push
TypeScript 50.6% JavaScript 46.1% SQL 2.1% Shell 0.6% CSS 0.5%
3.2 KB · 26 lines tsx
Raw Blame History
1"use client";2import Link from "next/link";3import { useCompare } from "@/lib/favorites";4import { useCards } from "../favoris/favorites-view";5import { fmtPrice, fmtArea, fmtInt, fmtPct, fmtDate } from "@/lib/format";6import { CATEGORY_LABEL } from "@/lib/normalize";7import { Empty } from "@/components/ui";8import { IconClose } from "@/components/icons";9export function CompareView() {10  const cmp = useCompare(); const cards = useCards(cmp.items);11  if (cmp.items.length < 1) return <div className="mx-auto max-w-5xl px-4 py-8"><h1 className="text-3xl font-extrabold tracking-tight mb-4">Comparateur</h1><Empty title="Ajoutez jusqu'à 4 annonces" hint="Utilisez le bouton « Comparer » sur les fiches ou les cartes d'annonces." action={{ href: "/recherche", label: "Explorer" }} /></div>;12  const rows: [string, (c: (typeof cards)[number]) => string][] = [13    ["Prix demandé", (c) => fmtPrice(c.price, { rent: c.market === "rent" })], ["Type", (c) => c.typeLabel ?? CATEGORY_LABEL[c.category]], ["Chambres", (c) => fmtInt(c.bedrooms)], ["Salles de bain", (c) => fmtInt(c.bathrooms)],14    ["Superficie", (c) => fmtArea(c.areaSqft)], ["Prix au pi²", (c) => c.price && c.areaSqft ? `${fmtInt(c.price / c.areaSqft)} $/pi²` : "—"], ["Estimation", (c) => c.estimate ? fmtPrice(c.estimate, { rent: c.market === "rent" }) : "—"], ["Estimation vs prix", (c) => c.ratio ? fmtPct(c.ratio - 1) : "—"], ["Confiance", (c) => c.confidence ?? "—"],15    ["En ligne depuis", (c) => fmtDate(c.firstSeen)], ["Source", (c) => c.connector.replace(/_/g, " ")],16  ];17  return (18    <div className="mx-auto max-w-7xl px-4 py-8">19      <div className="flex items-end justify-between"><h1 className="text-3xl font-extrabold tracking-tight">Comparateur</h1><button type="button" onClick={cmp.clear} className="text-sm text-ink-3 hover:text-brand">Tout retirer</button></div>20      <div className="mt-5 overflow-x-auto"><table className="w-full text-sm tv-card border-separate border-spacing-0"><thead><tr><th className="p-3 text-left text-xs text-ink-3 font-medium w-40 align-bottom">Critère</th>{cards.map((c) => <th key={c.uid} className="p-3 text-left align-top min-w-[200px]"><div className="relative aspect-[4/3] rounded-sm overflow-hidden bg-surface-2 mb-2">{c.image && <img src={c.image} alt="" className="absolute inset-0 w-full h-full object-cover" referrerPolicy="no-referrer" />}<button type="button" onClick={() => cmp.toggle(c.uid)} className="absolute top-1.5 right-1.5 p-1 rounded-full bg-white/90" aria-label="Retirer"><IconClose width={14} height={14} /></button></div><Link href={`/annonce/${encodeURIComponent(c.uid)}`} className="font-semibold hover:underline line-clamp-2">{c.address ?? "Annonce"}</Link><span className="block text-xs text-ink-3 font-normal">{c.city}</span></th>)}</tr></thead>21        <tbody>{rows.map(([k, f]) => <tr key={k} className="border-t border-line"><th scope="row" className="p-3 text-left text-xs text-ink-3 font-medium border-t border-line">{k}</th>{cards.map((c) => <td key={c.uid} className="p-3 border-t border-line tabular-nums">{f(c)}</td>)}</tr>)}</tbody></table></div>22      <p className="mt-3 text-xs text-ink-3">Les estimations sont statistiques et indicatives ; comparez aussi les annonces d'origine.</p>23    </div>24  );25}26