SPB Git forge

spb/immo-ka

Public

Immo-Ka — agrégateur des propriétés à vendre au Québec (73 connecteurs, ~40 000 annonces, React+FastAPI)

112commits 1branches 0releases
125.4 MBsize
maindefault branch
13 days agolast push
Python 47.5% HTML 27.9% TypeScript 15.5% CSS 7.2% JavaScript 2%
3.1 KB · 66 lines tsx
Raw Blame History
1// -----------------------------------------------------------------------------2// Immo-Ka — Agrégateur de propriétés à vendre (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// fiche/ImmoKaScore.tsx : carte « Immo-Ka Score » — anneau 0-100 animé,5//   libellé, composantes (emplacement / prix) et méthodologie en accordéon.6//   Quand une composante manque, le score est présenté comme partiel ; quand7//   aucune n'est fiable, la carte affiche la synthèse sans chiffre.8// -----------------------------------------------------------------------------9import { useEffect, useState } from "react";10import { Ico } from "../components/Icons";11import { ImmoKaScore as Score } from "./synthese";12import { Accordion, SectionCard } from "./ui";1314export function ScoreRing({ value, partial, size = 88 }: { value: number | null; partial?: boolean; size?: number }) {15  const [v, setV] = useState(0);16  useEffect(() => {17    const t = requestAnimationFrame(() => setV(value ?? 0));18    return () => cancelAnimationFrame(t);19  }, [value]);20  const r = 40, c = 2 * Math.PI * r;21  return (22    <div className="ik-score-ring" style={{ width: size, height: size }} role="img"23         aria-label={value != null ? `Immo-Ka Score ${value} sur 100${partial ? ", partiel" : ""}` : "Score non calculable"}>24      <svg viewBox="0 0 100 100" style={{ width: size, height: size }} aria-hidden="true">25        <circle className="bg" cx="50" cy="50" r={r} />26        <circle className={`arc ${partial ? "partial" : ""}`} cx="50" cy="50" r={r}27                strokeDasharray={`${(c * Math.max(0, Math.min(100, v))) / 100} ${c}`} />28      </svg>29      <div className="ik-score-val">30        <div>{value != null ? value : "—"}<small>/ 100</small></div>31      </div>32    </div>33  );34}3536export default function ImmoKaScore({ s, resume }: { s: Score; resume: string[] }) {37  return (38    <SectionCard id="score" title="Immo-Ka Score" icon={<Ico name="sparkles" size={18} />}39                 sub={s.partial ? "Score partiel — une composante manque" : undefined}>40      <div className="ik-score">41        <ScoreRing value={s.value} partial={s.partial} />42        <div>43          <div className="ik-score-lbl">44            {s.value != null ? s.label : "Pas assez de données pour un score"}45          </div>46          {resume.length > 0 && <div className="ik-score-sub">{resume.join(" · ")}</div>}47          <div className="ik-score-parts">48            <span className={`ik-score-part ${s.emplacement == null ? "na" : ""}`}>49              Emplacement {s.emplacement != null ? <b>{s.emplacement}</b> : "non évalué"}50            </span>51            <span className={`ik-score-part ${s.prix == null ? "na" : ""}`}>52              Prix {s.prix != null ? <b>{s.prix}</b> : "non évalué"}53            </span>54          </div>55        </div>56      </div>57      <Accordion title="Comment est calculé ce score ?" small>58        <p>{s.explication}</p>59        <p>60          <a href="https://www.vrai-prix.com" target="_blank" rel="noopener noreferrer">Méthodologie Vrai-Prix ↗</a> · <a href="/sources">Données et sources Immo-Ka</a>61        </p>62      </Accordion>63    </SectionCard>64  );65}66