// ----------------------------------------------------------------------------- // Immo-Ka — Agrégateur de propriétés à vendre (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // pages/Category.tsx : pages programmatiques /a-vendre/{ville}[/{type}] et // /type/{type} — mêmes URL que le rendu SEO serveur (immoka/seo.py), le SPA // prend le relais au montage. Résolution slug → valeurs via /api/seo/resolve. // ----------------------------------------------------------------------------- import { useEffect, useState } from "react"; import { Link, useParams, useSearchParams } from "react-router-dom"; import { Listing, fetchListings, fetchSources, fmtPrice, registerSourceNames, resolveSeo, setDocTitle, } from "../api"; import ListingCard from "../components/ListingCard"; import { Ico } from "../components/Icons"; const PAGE_SIZE = 48; // aligné sur immoka/seo.py export default function CategoryPage() { const { ville, type } = useParams<{ ville?: string; type?: string }>(); const [params, setParams] = useSearchParams(); const page = Math.max(1, Number(params.get("page")) || 1); const [labels, setLabels] = useState<{ city?: string; property_type?: string } | null>(null); const [listings, setListings] = useState(null); const [total, setTotal] = useState(0); const [error, setError] = useState(null); useEffect(() => { fetchSources().then((r) => registerSourceNames(r.sources)).catch(() => {}); }, []); useEffect(() => { setLabels(null); setListings(null); setError(null); resolveSeo(ville, type) .then(setLabels) .catch(() => setError("Page introuvable.")); }, [ville, type]); useEffect(() => { if (!labels) return; setListings(null); fetchListings( { city: labels.city, property_type: labels.property_type, sort: "price_asc" }, PAGE_SIZE, (page - 1) * PAGE_SIZE) .then((r) => { setListings(r.listings); setTotal(r.total); }) .catch((e) => setError(String(e))); window.scrollTo(0, 0); }, [labels, page]); const h1 = labels ? labels.city && labels.property_type ? `${labels.property_type} à vendre à ${labels.city}` : labels.city ? `Propriétés à vendre à ${labels.city}` : `${labels.property_type} à vendre au Québec` : ""; useEffect(() => { if (h1) setDocTitle(h1); }, [h1]); if (error) return (

Page introuvable

{error}

Toutes les propriétés
); const pages = Math.max(1, Math.ceil(total / PAGE_SIZE)); const refine = new URLSearchParams(); if (labels?.city) refine.set("city", labels.city); if (labels?.property_type) refine.set("property_type", labels.property_type); return (

{h1 || "Chargement…"}

{listings && (

{total.toLocaleString("fr-CA")} annonces {listings.length > 0 && listings[0].price != null && ( <> · à partir de {fmtPrice(listings[0].price)} )} {" · "} Affiner la recherche (carte, filtres)

)} {!listings && !error && (
{Array.from({ length: 8 }).map((_, i) => (
))}
)} {listings && (
{listings.map((l) => )}
)} {pages > 1 && ( )}
); }