// ----------------------------------------------------------------------------- // House-Ka — Homes-for-sale aggregator (Canada outside Québec, Ontario first) // Author: Simon-Pierre Boucher — contact@spboucher.ai // components/NearbyPlaces.tsx : “Shops and transit” block (listing page) // Distance to the closest location of each big banner (Costco, Walmart, // Canadian Tire… via the Mapbox Search Box API) + nearest transit stops. // SVG monogram chips in the banners' colours (no registered logos). // ----------------------------------------------------------------------------- import { useEffect, useState } from "react"; import { CommercesNearby, fetchCommerces, fmtDist } from "../api"; // id -> [background colour, monogram, text colour] const ICONS: Record = { metro_station: ["#0083C9", "M"], rem_station: ["#84BD00", "R"], arret_bus: ["#4E5357", "B"], gare_train: ["#6E5B3F", "T"], // national costco: ["#005DAA", "C"], walmart: ["#0071CE", "W"], canadiantire: ["#D6001C", "CT"], dollarama: ["#00B140", "D", "#FFDD00"], homedepot: ["#F96302", "HD"], homehardware: ["#DA291C", "HH"], shoppers: ["#E51937", "SDM"], gianttiger: ["#FFD200", "GT", "#D6001C"], // grocery — Loblaw banners loblaws: ["#E4002B", "L"], superstore: ["#D22630", "RCS"], atlanticsuperstore: ["#D22630", "AS"], nofrills: ["#FFDD00", "NF", "#111111"], zehrs: ["#C8102E", "Z"], dominion: ["#C8102E", "Do"], independent: ["#C8102E", "YIG"], // grocery — Empire/Sobeys banners sobeys: ["#00563F", "S"], safeway: ["#ED1C24", "SW"], foodland: ["#00794A", "FL"], freshco: ["#54B948", "FC"], // grocery — Metro (Ontario) metro: ["#EF3E42", "M"], foodbasics: ["#EE3124", "FB"], // grocery — western / co-ops / regional saveon: ["#00543D", "SOF"], coop: ["#00843D", "CO"], colemans: ["#0067B1", "Cm"], iga: ["#D50032", "IGA"], // provincial liquor boards lcbo: ["#00532A", "LC"], beerstore: ["#1E4620", "TBS"], bcliquor: ["#003E51", "BCL"], liquormart: ["#004F9F", "LM"], nbliquor: ["#00539F", "NBL"], nslc: ["#0B4F32", "NSL"], // regional pharmacies londondrugs: ["#004F9F", "LD"], rexall: ["#003087", "Rx"], lawtons: ["#00539F", "Lw"], // hardware rona: ["#1B4298", "R"], kent: ["#006A4D", "K"], }; function Chip({ id }: { id: string }) { const [bg, mono, fg] = ICONS[id] ?? ["#777", "•"]; const fs = mono.length >= 3 ? 9 : mono.length === 2 ? 11 : 14; return ( ); } export default function NearbyPlaces({ lat, lng, region }: { lat: number | null; lng: number | null; region?: string }) { const [d, setD] = useState(null); useEffect(() => { setD(null); if (lat == null || lng == null) return; fetchCommerces(lat, lng, region).then(setD).catch(() => setD(null)); }, [lat, lng, region]); if (lat == null || lng == null || !d) return null; const all = [...(d.transit ?? []), ...(d.commerces ?? [])]; if (all.length === 0) return null; return (

Shops and transit

    {all.map((c) => (
  • {c.commerce} {c.nom} {fmtDist(c.dist_m)}
  • ))}

Closest location of each banner — straight-line distances (Mapbox search; transit: OpenStreetMap).

); }