SPB Git

spb/house-ka

Public
3.2 KB · 85 lines tsx
1// -----------------------------------------------------------------------------2// House-Ka — Homes-for-sale aggregator (Canada outside Québec, Ontario first)3// Author: Simon-Pierre Boucher — contact@spboucher.ai4// components/NearbyPlaces.tsx : “Shops and transit” block (listing page)5//   Distance to the closest location of each big banner (Costco, Walmart,6//   Canadian Tire… via the Mapbox Search Box API) + nearest transit stops.7//   SVG monogram chips in the banners' colours (no registered logos).8// -----------------------------------------------------------------------------9import { useEffect, useState } from "react";10import { CommercesNearby, fetchCommerces, fmtDist } from "../api";1112// id -> [background colour, monogram, text colour]13const ICONS: Record<string, [string, string, string?]> = {14  metro_station: ["#0083C9", "M"],15  rem_station: ["#84BD00", "R"],16  arret_bus: ["#4E5357", "B"],17  gare_train: ["#6E5B3F", "T"],18  costco: ["#005DAA", "C"],19  walmart: ["#0071CE", "W"],20  metro: ["#EF3E42", "M"],21  iga: ["#D50032", "IGA"],22  maxi: ["#0079C1", "Mx"],23  superc: ["#E4002B", "SC"],24  provigo: ["#DA291C", "P"],25  canadiantire: ["#D6001C", "CT"],26  dollarama: ["#00B140", "D", "#FFDD00"],27  saq: ["#892034", "SAQ"],28  pharmaprix: ["#E11B22", "Ph"],29  jeancoutu: ["#003DA5", "JC"],30  homedepot: ["#F96302", "HD"],31  rona: ["#1B4298", "R"],32};3334function Chip({ id }: { id: string }) {35  const [bg, mono, fg] = ICONS[id] ?? ["#777", "•"];36  const fs = mono.length >= 3 ? 9 : mono.length === 2 ? 11 : 14;37  return (38    <svg className="cm-ico" viewBox="0 0 28 28" width="28" height="28"39         aria-hidden="true">40      {["metro_station", "rem_station", "arret_bus", "gare_train"].includes(id)41        ? <circle cx="14" cy="14" r="13" fill={bg} />42        : <rect x="1" y="1" width="26" height="26" rx="7" fill={bg} />}43      <text x="14" y="14" textAnchor="middle" dominantBaseline="central"44            fontSize={fs} fontWeight="800" fontFamily="inherit"45            fill={fg ?? "#fff"}>{mono}</text>46    </svg>47  );48}4950export default function NearbyPlaces({ lat, lng }:51    { lat: number | null; lng: number | null }) {52  const [d, setD] = useState<CommercesNearby | null>(null);53  useEffect(() => {54    setD(null);55    if (lat == null || lng == null) return;56    fetchCommerces(lat, lng).then(setD).catch(() => setD(null));57  }, [lat, lng]);58  if (lat == null || lng == null || !d) return null;59  const all = [...(d.transit ?? []), ...(d.commerces ?? [])];60  if (all.length === 0) return null;6162  return (63    <section className="f-bloc f-commerces" id="nearby">64      <h2>Shops and transit</h2>65      <ul className="cm-grille">66        {all.map((c) => (67          <li key={c.id} className="cm-item"68              title={c.adresse || undefined}>69            <Chip id={c.id} />70            <span className="cm-txt">71              <span className="cm-nom">{c.commerce}</span>72              <span className="cm-poi">{c.nom}</span>73            </span>74            <span className="cm-dist">{fmtDist(c.dist_m)}</span>75          </li>76        ))}77      </ul>78      <p className="fine">79        Closest location of each banner — straight-line distances (Mapbox80        search; transit: OpenStreetMap).81      </p>82    </section>83  );84}85
Files · 852c914b66dca5351aaa5df6ece6070a6e3d64c5
Menu

Theme