/** * recoSearch.js * * BUT: Optionnel. * - Si tu veux que le frontend fasse une "suggestion/recherche" via Rico, * FAIS-LE via le core-api (BFF), pas direct vers Rico. * * IMPORTANT: * - Si tu n'importes pas ce fichier, rien ne change dans ton UI. * - Ton search bar actuelle filtre déjà localement (0 call réseau). */ const DEFAULT_PATHS = [ "/api/suggestions", // NEW: Autocomplete suggestions (cities, posts, profiles) "/api/unified-search", // Unified search (posts + profiles + places) "/api/search", // Fallback: Posts only "/api/reco/search", "/api/reco/suggest", "/api/recommendations/search", ]; /** * Normalise un item venant du core-api. * On reste permissif: Rico / core-api peut renvoyer plusieurs shapes. */ function normalizeOne(r) { if (!r || typeof r !== "object") return null; const title = (r.title ?? r.name ?? r.label ?? r.display_name ?? r.disp ?? r.text ?? "") .toString() .trim(); // coords possibles (support multiple formats) const lat = typeof r.lat === "number" ? r.lat : (typeof r.latitude === "number" ? r.latitude : null); const lon = typeof r.lon === "number" ? r.lon : (typeof r.lng === "number" ? r.lng : null); const coords = Array.isArray(r.coords) && r.coords.length === 2 ? r.coords : (lat != null && lon != null ? [lon, lat] : null); const type = (r.type ?? r.kind ?? r.entity ?? "").toString().toLowerCase().trim(); const kind = type || (coords ? "place" : "item"); const tags = Array.isArray(r.event_tags) ? r.event_tags : Array.isArray(r.tags) ? r.tags : []; return { id: (r.id ?? r._id ?? r.key ?? title).toString(), type: kind, kind: kind, // Keep both for compatibility title, subtitle: (r.subtitle ?? r.sub ?? r.extra ?? r.category ?? "").toString(), coords, lat, lon, tags, raw: r, }; } /** * Tente une recherche sur une liste de paths (au cas où le endpoint exact diffère). * Retourne [] sur 404/erreur. */ async function tryFetchAnyPath(q, { signal } = {}) { const query = (q || "").toString().trim(); if (!query) return []; // Permet override runtime si tu veux: // window.__SW_RECO_SEARCH_PATH = "/api/reco/search" const override = typeof window !== "undefined" && window.__SW_RECO_SEARCH_PATH ? String(window.__SW_RECO_SEARCH_PATH) : ""; const paths = override ? [override, ...DEFAULT_PATHS] : DEFAULT_PATHS; for (const p of paths) { try { const url = `${p}?q=${encodeURIComponent(query)}`; console.log('[recoSearch] Trying:', url); const res = await fetch(url, { credentials: "include", signal }); console.log('[recoSearch] Response status:', res.status); if (res.status === 404) { console.log('[recoSearch] 404, trying next path'); continue; } if (!res.ok) { console.log('[recoSearch] Not OK, returning empty'); return []; // évite spam erreurs } const data = await res.json().catch(() => null); const arr = Array.isArray(data) ? data : Array.isArray(data?.items) ? data.items : Array.isArray(data?.results) ? data.results : []; console.log('[recoSearch] Success! Returning', arr.length, 'results from', p); return arr.map(normalizeOne).filter(Boolean); } catch (e) { // Abort => stop net if (e && (e.name === "AbortError" || e.code === 20)) { console.log('[recoSearch] Aborted'); return []; } // autres erreurs => on teste prochain path console.log('[recoSearch] Error on', p, ':', e.message, '- trying next'); continue; } } return []; } /** * recoSearch(query): renvoie une liste normalisée. * - "safe" : retourne [] si rien marche. */ export async function recoSearch(query, opts = {}) { return tryFetchAnyPath(query, opts); }