diff --git a/src/services/recoSearch.js b/src/services/recoSearch.js new file mode 100644 index 0000000..a14a603 --- /dev/null +++ b/src/services/recoSearch.js @@ -0,0 +1,105 @@ +/** + * 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/reco/search", + "/api/reco/suggest", + "/api/search", + "/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 + 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(); + + return { + id: (r.id ?? r._id ?? r.key ?? title).toString(), + type: type || (coords ? "place" : "item"), + title, + subtitle: (r.subtitle ?? r.sub ?? r.extra ?? r.category ?? "").toString(), + coords, + 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)}`; + const res = await fetch(url, { credentials: "include", signal }); + + if (res.status === 404) continue; + if (!res.ok) 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 + : []; + + return arr.map(normalizeOne).filter(Boolean); + } catch (e) { + // Abort => stop net + if (e && (e.name === "AbortError" || e.code === 20)) return []; + // autres erreurs => on teste prochain path + continue; + } + } + + return []; +} + +/** + * recoSearch(query): renvoie une liste normalisée. + * - "safe" : retourne [] si rien marche. + */ +export async function recoSearch(query, opts = {}) { + return tryFetchAnyPath(query, opts); +}