"use client"; import { useEffect, useState } from "react"; interface YandexMapProps { addresses: string[]; height?: number; } function cleanAddress(addr: string): string { return addr .replace(/^г\.\s*/i, "") .replace(/ул\.\s*/gi, "") .replace(/пр-т\.?\s*/gi, "") .replace(/пр\.\s*/gi, "") .replace(/просп\.\s*/gi, "") .replace(/,?\s*к\d+/gi, "") .replace(/,+/g, " ") .replace(/\s+/g, " ") .trim(); } export function YandexMap({ addresses, height = 380 }: YandexMapProps) { const [mapSrc, setMapSrc] = useState(null); useEffect(() => { if (!addresses.length) return; let cancelled = false; async function build() { // Geocode all addresses in parallel const results = await Promise.allSettled( addresses.map(async (addr) => { const cleaned = cleanAddress(addr); const query = cleaned.toLowerCase().includes("минск") ? cleaned : `Минск ${cleaned}`; const res = await fetch( `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&limit=1&countrycodes=by`, { signal: AbortSignal.timeout(5000) } ); const data = await res.json(); if (data.length > 0) { return { lat: parseFloat(data[0].lat), lon: parseFloat(data[0].lon) }; } return null; }) ); const points = results .filter((r): r is PromiseFulfilledResult<{ lat: number; lon: number } | null> => r.status === "fulfilled") .map((r) => r.value) .filter((p): p is { lat: number; lon: number } => p !== null); if (cancelled || points.length === 0) return; const centerLat = points.reduce((s, p) => s + p.lat, 0) / points.length; const centerLon = points.reduce((s, p) => s + p.lon, 0) / points.length; const zoom = points.length === 1 ? 15 : 12; const pts = points.map((p) => `${p.lon},${p.lat},pm2ntl`).join("~"); setMapSrc(`https://yandex.ru/map-widget/v1/?ll=${centerLon},${centerLat}&z=${zoom}&pt=${pts}&l=map&theme=dark`); } build(); return () => { cancelled = true; }; }, [addresses]); if (!addresses.length) return null; if (!mapSrc) { return (
Загрузка карты...
); } return (