'use client'; import { useEffect, useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { analyticsApi, type MarketReportDistrict, type HeatmapDataPoint, type DistrictStats, } from '@/lib/analytics-api'; const CITIES = ['Ho Chi Minh', 'Ha Noi', 'Da Nang']; const CURRENT_PERIOD = '2026-Q1'; function formatPrice(priceStr: string): string { const num = Number(priceStr); if (num >= 1_000_000_000) return `${(num / 1_000_000_000).toFixed(1)} ty`; if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(0)} trieu`; return num.toLocaleString('vi-VN'); } function formatPriceM2(price: number): string { if (price >= 1_000_000) return `${(price / 1_000_000).toFixed(1)} tr/m2`; return `${price.toLocaleString('vi-VN')} d/m2`; } function YoYBadge({ value }: { value: number | null }) { if (value === null) return N/A; const isPositive = value >= 0; return ( {isPositive ? '+' : ''}{value.toFixed(1)}% ); } export default function AnalyticsPage() { const [city, setCity] = useState(CITIES[0]); const [period] = useState(CURRENT_PERIOD); const [marketReport, setMarketReport] = useState([]); const [heatmap, setHeatmap] = useState([]); const [districtStats, setDistrictStats] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { setLoading(true); setError(null); Promise.all([ analyticsApi.getMarketReport(city, period).catch(() => ({ districts: [] as MarketReportDistrict[] })), analyticsApi.getHeatmap(city, period).catch(() => ({ dataPoints: [] as HeatmapDataPoint[] })), analyticsApi.getDistrictStats(city, period).catch(() => ({ districts: [] as DistrictStats[] })), ]) .then(([report, heatmapData, stats]) => { setMarketReport(report.districts); setHeatmap(heatmapData.dataPoints); setDistrictStats(stats.districts); }) .catch(() => setError('Khong the tai du lieu phan tich')) .finally(() => setLoading(false)); }, [city, period]); const totalListings = marketReport.reduce((sum, d) => sum + d.totalListings, 0); const avgDaysOnMarket = marketReport.length > 0 ? marketReport.reduce((sum, d) => sum + d.daysOnMarket, 0) / marketReport.length : 0; const avgPriceM2 = marketReport.length > 0 ? marketReport.reduce((sum, d) => sum + d.avgPriceM2, 0) / marketReport.length : 0; return (

Phan tich thi truong

Bao cao thi truong bat dong san - {period}

{CITIES.map((c) => ( ))}
{error && (
{error}
)} {/* Summary Cards */}
Tong tin dang {loading ? '...' : totalListings.toLocaleString('vi-VN')} Gia TB/m2 {loading ? '...' : formatPriceM2(avgPriceM2)} Ngay trung binh de ban {loading ? '...' : `${avgDaysOnMarket.toFixed(0)} ngay`} So quan/huyen {loading ? '...' : new Set(marketReport.map(d => d.district)).size}
{/* Heatmap - Price by District */} Ban do gia theo quan So sanh gia trung binh/m2 giua cac quan tai {city} {loading ? (
Dang tai...
) : heatmap.length === 0 ? (
Chua co du lieu
) : (
{heatmap .sort((a, b) => b.avgPriceM2 - a.avgPriceM2) .map((point) => { const maxPrice = heatmap[0] ? Math.max(...heatmap.map(h => h.avgPriceM2)) : 1; const intensity = Math.round((point.avgPriceM2 / maxPrice) * 100); return (
{point.district}
{formatPriceM2(point.avgPriceM2)}
{point.totalListings} tin dang
); })}
)}
{/* District Stats Table */} Thong ke chi tiet theo quan Du lieu thi truong bat dong san tai {city} - {period} {loading ? (
Dang tai...
) : districtStats.length === 0 ? (
Chua co du lieu
) : (
{districtStats.map((stat, i) => ( ))}
Quan Loai BDS Gia trung vi Gia/m2 Tin dang Ngay ban YoY
{stat.district} {stat.propertyType} {formatPrice(stat.medianPrice)} {formatPriceM2(stat.avgPriceM2)} {stat.totalListings} {stat.daysOnMarket.toFixed(0)}
)}
{/* Market Report Summary */} Bao cao thi truong Tong hop chi so thi truong theo tung quan {loading ? (
Dang tai...
) : marketReport.length === 0 ? (
Chua co du lieu
) : (
{[...new Map(marketReport.map(d => [d.district, d])).values()].map((district) => (

{district.district}

Gia trung vi {formatPrice(district.medianPrice)}
Gia/m2 {formatPriceM2(district.avgPriceM2)}
Tin dang {district.totalListings}
Ton kho {district.inventoryLevel}
Thay doi YoY
))}
)}
); }