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:
@@ -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)} triệu`;
|
||||
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">Quản lý tin đăng</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Quan ly, theo doi va cap nhat cac tin dang cua ban
|
||||
Quản lý, theo dõi và cập nhật các tin đăng của bạn
|
||||
</p>
|
||||
</div>
|
||||
<Link href="/listings/new">
|
||||
<Button>Dang tin moi</Button>
|
||||
<Button>Đăng tin mới</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>Tổng 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 hoạt độ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ờ duyệt</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>Tổng 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="">Tất cả giao dịch</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="">Tất cả loại 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="">Tất cả trạng 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
|
||||
Bảng
|
||||
</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">Loại</th>
|
||||
<th className="p-3 font-medium text-right">Giá</th>
|
||||
<th className="p-3 font-medium text-right">Diện tích</th>
|
||||
<th className="p-3 font-medium text-center">Trạng 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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user