fix(web): add proper Vietnamese diacritics to all dashboard and listing pages

Vietnamese text throughout the frontend was missing accent marks (diacritics),
using plain ASCII instead of proper Unicode characters. Fixed all user-visible
text across dashboard, analytics, listings, search, and chart components.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 13:21:37 +07:00
parent 47c34f129e
commit 36c1e3b39a
7 changed files with 158 additions and 158 deletions

View File

@@ -16,7 +16,7 @@ import { listingsApi, type ListingDetail, type PaginatedResult } from '@/lib/lis
const DistrictBarChart = dynamic(
() => import('@/components/charts/district-bar-chart').then((mod) => mod.DistrictBarChart),
{ ssr: false, loading: () => <div className="flex h-64 items-center justify-center text-muted-foreground">Dang tai bieu do...</div> },
{ ssr: false, loading: () => <div className="flex h-64 items-center justify-center text-muted-foreground">Đang ti biu đ...</div> },
);
const CITY = 'Ho Chi Minh';
@@ -24,14 +24,14 @@ 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)} ty`;
if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(0)} trieu`;
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)} triu`;
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`;
if (price >= 1_000_000) return `${(price / 1_000_000).toFixed(1)} tr/m²`;
return `${price.toLocaleString('vi-VN')} đ/m²`;
}
interface StatCardProps {
@@ -124,35 +124,35 @@ export default function DashboardPage() {
<div className="space-y-8">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Bang dieu khien</h1>
<h1 className="text-3xl font-bold">Bng điều khin</h1>
<p className="mt-2 text-muted-foreground">
Tong quan thi truong va tin dang cua ban
Tng quan th trường và tin đăng ca bn
</p>
</div>
<Link href="/listings/new">
<Button>Dang tin moi</Button>
<Button>Đăng tin mi</Button>
</Link>
</div>
{/* Stats overview */}
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<StatCard
title="Tin dang cua toi"
title="Tin đăng ca tôi"
value={loading ? '...' : myListingsCount.toLocaleString('vi-VN')}
description="Tong so tin da dang"
description="Tng s tin đã đăng"
/>
<StatCard
title="Luot xem"
title="Lượt xem"
value={loading ? '...' : totalViews.toLocaleString('vi-VN')}
description="Tren tat ca tin dang"
description="Trên tt c tin đăng"
/>
<StatCard
title="Lien he"
title="Liên h"
value={loading ? '...' : totalInquiries.toLocaleString('vi-VN')}
description="Yeu cau tu khach hang"
description="Yêu cu t khách hàng"
/>
<StatCard
title="Gia TB thi truong"
title="Giá TB th trường"
value={loading ? '...' : formatPriceM2(avgPriceM2)}
trend={avgYoy}
description="YoY"
@@ -164,24 +164,24 @@ export default function DashboardPage() {
{/* Price chart */}
<Card className="lg:col-span-2">
<CardHeader>
<CardTitle className="text-lg">Gia trung binh theo quan</CardTitle>
<CardDescription>{CITY} - {PERIOD} (trieu VND/m2)</CardDescription>
<CardTitle className="text-lg">Giá trung bình theo qun</CardTitle>
<CardDescription>{CITY} - {PERIOD} (triu VND/m²)</CardDescription>
</CardHeader>
<CardContent>
{loading ? (
<div className="flex h-64 items-center justify-center text-muted-foreground">
Dang tai...
Đang ti...
</div>
) : chartData.length === 0 ? (
<div className="flex h-64 items-center justify-center text-muted-foreground">
Chua co du lieu
Chưa có d liu
</div>
) : (
<DistrictBarChart
data={chartData}
height={280}
dataKey="Gia/m2"
tooltipFormatter={(value) => [`${value} tr/m2`, 'Gia']}
tooltipFormatter={(value) => [`${value} tr/m²`, 'Giá']}
/>
)}
</CardContent>
@@ -190,30 +190,30 @@ export default function DashboardPage() {
{/* Market summary */}
<Card>
<CardHeader>
<CardTitle className="text-lg">Thi truong {CITY}</CardTitle>
<CardDescription>Chi so chinh - {PERIOD}</CardDescription>
<CardTitle className="text-lg">Th trường {CITY}</CardTitle>
<CardDescription>Ch s chính - {PERIOD}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Tong tin dang</span>
<span className="text-sm text-muted-foreground">Tng tin đăng</span>
<span className="font-semibold">
{loading ? '...' : totalListings.toLocaleString('vi-VN')}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Gia TB/m2</span>
<span className="text-sm text-muted-foreground">Giá TB/m²</span>
<span className="font-semibold">
{loading ? '...' : formatPriceM2(avgPriceM2)}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Ngay TB de ban</span>
<span className="text-sm text-muted-foreground">Ngày TB đ bán</span>
<span className="font-semibold">
{loading ? '...' : `${avgDaysOnMarket.toFixed(0)} ngay`}
{loading ? '...' : `${avgDaysOnMarket.toFixed(0)} ngày`}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">So quan</span>
<span className="text-sm text-muted-foreground">S qun</span>
<span className="font-semibold">
{loading ? '...' : new Set(marketReport.map((d) => d.district)).size}
</span>
@@ -221,7 +221,7 @@ export default function DashboardPage() {
<div className="pt-2">
<Link href="/analytics">
<Button variant="outline" size="sm" className="w-full">
Xem phan tich chi tiet
Xem phân tích chi tiết
</Button>
</Link>
</div>
@@ -233,26 +233,26 @@ export default function DashboardPage() {
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<div>
<CardTitle className="text-lg">Tin dang gan day</CardTitle>
<CardDescription>Danh sach tin dang moi nhat cua ban</CardDescription>
<CardTitle className="text-lg">Tin đăng gn đây</CardTitle>
<CardDescription>Danh sách tin đăng mi nht ca bn</CardDescription>
</div>
<Link href="/listings">
<Button variant="outline" size="sm">
Xem tat ca
Xem tt c
</Button>
</Link>
</CardHeader>
<CardContent>
{loading ? (
<div className="flex h-32 items-center justify-center text-muted-foreground">
Dang tai...
Đang ti...
</div>
) : !listings || listings.data.length === 0 ? (
<div className="flex h-32 flex-col items-center justify-center text-muted-foreground">
<p>Chua co tin dang nao</p>
<p>Chưa có tin đăng nào</p>
<Link href="/listings/new" className="mt-2">
<Button variant="outline" size="sm">
Dang tin dau tien
Đăng tin đu tiên
</Button>
</Link>
</div>
@@ -292,8 +292,8 @@ export default function DashboardPage() {
<ListingStatusBadge status={listing.status} />
</div>
<div className="hidden sm:flex sm:gap-3 sm:text-sm sm:text-muted-foreground">
<span>{listing.viewCount} luot xem</span>
<span>{listing.inquiryCount} lien he</span>
<span>{listing.viewCount} lượt xem</span>
<span>{listing.inquiryCount} liên h</span>
</div>
</Link>
))}