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
) : (
| Quan |
Loai BDS |
Gia trung vi |
Gia/m2 |
Tin dang |
Ngay ban |
YoY |
{districtStats.map((stat, i) => (
| {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
))}
)}
);
}