'use client'; import { Loader2, MapPin } from 'lucide-react'; import * as React from 'react'; import { POI_ICONS, POI_LABELS, poiApi, type NearbyPoiResult, type PoiCategory, } from '@/lib/poi-api'; interface Props { /** Centre coordinates of the asset (listing / project / KCN). */ lat: number; lng: number; /** Search radius in metres. Default 1500m (~15 phút đi bộ). */ radius?: number; /** Restrict to these categories. Default: 6 most relevant for residential. */ categories?: PoiCategory[]; /** N nearest POI shown per category. */ limitPerCategory?: number; className?: string; } const DEFAULT_CATEGORIES: PoiCategory[] = [ 'SCHOOL_PRIMARY', 'SCHOOL_SECONDARY', 'HOSPITAL', 'MARKET', 'BANK', 'METRO_STATION', ]; function formatDistance(m: number): string { if (m < 1000) return `${m} m`; return `${(m / 1000).toFixed(1)} km`; } /** * Sidebar widget that lists the nearest POI of each category around a * geo-tagged asset. Renders inside listing detail, project detail and KCN * detail pages. */ export function NearbyPoiSidebar({ lat, lng, radius = 1500, categories = DEFAULT_CATEGORIES, limitPerCategory = 3, className, }: Props) { const [data, setData] = React.useState(null); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(null); React.useEffect(() => { let cancelled = false; setLoading(true); setError(null); poiApi .nearby({ lat, lng, radius, categories, limitPerCategory }) .then((res) => { if (cancelled) return; setData(res); }) .catch((err: Error) => { if (cancelled) return; setError(err.message ?? 'Không tải được tiện ích'); }) .finally(() => { if (cancelled) return; setLoading(false); }); return () => { cancelled = true; }; }, [lat, lng, radius, categories, limitPerCategory]); if (loading) { return (
Đang tải tiện ích xung quanh…
); } if (error) { return (
{error}
); } if (!data || data.all.length === 0) { return (
Chưa có dữ liệu tiện ích trong bán kính {formatDistance(radius)}.
); } return (

Tiện ích xung quanh

{data.meta.totalCount} điểm · bán kính {formatDistance(data.meta.radiusMeters)}
{categories.map((cat) => { const items = data.byCategory[cat] ?? []; if (items.length === 0) return null; return (
{POI_ICONS[cat]} {POI_LABELS[cat]}
    {items.map((p) => (
  • {p.name}
    {p.address && (
    {p.address}
    )}
    {formatDistance(p.distanceM)}
  • ))}
); })}
); }