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,8 +16,8 @@ import {
import { PROPERTY_TYPES, TRANSACTION_TYPES, LISTING_STATUSES } from '@/lib/validations/listings';
function formatPrice(priceVND: string): string {
const num = Number(priceVND);
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');
}
@@ -77,13 +77,13 @@ export default function ListingsPage() {
{/* Header */}
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 className="text-2xl font-bold">Quan ly tin dang</h1>
<h1 className="text-2xl font-bold">Qun lý tin đăng</h1>
<p className="text-sm text-muted-foreground">
Quan ly, theo doi va cap nhat cac tin dang cua ban
Qun lý, theo dõi và cp nht các tin đăng ca bn
</p>
</div>
<Link href="/listings/new">
<Button>Dang tin moi</Button>
<Button>Đăng tin mi</Button>
</Link>
</div>
@@ -91,13 +91,13 @@ export default function ListingsPage() {
<div className="grid gap-3 sm:grid-cols-4">
<Card>
<CardHeader className="pb-2">
<CardDescription>Tong tin dang</CardDescription>
<CardDescription>Tng tin đăng</CardDescription>
<CardTitle className="text-xl">{loading ? '...' : stats.total}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-2">
<CardDescription>Dang hoat dong</CardDescription>
<CardDescription>Đang hot đng</CardDescription>
<CardTitle className="text-xl text-green-600">
{loading ? '...' : stats.active}
</CardTitle>
@@ -105,7 +105,7 @@ export default function ListingsPage() {
</Card>
<Card>
<CardHeader className="pb-2">
<CardDescription>Cho duyet</CardDescription>
<CardDescription>Ch duyt</CardDescription>
<CardTitle className="text-xl text-yellow-600">
{loading ? '...' : stats.pending}
</CardTitle>
@@ -113,7 +113,7 @@ export default function ListingsPage() {
</Card>
<Card>
<CardHeader className="pb-2">
<CardDescription>Tong luot xem</CardDescription>
<CardDescription>Tng lượt xem</CardDescription>
<CardTitle className="text-xl">{loading ? '...' : stats.views}</CardTitle>
</CardHeader>
</Card>
@@ -128,7 +128,7 @@ export default function ListingsPage() {
}
className="w-40"
>
<option value="">Tat ca giao dich</option>
<option value="">Tt c giao dch</option>
{TRANSACTION_TYPES.map((t) => (
<option key={t.value} value={t.value}>
{t.label}
@@ -142,7 +142,7 @@ export default function ListingsPage() {
}
className="w-44"
>
<option value="">Tat ca loai BDS</option>
<option value="">Tt c loi BĐS</option>
{PROPERTY_TYPES.map((t) => (
<option key={t.value} value={t.value}>
{t.label}
@@ -154,7 +154,7 @@ export default function ListingsPage() {
onChange={(e) => setFilters((f) => ({ ...f, status: e.target.value, page: 1 }))}
className="w-40"
>
<option value="">Tat ca trang thai</option>
<option value="">Tt c trng thái</option>
{Object.entries(LISTING_STATUSES).map(([key, { label }]) => (
<option key={key} value={key}>
{label}
@@ -168,14 +168,14 @@ export default function ListingsPage() {
size="sm"
onClick={() => setViewMode('grid')}
>
Luoi
Lưới
</Button>
<Button
variant={viewMode === 'table' ? 'default' : 'outline'}
size="sm"
onClick={() => setViewMode('table')}
>
Bang
Bng
</Button>
</div>
</div>
@@ -187,10 +187,10 @@ export default function ListingsPage() {
</div>
) : !result || result.data.length === 0 ? (
<div className="flex min-h-[300px] 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>
@@ -228,7 +228,7 @@ export default function ListingsPage() {
</p>
<div className="mt-3 flex flex-wrap gap-1.5">
<Badge variant="secondary" className="text-xs">
{listing.property.areaM2} m2
{listing.property.areaM2} m²
</Badge>
{listing.property.bedrooms != null && (
<Badge variant="secondary" className="text-xs">
@@ -242,9 +242,9 @@ export default function ListingsPage() {
)}
</div>
<div className="mt-3 flex gap-3 text-xs text-muted-foreground">
<span>{listing.viewCount} luot xem</span>
<span>{listing.inquiryCount} lien he</span>
<span>{listing.saveCount} da luu</span>
<span>{listing.viewCount} lượt xem</span>
<span>{listing.inquiryCount} liên h</span>
<span>{listing.saveCount} đã lưu</span>
</div>
</CardContent>
</Card>
@@ -259,14 +259,14 @@ export default function ListingsPage() {
<table className="w-full text-sm">
<thead>
<tr className="border-b text-left">
<th className="p-3 font-medium">Tin dang</th>
<th className="p-3 font-medium">Loai</th>
<th className="p-3 font-medium text-right">Gia</th>
<th className="p-3 font-medium text-right">Dien tich</th>
<th className="p-3 font-medium text-center">Trang thai</th>
<th className="p-3 font-medium text-right">Luot xem</th>
<th className="p-3 font-medium text-right">Lien he</th>
<th className="p-3 font-medium text-right">Ngay dang</th>
<th className="p-3 font-medium">Tin đăng</th>
<th className="p-3 font-medium">Loi</th>
<th className="p-3 font-medium text-right">Giá</th>
<th className="p-3 font-medium text-right">Din tích</th>
<th className="p-3 font-medium text-center">Trng thái</th>
<th className="p-3 font-medium text-right">Lượt xem</th>
<th className="p-3 font-medium text-right">Liên h</th>
<th className="p-3 font-medium text-right">Ngày đăng</th>
</tr>
</thead>
<tbody>
@@ -311,7 +311,7 @@ export default function ListingsPage() {
<td className="p-3 text-right font-medium text-primary">
{formatPrice(listing.priceVND)}
</td>
<td className="p-3 text-right">{listing.property.areaM2} m2</td>
<td className="p-3 text-right">{listing.property.areaM2} m²</td>
<td className="p-3 text-center">
<ListingStatusBadge status={listing.status} />
</td>
@@ -338,7 +338,7 @@ export default function ListingsPage() {
disabled={filters.page <= 1}
onClick={() => setFilters((f) => ({ ...f, page: f.page - 1 }))}
>
Truoc
Trước
</Button>
<span className="text-sm text-muted-foreground">
Trang {result.page} / {result.totalPages}
@@ -349,7 +349,7 @@ export default function ListingsPage() {
disabled={filters.page >= result.totalPages}
onClick={() => setFilters((f) => ({ ...f, page: f.page + 1 }))}
>
Tiep
Tiếp
</Button>
</div>
)}