Python 67%
TypeScript 18.2%
CSS 14.4%
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