// UQO Éval — laboratoire pédagogique d'évaluation immobilière // lib/kamaps.ts : thème UQO Éval Maps (framework Ka Maps) + adaptateur /api/nearby. // La carte affiche des VALEURS ESTIMÉES (jamais des prix demandés) : // pastilles encre à liseré turquoise UQO, sélection bleu UQO — le langage // visuel du site (encre #16181b, bleu #006085, turquoise #2bccae). import type { BoundsQuery, BoundsQueryResult, KaAppSource, KaDataAdapter, KaMapTheme, MapProperty, } from "@groupe-ka/ka-maps"; // Le framework ka-maps fige l'union KaAppSource à ses apps d'origine ; // « uqo-eval » n'y figure pas — l'id ne sert qu'au thème/URL, déjà fournis ici. const APP_SOURCE = "uqo-eval" as KaAppSource; const INK = "#16181b"; const RED = "#2bccae"; // turquoise UQO (liseré/halo) const BORDEAUX = "#00465f"; // bleu UQO profond (grappes) export const MAPBOX_TOKEN: string = process.env.NEXT_PUBLIC_MAPBOX_TOKEN ?? "pk.eyJ1Ijoic3Bib3VjaGVyIiwiYSI6ImNtc3Fyb3k4djAwOTgyenB3dWt6NHBjc2kifQ.poqLf0ADy3lIh28O-pFI2Q"; /** Zoom minimal de chargement : sous ce niveau, on invite à zoomer. */ export const MIN_ZOOM_FETCH = 12; export const TYPE_FR: Record = { unifamilial: "Unifamiliale", condo_ou_multi: "Condo / multi", plex: "Plex", chalet: "Chalet", terrain: "Terrain", maison_mobile: "Maison mobile", autre: "Autre", }; export const uqoEvalMapTheme: KaMapTheme = { id: "uqo-eval", productName: "UQO Éval Maps", accent: "#006085", // bleu UQO onAccent: "#ffffff", fontFamily: "var(--font-inter), system-ui, sans-serif", markers: { sale: { background: INK, text: "#ffffff", halo: RED, selectedBackground: "#006085", selectedText: "#ffffff", }, // Valeurs estimées : pastille encre, liseré turquoise — visuellement // distinctes d'un prix demandé (jamais présentées comme une annonce). valuation: { background: INK, text: "#ffffff", halo: RED, selectedBackground: "#006085", selectedText: "#ffffff", }, }, cluster: { background: BORDEAUX, text: "#ffffff", border: "rgba(43,204,174,0.5)", }, supportsDark: false, }; interface NearbyUnit { id: string; adresse: string | null; apt: string | null; municipalite: string | null; typeProp: string | null; lat: number; lng: number; est2026: number | null; } function toMapProperty(u: NearbyUnit): MapProperty { return { id: u.id, appSource: APP_SOURCE, latitude: u.lat, longitude: u.lng, kind: "valuation", estimatedValue: u.est2026 ?? undefined, propertyType: u.typeProp ? TYPE_FR[u.typeProp] ?? u.typeProp : undefined, address: u.apt ? `${u.adresse} app. ${u.apt}` : u.adresse ?? undefined, city: u.municipalite ?? undefined, originalUrl: `/estimation/${u.id}`, extra: { typeProp: u.typeProp }, }; } /** Adaptateur viewport→données : /api/nearby (contrat partagé avec iOS). */ export const uqoEvalMapAdapter: KaDataAdapter = { id: "uqo-eval-nearby", appSource: APP_SOURCE, async fetchInBounds(query: BoundsQuery): Promise { if (query.zoom < MIN_ZOOM_FETCH) return { properties: [] }; const { bbox } = query; const lat = (bbox.north + bbox.south) / 2; const lng = (bbox.east + bbox.west) / 2; const halfLat = Math.abs(bbox.north - lat); const halfLng = Math.abs(bbox.east - lng); const params = new URLSearchParams({ lat: lat.toFixed(6), lng: lng.toFixed(6), halfLat: halfLat.toFixed(6), halfLng: halfLng.toFixed(6), limit: "1500", }); const res = await fetch(`/api/nearby?${params}`, { signal: query.signal }); if (!res.ok) throw new Error(`Carte : API ${res.status}`); const data = (await res.json()) as { results: NearbyUnit[] }; return { properties: data.results.map(toMapProperty) }; }, };