Lou·Ka — tous les logements à louer du Québec, un seul endroit.
HTML 98.9%
Python 0.6%
1// -----------------------------------------------------------------------------2// Lou-Ka — Agrégateur de logements à louer (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// fiche/LouKaScore.tsx : carte « Lou-Ka Score » — anneau 0-100 animé, libellé,5// composantes (emplacement / prix) et méthodologie en accordéon. Quand une6// composante manque, le score est présenté comme partiel ; quand aucune7// n'est fiable, la carte affiche la synthèse sans chiffre (jamais inventé).8// -----------------------------------------------------------------------------9import { useEffect, useState } from "react";10import { Link } from "react-router-dom";11import { IcoSparkles } from "../components/Icons";12import { LouKaScore as Score } from "./synthese";13import { Accordion, SectionCard } from "./ui";1415export function ScoreRing({ value, partial, size = 88 }: { value: number | null; partial?: boolean; size?: number }) {16 const [v, setV] = useState(0);17 useEffect(() => {18 const t = requestAnimationFrame(() => setV(value ?? 0));19 return () => cancelAnimationFrame(t);20 }, [value]);21 const r = 40, c = 2 * Math.PI * r;22 return (23 <div className="lk-score-ring" style={{ width: size, height: size }} role="img"24 aria-label={value != null ? `Lou-Ka Score ${value} sur 100${partial ? ", partiel" : ""}` : "Score non calculable"}>25 <svg viewBox="0 0 100 100" style={{ width: size, height: size }} aria-hidden="true">26 <circle className="bg" cx="50" cy="50" r={r} />27 <circle className={`arc ${partial ? "partial" : ""}`} cx="50" cy="50" r={r}28 strokeDasharray={`${(c * Math.max(0, Math.min(100, v))) / 100} ${c}`} />29 </svg>30 <div className="lk-score-val">31 <div>{value != null ? value : "—"}<small>/ 100</small></div>32 </div>33 </div>34 );35}3637export default function LouKaScore({ s, resume }: { s: Score; resume: string[] }) {38 return (39 <SectionCard id="score" title="Lou-Ka Score" icon={<IcoSparkles size={18} />}40 sub={s.partial ? "Score partiel — une composante manque" : undefined}>41 <div className="lk-score">42 <ScoreRing value={s.value} partial={s.partial} />43 <div>44 <div className="lk-score-lbl">45 {s.value != null ? s.label : "Pas assez de données pour un score"}46 </div>47 {resume.length > 0 && <div className="lk-score-sub">{resume.join(" · ")}</div>}48 <div className="lk-score-parts">49 <span className={`lk-score-part ${s.emplacement == null ? "na" : ""}`}>50 Emplacement {s.emplacement != null ? <b>{s.emplacement}</b> : "non évalué"}51 </span>52 <span className={`lk-score-part ${s.prix == null ? "na" : ""}`}>53 Prix {s.prix != null ? <b>{s.prix}</b> : "non évalué"}54 </span>55 </div>56 </div>57 </div>58 <Accordion title="Comment est calculé ce score ?" small>59 <p>{s.explication}</p>60 <p>61 <Link to="/ka-scores">Méthodologie des KA Scores</Link> · <Link to="/juste-valeur">Méthodologie de la juste valeur</Link>62 </p>63 </Accordion>64 </SectionCard>65 );66}67