/** * PoiList2Gis — top-7 POI list with weighted score. * Category icons: Lucide Train / TreePine / GraduationCap / Baby / * ShoppingBag / Hospital / Banknote * Per row: icon + name + distance + weight badge. */ import { Train, TreePine, GraduationCap, Baby, ShoppingBag, Hospital, Banknote, MapPin, } from "lucide-react"; import type { ReactNode } from "react"; import { Badge } from "@/components/ui/Badge"; import type { PoiScoreItem } from "@/lib/site-finder-api"; // ── Icon mapping ────────────────────────────────────────────────────────────── const CATEGORY_ICONS: Record = { metro_stop: , tram_stop: , bus_stop: , park: , school: , kindergarten: , shop_mall: , shop_supermarket: , shop_small: , hospital: , pharmacy: , bank: , }; const CATEGORY_LABELS: Record = { metro_stop: "Метро", tram_stop: "Трамвай", bus_stop: "Автобус", park: "Парк", school: "Школа", kindergarten: "Детский сад", shop_mall: "ТЦ", shop_supermarket: "Супермаркет", shop_small: "Магазин", hospital: "Больница", pharmacy: "Аптека", bank: "Банк", }; function weightBadgeVariant( weight: number, ): "success" | "info" | "neutral" | "warning" { if (weight >= 0.2) return "success"; if (weight >= 0.12) return "info"; if (weight >= 0.08) return "neutral"; return "warning"; } // ── Props ───────────────────────────────────────────────────────────────────── interface Props { items: PoiScoreItem[]; totalScore: number; } // ── Component ───────────────────────────────────────────────────────────────── export function PoiList2Gis({ items, totalScore }: Props) { // POI-weighted score is presented as «X / 100», so clamp to 0..100 defensively: // the adapter sums round(weight*100) per POI без нормировки и при достаточном // числе POI может выдать >100 → бессмысленное «137 / 100» (#1470). Корневую // нормировку нужно чинить в site-finder-api.ts (useParcelPoiScoreQuery). const displayScore = Math.min(100, Math.max(0, totalScore)); // Top-7, sorted by score_contribution desc const top7 = [...items] .sort((a, b) => b.score_contribution - a.score_contribution) .slice(0, 7); if (top7.length === 0) { return (
POI данные недоступны
); } return (
{/* Header */}
POI · 2ГИС / OSM {displayScore.toFixed(0)} / 100
{/* List */}
    {top7.map((item, i) => { const icon = CATEGORY_ICONS[item.category] ?? ( ); const categoryLabel = CATEGORY_LABELS[item.category] ?? item.category; const isLast = i === top7.length - 1; return (
  • {/* Icon */} {icon} {/* Name + category */}
    {item.name}
    {categoryLabel}
    {/* Distance */} {item.distance_m < 1000 ? `${Math.round(item.distance_m)} м` : `${(item.distance_m / 1000).toFixed(1)} км`} {/* Weight badge */} ×{item.weight.toFixed(2)}
  • ); })}
); }