Lou·Ka — tous les logements à louer du Québec, un seul endroit.
HTML 98.9%
Python 0.6%
1// -----------------------------------------------------------------------------2// Groupe KA — Annuaire des déménageurs du Québec (page partagée Lou-Ka/Immo-Ka)3// pages/Demenageurs.tsx : liste complète des entreprises de déménagement de la4// province (17 régions), filtrable par région et par texte — /api/demenageurs.5// -----------------------------------------------------------------------------6import { useEffect, useMemo, useState } from "react";78type Mover = {9 id: string;10 name: string;11 city: string;12 region: string;13 address: string;14 phone: string;15 website: string;16 rating: number | null;17 reviews: number | null;18 category: string;19};2021type Doc = {22 generated: string | null;23 total: number;24 count: number;25 regions: { region: string; n: number }[];26 movers: Mover[];27};2829const telHref = (p: string) => "tel:+1" + p.replace(/\D/g, "").slice(-10);3031const host = (u: string) => {32 try {33 return new URL(u).hostname.replace(/^www\./, "");34 } catch {35 return u;36 }37};3839export default function DemenageursPage() {40 const [doc, setDoc] = useState<Doc | null>(null);41 const [error, setError] = useState<string | null>(null);42 const [q, setQ] = useState("");43 const [region, setRegion] = useState("");4445 useEffect(() => {46 fetch("/api/demenageurs")47 .then((r) => {48 if (!r.ok) throw new Error("HTTP " + r.status);49 return r.json();50 })51 .then(setDoc)52 .catch((e) => setError(String(e)));53 }, []);5455 const filtered = useMemo(() => {56 if (!doc) return [];57 const needle = q.trim().toLowerCase();58 return doc.movers.filter(59 (m) =>60 (!region || m.region === region) &&61 (!needle || (m.name + " " + m.city).toLowerCase().includes(needle)),62 );63 }, [doc, q, region]);6465 const groups = useMemo(() => {66 const order = (doc?.regions ?? []).map((r) => r.region);67 const by: Record<string, Mover[]> = {};68 for (const m of filtered) (by[m.region] ??= []).push(m);69 return order70 .filter((r) => by[r]?.length)71 .map((r) => ({ region: r, movers: by[r] }));72 }, [doc, filtered]);7374 const filtering = q.trim() !== "" || region !== "";7576 return (77 <div className="container sources">78 <span className="kicker">Annuaire — services de déménagement</span>79 <h1>Déménageurs du Québec</h1>80 <p className="sub">81 L'annuaire le plus complet des entreprises de déménagement de la82 province : <b>{doc ? doc.total : "…"}</b> déménageurs répertoriés dans83 les 17 régions administratives du Québec, avec téléphone, site web et84 note Google. Recensement continu par le Groupe KA85 {doc?.generated ? ` — mis à jour le ${doc.generated}` : ""}.86 </p>8788 {error && <div className="notice">⚠️ {error}</div>}89 {!doc && !error && <div className="notice">Chargement…</div>}9091 {doc && (92 <>93 <div94 style={{95 display: "flex",96 flexWrap: "wrap",97 gap: 10,98 alignItems: "center",99 margin: "6px 0 10px",100 }}101 >102 <input103 type="search"104 value={q}105 onChange={(e) => setQ(e.target.value)}106 placeholder="Rechercher un déménageur ou une ville…"107 aria-label="Rechercher un déménageur ou une ville"108 style={{109 flex: "1 1 240px",110 maxWidth: 380,111 padding: "10px 14px",112 border: "1.5px solid var(--line)",113 borderRadius: 10,114 font: "inherit",115 background: "var(--surface)",116 color: "inherit",117 }}118 />119 <select120 value={region}121 onChange={(e) => setRegion(e.target.value)}122 aria-label="Filtrer par région administrative"123 style={{124 padding: "10px 12px",125 border: "1.5px solid var(--line)",126 borderRadius: 10,127 font: "inherit",128 background: "var(--surface)",129 color: "inherit",130 maxWidth: "100%",131 }}132 >133 <option value="">Toutes les régions ({doc.total})</option>134 {doc.regions.map((r) => (135 <option key={r.region} value={r.region}>136 {r.region} ({r.n})137 </option>138 ))}139 </select>140 {filtering && (141 <span style={{ color: "var(--ink-2)", fontSize: 14 }}>142 {filtered.length} résultat{filtered.length > 1 ? "s" : ""}143 </span>144 )}145 </div>146147 {filtered.length === 0 && (148 <div className="notice">149 Aucun déménageur ne correspond à cette recherche.150 </div>151 )}152153 {groups.map((g) => (154 <section key={g.region}>155 <h2 style={{ marginTop: 34 }}>156 {g.region}{" "}157 <span style={{ color: "var(--ink-2)", fontWeight: 600 }}>158 ({g.movers.length})159 </span>160 </h2>161 <div className="src-wrap" style={{ marginTop: 12 }}>162 <table className="src-table">163 <thead>164 <tr>165 <th>Entreprise</th>166 <th>Ville</th>167 <th>Téléphone</th>168 <th>Site web</th>169 <th>Note Google</th>170 </tr>171 </thead>172 <tbody>173 {g.movers.map((m) => (174 <tr key={m.id}>175 <td>176 <b>{m.name}</b>177 {m.category && (178 <div179 style={{ fontSize: 12, color: "var(--ink-2)" }}180 >181 {m.category}182 </div>183 )}184 </td>185 <td>{m.city}</td>186 <td style={{ whiteSpace: "nowrap" }}>187 {m.phone ? (188 <a href={telHref(m.phone)}>{m.phone}</a>189 ) : (190 "—"191 )}192 </td>193 <td>194 {m.website ? (195 <a196 href={m.website}197 target="_blank"198 rel="noopener noreferrer nofollow"199 >200 {host(m.website)}201 </a>202 ) : (203 "—"204 )}205 </td>206 <td style={{ whiteSpace: "nowrap" }}>207 {m.rating208 ? `★ ${m.rating.toFixed(1)} (${m.reviews ?? 0})`209 : "—"}210 </td>211 </tr>212 ))}213 </tbody>214 </table>215 </div>216 </section>217 ))}218219 <p220 className="sub"221 style={{ marginTop: 34, fontSize: 13, maxWidth: 720 }}222 >223 Annuaire informatif compilé à partir des fiches publiques Google.224 Avant d'embaucher un déménageur, vérifiez qu'il est inscrit au225 Registraire des entreprises du Québec, qu'il détient les assurances226 requises et demandez toujours une soumission écrite. Le Groupe KA227 n'a aucun lien d'affaires avec les entreprises listées.228 </p>229 </>230 )}231 </div>232 );233}234