SPB Git forge

spb/home-ka

Public
10commits 1branches 0releases
793.0 KBsize
maindefault branch
20 days agolast push
Python 49.6% TypeScript 25.5% CSS 24.1%
5.0 KB · 97 lines markdown
Rendered Raw Blame History
1# Ka Maps — Synchronisation liste ↔ carte (le modèle Groupe-KA)23Référence du module « recherche synchronisée » introduit avec Lou-Ka4(`frontend/src/search/MapSearch.tsx`), conçu pour être décliné tel quel sur5**immo-ka**, **resto-ka**, **sorti-ka** et servir de contrat à l'app iOS KA.67## Le principe : une seule vérité partagée89La liste et la carte sont **deux vues du même état de recherche** :1011```12État = { filtres, zone (bbox visible OU polygone dessiné), tri, page }13```1415Chaque changement d'état déclenche **UNE requête** vers l'endpoint unifié16(`/api/search` chez Lou-Ka) qui renvoie **dans la même réponse** :1718| Champ          | Rôle |19|----------------|------|20| `total`        | compteur partagé — affiché en tête de liste, égal PAR CONSTRUCTION au nombre de points carte |21| `listings`     | la page de liste demandée (objets complets) |22| `points`       | TOUS les points carte au format compact `[uid, lng, lat, prix, verdict]`, **triés comme la liste** |23| `unpositioned` | annonces filtrées sans coordonnées (affichées honnêtement, jamais silencieusement perdues) |2425Corollaires structurels (pas des efforts de synchronisation, des invariants) :2627- compteur liste = nombre de marqueurs, dans 100 % des cas ;28- `index d'un uid dans points` ÷ `page_size` = sa page de liste → un clic sur29  n'importe quel marqueur peut TOUJOURS faire défiler la liste vers l'annonce,30  même si elle est sur une autre page ;31- aucune requête périmée ne peut écraser une récente (numéro de séquence +32  `AbortController` ; le serveur n'est jamais la source du tri d'arrivée).3334## Répartition des rôles3536**Ka Maps (framework)** fournit :3738- `KaMap.fitBounds(bbox)` / `fitToProperties()` — recadrages animés marqués39  *programmés* ;40- `moveend { byUser }` — distinction geste utilisateur / mouvement du code,41  c'est la clé du « respect de l'intention » : seuls les gestes verrouillent42  la vue (`userLocked`) et déclenchent la recherche par zone ;43- sélection & survol bidirectionnels par feature-state GPU44  (`select(uid, "map"|"app")`, `setHovered`, événements `select`/`hover`) ;45- outil polygone intégré (`startDraw`, `clearDrawnPolygon`,46  `setDrawnPolygon`, événement `draw`) + `<DrawControl/>` ;47- fourchette de prix des clusters au survol (`clusterHover`, accumulateurs48  `valueMin`/`valueMax`) ;49- état « vu » (`setSeenIds`) — pastilles atténuées des annonces consultées ;50- utilitaires : `bboxOfProperties`, `pointInPolygon`, `bboxToString`,51  `cameraToParams`/`cameraFromParams`.5253**L'app** possède : l'état de recherche, la requête unifiée, l'URL, la liste,54le carrousel mobile et le langage visuel des cartes/mini-fiches.5556## La machine d'états côté app (copier ce comportement)57581. **Arrivée sans caméra dans l'URL** : requête sans zone → `fitBounds`59   animé sur les résultats (`byUser: false`, ne verrouille pas la vue).602. **Arrivée avec caméra (lien partagé)** : la zone visible restaurée devient61   la contrainte spatiale de la première requête ; vue considérée verrouillée.623. **Geste utilisateur** (`byUser: true`) : verrouille la vue ; si63   « Rechercher quand je déplace la carte » (défaut : coché) → nouvelle64   requête avec la bbox (debounce ~250 ms après la fin du geste) ; sinon65   marquer la zone divergée et montrer « Rechercher dans cette zone ».664. **Filtre modifié** : page 1 ; si la vue n'est PAS verrouillée → requête67   sans zone + fitBounds ; si verrouillée → requête dans la zone courante et68   bouton discret « Recadrer sur les résultats ».695. **Polygone dessiné** : remplace la bbox (il EST la zone), devient une puce70   retirable, encodé dans l'URL (`zone=lng,lat;…`), fitBounds sur son contenu.716. **Tri/page** : même requête ; un changement de page passe72   `include=liste` (les points, identiques, ne sont pas retéléchargés).737. **URL** : caméra (`lat/lng/zoom`) + `tri` + `page` + `zone` + `move=0`74   via `replaceState` — partager le lien reproduit la recherche à l'identique.7576## Miroirs (latence < 100 ms)7778- survol carte d'annonce → `setHovered(uid, "app")` (feature-state, aucun re-rendu carte) ;79- survol marqueur → événement `hover` → classe CSS sur la carte d'annonce +80  indicateur « ▲/▼ annonce hors écran » si elle n'est pas visible ;81- clic marqueur → `select` → page ajustée au besoin → défilement animé + pulsation ;82- clic annonce → `select(uid, "app")` → recentrage doux (uniquement si hors83  champ) + mini-fiche ; Cmd/Ctrl-clic ou 2ᵉ clic → navigation vers la fiche.8485## Mobile8687Bascule Liste ↔ Carte sans perte (état dans l'URL). En vue carte : carrousel88horizontal `scroll-snap` en bas, alimenté par la MÊME page de liste ;89balayage → sélection du marqueur (`select`, origin `"app"`), marqueur tapé →90défilement du carrousel. Un seul état, deux projections.9192## Anti-régression9394- E2E : `lou-ka/scripts/test-sync.mjs` (25 vérifications, critères 1-9).95- Unitaires framework : `ka-maps/tests/drawGeo.test.ts` (+ suites existantes).96- API : `lou-ka/tests/test_search.py` (règle d'or, tris, polygone, bornes).97