Python 68.8%
TypeScript 18.6%
CSS 8.7%
JavaScript 3.3%
HTML 0.6%
1// -----------------------------------------------------------------------------2// Rent-Ka — Rental listings aggregator (Canada, outside Québec)3// Author: Simon-Pierre Boucher — contact@spboucher.ai4// components/KaScoresBlock.tsx: "KA Scores" section of the listing page.5// Global badge + 5 gauges (Walk/Transit/Bike/Quiet/Services), honest6// detail per score, personalized score from the user's weights7// (/ka-scores), link to the methodology.8// -----------------------------------------------------------------------------9import { Link } from "react-router-dom";10import { KaScores, KA_DEFAULT_WEIGHTS, fmtDist, kaGlobal, kaWeights } from "../api";11import KaScoreBadge, { KaScoreCircle } from "./KaScoreBadge";1213const CAT_LABELS: Record<string, string> = {14 epicerie: "Grocery", pharmacie: "Pharmacy", parc: "Park", cafe: "Café",15 ecole: "School", clinique: "Clinic", garderie: "Daycare",16 depanneur: "Convenience store", gym: "Gym", bibliotheque: "Library",17};18const FAM_LABELS: Record<string, string> = {19 commerces: "shops", sante: "health", education: "education",20 loisirs: "leisure",21};2223export default function KaScoresBlock({ ks }: { ks: KaScores }) {24 const weights = kaWeights();25 const custom = JSON.stringify(weights) !== JSON.stringify(KA_DEFAULT_WEIGHTS);26 const perso = custom ? kaGlobal(ks, weights) : null;27 const d = ks.details ?? {};28 const walkCats = (d.walk?.cats ?? []).filter((c) => c.dist_m != null).slice(0, 5);2930 return (31 <section className="f-bloc f-kascores" id="ka-scores">32 <h2>33 KA Scores34 {ks.global != null && <KaScoreBadge score={ks.global} />}35 {perso != null && (36 <span className="ka-perso" title="Global score recomputed from your priorities (set on the KA Scores page)">37 you: <b>{Math.round(perso)}</b>38 </span>39 )}40 </h2>4142 <div className="ka-circles">43 <KaScoreCircle score={ks.walk} nom="Walk" />44 <KaScoreCircle score={ks.transit} nom="Transit"45 note={ks.transit == null ? "Not served" : undefined} />46 <KaScoreCircle score={ks.bike} nom="Bike" />47 <KaScoreCircle score={ks.calme} nom="Quiet" />48 <KaScoreCircle score={ks.services} nom="Services" />49 </div>5051 <details className="ka-detail">52 <summary>The detail behind this area's scores</summary>53 <div className="ka-detail-grille">54 {walkCats.length > 0 && (55 <div>56 <h4>Walk</h4>57 <ul>58 {walkCats.map((c) => (59 <li key={c.cat}>60 {CAT_LABELS[c.cat] ?? c.cat} : {fmtDist(c.dist_m as number)}61 {" "}{c.pts >= 99 ? "✓" : c.pts >= 50 ? "~" : "·"}62 </li>63 ))}64 </ul>65 </div>66 )}67 <div>68 <h4>Transit</h4>69 <ul>70 {d.transit?.arret_bus_m != null && (71 <li>Bus stop at {fmtDist(d.transit.arret_bus_m)}</li>72 )}73 {d.transit?.station_metro_m != null && (74 <li>Subway station at {fmtDist(d.transit.station_metro_m)}</li>75 )}76 {d.transit?.pmd_percentile != null && (77 <li>Area transit service: {d.transit.pmd_percentile}th Canadian percentile (StatCan)</li>78 )}79 {d.transit?.raison && <li>{d.transit.raison}</li>}80 </ul>81 <h4>Bike</h4>82 <ul>83 {d.bike?.km_cyclables_1km != null && (84 <li>{d.bike.km_cyclables_1km.toLocaleString("en-CA")} km of bike lanes within 1 km</li>85 )}86 {d.bike?.raison && <li>{d.bike.raison}</li>}87 </ul>88 </div>89 <div>90 <h4>Quiet</h4>91 <ul>92 {(d.calme?.sources_bruit ?? []).length === 0 && (93 <li>No major noise source detected nearby</li>94 )}95 {(d.calme?.sources_bruit ?? []).map((s) => (96 <li key={s.source}>97 {s.source}{s.dist_m != null ? ` at ${fmtDist(s.dist_m)}` : ""}98 </li>99 ))}100 {(d.calme?.bonus_parc ?? 0) > 0 && <li>Park nearby ✓</li>}101 </ul>102 {d.services?.familles && (103 <>104 <h4>Services</h4>105 <ul>106 {Object.entries(d.services.familles).map(([f, n]) => (107 <li key={f}>{n} {FAM_LABELS[f] ?? f} in the area</li>108 ))}109 </ul>110 </>111 )}112 </div>113 </div>114 </details>115116 <p className="fine">117 0-100 scores computed from OpenStreetMap and Statistics Canada118 proximity measures — Quiet is an environment estimate, not a sound119 measurement. Computed on{" "}120 {new Date(ks.computed_at * 1000).toLocaleDateString("en-CA")} (scale {ks.version}).{" "}121 <Link to="/ka-scores">How are KA Scores calculated? — and set your priorities</Link>122 </p>123 </section>124 );125}126