Vrai-Prix — l'évaluation du vrai prix des propriétés résidentielles au Québec.
TypeScript 90.2%
JavaScript 3.5%
Python 3.4%
CSS 1.9%
HTML 0.6%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2// Projet : Groupe Ka / Ka Maps (intégration Vrai-Prix)3// lib/kamaps.ts : thème Vrai-Prix Maps + adaptateur /api/nearby.4// La carte affiche des VALEURS ESTIMÉES (jamais des prix demandés) :5// pastilles encre à liseré rouge #ff5148, sélection rouge — le langage6// visuel du site (encre #141814, « lime » rouge, bordeaux #9e2a25).78import type {9 BoundsQuery,10 BoundsQueryResult,11 KaDataAdapter,12 KaMapTheme,13 MapProperty,14} from "@groupe-ka/ka-maps";1516const INK = "#141814";17const RED = "#ff5148"; // --lime (nom hérité)18const BORDEAUX = "#9e2a25"; // --green (nom hérité)1920export const MAPBOX_TOKEN: string =21 process.env.NEXT_PUBLIC_MAPBOX_TOKEN ??22 "pk.eyJ1Ijoic3Bib3VjaGVyIiwiYSI6ImNtc3Fyb3k4djAwOTgyenB3dWt6NHBjc2kifQ.poqLf0ADy3lIh28O-pFI2Q";2324/** Zoom minimal de chargement : sous ce niveau, on invite à zoomer. */25export const MIN_ZOOM_FETCH = 12;2627export const TYPE_FR: Record<string, string> = {28 unifamilial: "Unifamiliale",29 condo_ou_multi: "Condo / multi",30 plex: "Plex",31 chalet: "Chalet",32 terrain: "Terrain",33 maison_mobile: "Maison mobile",34 autre: "Autre",35};3637export const vraiPrixMapTheme: KaMapTheme = {38 id: "vrai-prix",39 productName: "Vrai-Prix Maps",40 accent: RED,41 onAccent: "#ffffff",42 fontFamily: "var(--font-inter), system-ui, sans-serif",43 markers: {44 sale: {45 background: INK,46 text: "#ffffff",47 halo: RED,48 selectedBackground: RED,49 selectedText: "#ffffff",50 },51 // Valeurs estimées : pastille encre, liseré rouge — visuellement52 // distinctes d'un prix demandé (jamais présentées comme une annonce).53 valuation: {54 background: INK,55 text: "#ffffff",56 halo: RED,57 selectedBackground: RED,58 selectedText: "#ffffff",59 },60 },61 cluster: {62 background: BORDEAUX,63 text: "#ffffff",64 border: "rgba(255,81,72,0.5)",65 },66 supportsDark: false,67};6869interface NearbyUnit {70 id: string;71 adresse: string | null;72 apt: string | null;73 municipalite: string | null;74 typeProp: string | null;75 lat: number;76 lng: number;77 est2026: number | null;78}7980function toMapProperty(u: NearbyUnit): MapProperty {81 return {82 id: u.id,83 appSource: "vrai-prix",84 latitude: u.lat,85 longitude: u.lng,86 kind: "valuation",87 estimatedValue: u.est2026 ?? undefined,88 propertyType: u.typeProp ? TYPE_FR[u.typeProp] ?? u.typeProp : undefined,89 address: u.apt ? `${u.adresse} app. ${u.apt}` : u.adresse ?? undefined,90 city: u.municipalite ?? undefined,91 originalUrl: `/estimation/${u.id}`,92 extra: { typeProp: u.typeProp },93 };94}9596/** Adaptateur viewport→données : /api/nearby (contrat partagé avec iOS). */97export const vraiPrixMapAdapter: KaDataAdapter = {98 id: "vrai-prix-nearby",99 appSource: "vrai-prix",100 async fetchInBounds(query: BoundsQuery): Promise<BoundsQueryResult> {101 if (query.zoom < MIN_ZOOM_FETCH) return { properties: [] };102 const { bbox } = query;103 const lat = (bbox.north + bbox.south) / 2;104 const lng = (bbox.east + bbox.west) / 2;105 const halfLat = Math.abs(bbox.north - lat);106 const halfLng = Math.abs(bbox.east - lng);107 const params = new URLSearchParams({108 lat: lat.toFixed(6),109 lng: lng.toFixed(6),110 halfLat: halfLat.toFixed(6),111 halfLng: halfLng.toFixed(6),112 limit: "1500",113 });114 const res = await fetch(`/api/nearby?${params}`, { signal: query.signal });115 if (!res.ok) throw new Error(`Carte : API ${res.status}`);116 const data = (await res.json()) as { results: NearbyUnit[] };117 return { properties: data.results.map(toMapProperty) };118 },119};120