spb/lou-ka Public
Lou·Ka — tous les logements à louer du Québec, un seul endroit.
HTML 99.7%
1// -----------------------------------------------------------------------------2// Lou-Ka — Agrégateur de logements à louer (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// pages/Passerelle.tsx : page de transition vers l'annonce originale5// (« passerelle » à la Centris). Ouverte dans un nouvel onglet par les CTA6// de la fiche : affiche Lou-Ka → gestionnaire avec une barre de progression,7// puis redirige vers l'annonce chez le gestionnaire après ~2,2 s.8// Sécurité : on reçoit un uid (jamais une URL brute) — l'URL de destination9// vient de notre API, pas du paramètre. Aucune redirection ouverte possible.10// -----------------------------------------------------------------------------11import { useEffect, useRef, useState } from "react";12import { Link, useParams } from "react-router-dom";13import { fetchListing, fetchSources, registerSourceNames, sourceName } from "../api";1415const DELAI_MS = 2200;1617export default function PasserellePage() {18 const { uid } = useParams<{ uid: string }>();19 const [dest, setDest] = useState<{ url: string; source: string } | null>(null);20 const [error, setError] = useState(false);21 const timer = useRef<number | null>(null);2223 useEffect(() => {24 if (!uid) return;25 let cancelled = false;26 Promise.all([fetchSources(), fetchListing(uid)])27 .then(([src, l]) => {28 if (cancelled) return;29 registerSourceNames(src.sources);30 if (!l.url || !/^https?:\/\//.test(l.url)) { setError(true); return; }31 setDest({ url: l.url, source: l.source });32 timer.current = window.setTimeout(() => {33 window.location.replace(l.url);34 }, DELAI_MS);35 })36 .catch(() => !cancelled && setError(true));37 return () => {38 cancelled = true;39 if (timer.current) window.clearTimeout(timer.current);40 };41 }, [uid]);4243 if (error)44 return (45 <div className="notice container">46 <div className="big">⚠️</div>47 <h2>Annonce introuvable</h2>48 <p>Impossible de retrouver l'annonce originale.</p>49 <Link className="btn btn-primary" to="/">Retour aux logements</Link>50 </div>51 );5253 const nom = dest ? sourceName(dest.source) : "…";5455 return (56 <div className="passerelle" role="status" aria-live="polite">57 <div className="pass-card">58 <div className="pass-brands">59 <span className="pass-brand">60 Lou<span className="ka">Ka</span>61 </span>62 <span className="pass-trajet" aria-hidden="true">63 <span className="pass-dot" /><span className="pass-dot" /><span className="pass-dot" />64 <span className="pass-fleche">➔</span>65 </span>66 <span className="pass-gestionnaire">67 <span className="pass-gest-ico" aria-hidden="true">🏢</span>68 {nom}69 </span>70 </div>7172 <h1>Passerelle vers l'annonce originale</h1>73 <p className="pass-texte">74 {dest75 ? <>Nous vous amenons chez <b>{nom}</b> — les prix, visites et76 signatures se passent directement chez le gestionnaire.</>77 : "Préparation de la redirection…"}78 </p>7980 <div className="pass-progress" aria-hidden="true">81 <span className={`pass-progress-fill ${dest ? "go" : ""}`} />82 </div>8384 <div className="pass-actions">85 {dest && (86 <a className="btn btn-primary" href={dest.url} rel="noopener noreferrer">87 Y aller maintenant ↗88 </a>89 )}90 <button className="btn btn-ghost" onClick={() => window.close()}>91 Rester sur Lou-Ka92 </button>93 </div>9495 <div className="fine">96 Lou-Ka est un agrégateur indépendant : chaque fiche renvoie à97 l'annonce publiée par le gestionnaire immobilier.98 </div>99 </div>100 </div>101 );102}103