SPB Git forge

spb/house-ka

Public
18commits 1branches 0releases
1.9 MBsize
maindefault branch
19 days agolast push
Python 67% TypeScript 18.2% CSS 14.4%
4.2 KB · 115 lines tsx
Raw Blame History
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  // national19  costco: ["#005DAA", "C"],20  walmart: ["#0071CE", "W"],21  canadiantire: ["#D6001C", "CT"],22  dollarama: ["#00B140", "D", "#FFDD00"],23  homedepot: ["#F96302", "HD"],24  homehardware: ["#DA291C", "HH"],25  shoppers: ["#E51937", "SDM"],26  gianttiger: ["#FFD200", "GT", "#D6001C"],27  // grocery — Loblaw banners28  loblaws: ["#E4002B", "L"],29  superstore: ["#D22630", "RCS"],30  atlanticsuperstore: ["#D22630", "AS"],31  nofrills: ["#FFDD00", "NF", "#111111"],32  zehrs: ["#C8102E", "Z"],33  dominion: ["#C8102E", "Do"],34  independent: ["#C8102E", "YIG"],35  // grocery — Empire/Sobeys banners36  sobeys: ["#00563F", "S"],37  safeway: ["#ED1C24", "SW"],38  foodland: ["#00794A", "FL"],39  freshco: ["#54B948", "FC"],40  // grocery — Metro (Ontario)41  metro: ["#EF3E42", "M"],42  foodbasics: ["#EE3124", "FB"],43  // grocery — western / co-ops / regional44  saveon: ["#00543D", "SOF"],45  coop: ["#00843D", "CO"],46  colemans: ["#0067B1", "Cm"],47  iga: ["#D50032", "IGA"],48  // provincial liquor boards49  lcbo: ["#00532A", "LC"],50  beerstore: ["#1E4620", "TBS"],51  bcliquor: ["#003E51", "BCL"],52  liquormart: ["#004F9F", "LM"],53  nbliquor: ["#00539F", "NBL"],54  nslc: ["#0B4F32", "NSL"],55  // regional pharmacies56  londondrugs: ["#004F9F", "LD"],57  rexall: ["#003087", "Rx"],58  lawtons: ["#00539F", "Lw"],59  // hardware60  rona: ["#1B4298", "R"],61  kent: ["#006A4D", "K"],62};6364function Chip({ id }: { id: string }) {65  const [bg, mono, fg] = ICONS[id] ?? ["#777", "•"];66  const fs = mono.length >= 3 ? 9 : mono.length === 2 ? 11 : 14;67  return (68    <svg className="cm-ico" viewBox="0 0 28 28" width="28" height="28"69         aria-hidden="true">70      {["metro_station", "rem_station", "arret_bus", "gare_train"].includes(id)71        ? <circle cx="14" cy="14" r="13" fill={bg} />72        : <rect x="1" y="1" width="26" height="26" rx="7" fill={bg} />}73      <text x="14" y="14" textAnchor="middle" dominantBaseline="central"74            fontSize={fs} fontWeight="800" fontFamily="inherit"75            fill={fg ?? "#fff"}>{mono}</text>76    </svg>77  );78}7980export default function NearbyPlaces({ lat, lng, region }:81    { lat: number | null; lng: number | null; region?: string }) {82  const [d, setD] = useState<CommercesNearby | null>(null);83  useEffect(() => {84    setD(null);85    if (lat == null || lng == null) return;86    fetchCommerces(lat, lng, region).then(setD).catch(() => setD(null));87  }, [lat, lng, region]);88  if (lat == null || lng == null || !d) return null;89  const all = [...(d.transit ?? []), ...(d.commerces ?? [])];90  if (all.length === 0) return null;9192  return (93    <section className="f-bloc f-commerces" id="nearby">94      <h2>Shops and transit</h2>95      <ul className="cm-grille">96        {all.map((c) => (97          <li key={c.id} className="cm-item"98              title={c.adresse || undefined}>99            <Chip id={c.id} />100            <span className="cm-txt">101              <span className="cm-nom">{c.commerce}</span>102              <span className="cm-poi">{c.nom}</span>103            </span>104            <span className="cm-dist">{fmtDist(c.dist_m)}</span>105          </li>106        ))}107      </ul>108      <p className="fine">109        Closest location of each banner — straight-line distances (Mapbox110        search; transit: OpenStreetMap).111      </p>112    </section>113  );114}115