'use client'; import dynamic from 'next/dynamic'; import Image from 'next/image'; import Link from 'next/link'; import { ListingStatusBadge } from '@/components/listings/listing-status-badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { useMarketReport, useHeatmap } from '@/lib/hooks/use-analytics'; import { useListingsSearch } from '@/lib/hooks/use-listings'; const DistrictBarChart = dynamic( () => import('@/components/charts/district-bar-chart').then((mod) => mod.DistrictBarChart), { ssr: false, loading: () =>
Đang tải biểu đồ...
}, ); const CITY = 'Ho Chi Minh'; const 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)} tỷ`; if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(0)} triệu`; return num.toLocaleString('vi-VN'); } function formatPriceM2(price: number): string { if (price >= 1_000_000) return `${(price / 1_000_000).toFixed(1)} tr/m²`; return `${price.toLocaleString('vi-VN')} đ/m²`; } interface StatCardProps { title: string; value: string; description?: string; trend?: number | null; } function StatCard({ title, value, description, trend }: StatCardProps) { return ( {title} {value} {(description || trend != null) && (
{trend != null && ( = 0 ? 'text-green-600' : 'text-red-600'}`} > {trend >= 0 ? '+' : ''} {trend.toFixed(1)}% )} {description && ( {description} )}
)}
); } export default function DashboardPage() { const { data: reportData, isLoading: reportLoading } = useMarketReport(CITY, PERIOD); const { data: heatmapData, isLoading: heatmapLoading } = useHeatmap(CITY, PERIOD); const { data: listings, isLoading: listingsLoading } = useListingsSearch({ page: 1, limit: 6 }); const loading = reportLoading || heatmapLoading || listingsLoading; const marketReport = reportData?.districts ?? []; const heatmap = heatmapData?.dataPoints ?? []; const totalListings = marketReport.reduce((sum, d) => sum + d.totalListings, 0); const avgPriceM2 = marketReport.length > 0 ? marketReport.reduce((sum, d) => sum + d.avgPriceM2, 0) / marketReport.length : 0; const avgDaysOnMarket = marketReport.length > 0 ? marketReport.reduce((sum, d) => sum + d.daysOnMarket, 0) / marketReport.length : 0; const avgYoy = marketReport.filter((d) => d.yoyChange != null).length > 0 ? marketReport .filter((d) => d.yoyChange != null) .reduce((sum, d) => sum + (d.yoyChange ?? 0), 0) / marketReport.filter((d) => d.yoyChange != null).length : null; const myListingsCount = listings?.total ?? 0; const totalViews = listings?.data.reduce((s, l) => s + l.viewCount, 0) ?? 0; const totalInquiries = listings?.data.reduce((s, l) => s + l.inquiryCount, 0) ?? 0; const chartData = heatmap .sort((a, b) => b.avgPriceM2 - a.avgPriceM2) .slice(0, 8) .map((p) => ({ district: p.district.replace(/^Quan\s*/i, 'Q.').replace(/^Huyen\s*/i, 'H.'), 'Gia/m2': Math.round(p.avgPriceM2 / 1_000_000), listings: p.totalListings, })); return (

Bảng điều khiển

Tổng quan thị trường và tin đăng của bạn

{/* Stats overview */}
{/* Market overview + quick stats */}
{/* Price chart */} Giá trung bình theo quận {CITY} - {PERIOD} (triệu VND/m²) {loading ? (
Đang tải...
) : chartData.length === 0 ? (
Chưa có dữ liệu
) : ( [`${value} tr/m²`, 'Giá']} /> )}
{/* Market summary */} Thị trường {CITY} Chỉ số chính - {PERIOD}
Tổng tin đăng {loading ? '...' : totalListings.toLocaleString('vi-VN')}
Giá TB/m² {loading ? '...' : formatPriceM2(avgPriceM2)}
Ngày TB để bán {loading ? '...' : `${avgDaysOnMarket.toFixed(0)} ngày`}
Số quận {loading ? '...' : new Set(marketReport.map((d) => d.district)).size}
{/* Recent listings */}
Tin đăng gần đây Danh sách tin đăng mới nhất của bạn
{loading ? (
Đang tải...
) : !listings || listings.data.length === 0 ? (

Chưa có tin đăng nào

) : (
{listings.data.slice(0, 5).map((listing) => (
{listing.property.media.length > 0 ? ( {listing.property.title} ) : (
N/A
)}

{listing.property.title}

{listing.property.district}, {listing.property.city}

{formatPrice(listing.priceVND)}

{listing.viewCount} lượt xem {listing.inquiryCount} liên hệ
))}
)}
); }