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// pages/Agencies.tsx : brokerage registry — banner → brokerage (office)5// -----------------------------------------------------------------------------6import { useEffect, useState } from "react";7import { Link } from "react-router-dom";8import { Franchise, fetchAgencies } from "../api";910export default function AgenciesPage() {11 const [data, setData] = useState<Franchise[] | null>(null);12 const [error, setError] = useState<string | null>(null);13 const [open, setOpen] = useState<Record<string, boolean>>({});1415 useEffect(() => {16 document.title = "Covered brokerages | House-Ka";17 fetchAgencies().then((r) => setData(r.franchises)).catch((e) => setError(String(e)));18 }, []);1920 const totalProps = (data ?? []).reduce((s, f) => s + f.total, 0);21 const totalSub = (data ?? []).reduce((s, f) => s + f.sub_agencies, 0);2223 return (24 <div className="container sources">25 <span className="kicker">Registry — brokerages & offices</span>26 <h1>Sources by brokerage</h1>27 <p className="sub">28 Every covered brokerage or team publishes its board's full inventory29 through the CREA DDF feed. Each listing is synced by a dedicated30 connector and deduplicated by DDF number — the same property published31 on several sites is only counted once. Click a source to see its homes.32 </p>3334 {error && <div className="notice"> {error}</div>}35 {!data && !error && <div className="notice">Loading…</div>}3637 {data && (38 <>39 <p className="stats-foot" style={{ marginTop: 0 }}>40 <b>{totalProps.toLocaleString("en-CA")}</b> homes ·{" "}41 <b>{data.length}</b> banners ·{" "}42 <b>{totalSub.toLocaleString("en-CA")}</b> offices43 </p>4445 <div className="franchise-list">46 {data.map((f) => {47 const isOpen = open[f.franchise] ?? false;48 return (49 <div className="franchise" key={f.franchise}>50 <button51 className="franchise-head"52 onClick={() => setOpen({ ...open, [f.franchise]: !isOpen })}53 aria-expanded={isOpen}54 >55 <span className="fh-caret">{isOpen ? "▾" : "▸"}</span>56 <span className="fh-name">{f.franchise}</span>57 <span className="fh-sub">58 {f.sub_agencies} office{f.sub_agencies > 1 ? "s" : ""}59 </span>60 <span className="fh-count">{f.total.toLocaleString("en-CA")}</span>61 </button>6263 {isOpen && (64 <ul className="subagency-list">65 {f.agencies.map((a) => (66 <li key={a.name}>67 <span className="sa-name">{a.name}</span>68 <Link69 className="count-pill"70 to={`/?source=${encodeURIComponent(a.sources[0])}`}71 >72 {a.count.toLocaleString("en-CA")}73 </Link>74 </li>75 ))}76 </ul>77 )}78 </div>79 );80 })}81 </div>8283 <p className="stats-foot">84 Sites sharing the same CREA DDF pool also serve as backup sources,85 deduplicated by DDF number — no double counting.86 </p>87 </>88 )}89 </div>90 );91}92