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:
@@ -17,7 +17,7 @@ const ListingMap = dynamic(
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="flex h-[300px] items-center justify-center rounded-lg bg-muted">
|
||||
<p className="text-sm text-muted-foreground">Dang tai ban do...</p>
|
||||
<p className="text-sm text-muted-foreground">Đang tải bản đồ...</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -45,7 +45,7 @@ export default function PublicListingDetailPage() {
|
||||
listingsApi
|
||||
.getById(id)
|
||||
.then(setListing)
|
||||
.catch((err) => setError(err instanceof Error ? err.message : 'Khong tai duoc tin dang'))
|
||||
.catch((err) => setError(err instanceof Error ? err.message : 'Không tải được tin đăng'))
|
||||
.finally(() => setLoading(false));
|
||||
}, [id]);
|
||||
|
||||
@@ -74,9 +74,9 @@ export default function PublicListingDetailPage() {
|
||||
<svg className="h-12 w-12 text-muted-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||
</svg>
|
||||
<p className="text-destructive">{error || 'Khong tim thay tin dang'}</p>
|
||||
<p className="text-destructive">{error || 'Không tìm thấy tin đăng'}</p>
|
||||
<Link href="/search">
|
||||
<Button variant="outline">Quay lai tim kiem</Button>
|
||||
<Button variant="outline">Quay lại tìm kiếm</Button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
@@ -90,9 +90,9 @@ export default function PublicListingDetailPage() {
|
||||
<div className="mx-auto max-w-6xl px-4 py-6">
|
||||
{/* Breadcrumb */}
|
||||
<nav className="mb-4 flex items-center gap-1.5 text-sm text-muted-foreground">
|
||||
<Link href="/" className="hover:text-foreground">Trang chu</Link>
|
||||
<Link href="/" className="hover:text-foreground">Trang chủ</Link>
|
||||
<span>/</span>
|
||||
<Link href="/search" className="hover:text-foreground">Tim kiem</Link>
|
||||
<Link href="/search" className="hover:text-foreground">Tìm kiếm</Link>
|
||||
<span>/</span>
|
||||
<span className="truncate text-foreground">{property.title}</span>
|
||||
</nav>
|
||||
@@ -121,12 +121,12 @@ export default function PublicListingDetailPage() {
|
||||
<p className="text-2xl font-bold text-primary md:text-3xl">{formatPrice(listing.priceVND)} VND</p>
|
||||
{listing.pricePerM2 != null && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
~{listing.pricePerM2.toLocaleString('vi-VN')} VND/m2
|
||||
~{listing.pricePerM2.toLocaleString('vi-VN')} VND/m²
|
||||
</p>
|
||||
)}
|
||||
{listing.rentPriceMonthly && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Thue: {formatPrice(listing.rentPriceMonthly)}/thang
|
||||
Thuê: {formatPrice(listing.rentPriceMonthly)}/tháng
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -137,18 +137,18 @@ export default function PublicListingDetailPage() {
|
||||
|
||||
{/* Quick specs bar */}
|
||||
<div className="my-6 flex flex-wrap gap-4 rounded-lg border bg-card p-4">
|
||||
<QuickStat icon="area" label="Dien tich" value={`${property.areaM2} m\u00B2`} />
|
||||
<QuickStat icon="area" label="Diện tích" value={`${property.areaM2} m\u00B2`} />
|
||||
{property.bedrooms != null && (
|
||||
<QuickStat icon="bed" label="Phong ngu" value={`${property.bedrooms}`} />
|
||||
<QuickStat icon="bed" label="Phòng ngủ" value={`${property.bedrooms}`} />
|
||||
)}
|
||||
{property.bathrooms != null && (
|
||||
<QuickStat icon="bath" label="Phong tam" value={`${property.bathrooms}`} />
|
||||
<QuickStat icon="bath" label="Phòng tắm" value={`${property.bathrooms}`} />
|
||||
)}
|
||||
{property.floors != null && (
|
||||
<QuickStat icon="floors" label="So tang" value={`${property.floors}`} />
|
||||
<QuickStat icon="floors" label="Số tầng" value={`${property.floors}`} />
|
||||
)}
|
||||
{property.direction && (
|
||||
<QuickStat icon="compass" label="Huong" value={getLabel(DIRECTIONS, property.direction) || ''} />
|
||||
<QuickStat icon="compass" label="Hướng" value={getLabel(DIRECTIONS, property.direction) || ''} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -158,7 +158,7 @@ export default function PublicListingDetailPage() {
|
||||
{/* Description */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Mo ta</CardTitle>
|
||||
<CardTitle>Mô tả</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="whitespace-pre-wrap text-sm leading-relaxed">{property.description}</p>
|
||||
@@ -168,19 +168,19 @@ export default function PublicListingDetailPage() {
|
||||
{/* Details */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Thong tin chi tiet</CardTitle>
|
||||
<CardTitle>Thông tin chi tiết</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3">
|
||||
<InfoItem label="Loai BDS" value={propertyTypeLabel || '---'} />
|
||||
<InfoItem label="Dien tich" value={`${property.areaM2} m\u00B2`} />
|
||||
<InfoItem label="Phong ngu" value={property.bedrooms != null ? `${property.bedrooms}` : '---'} />
|
||||
<InfoItem label="Phong tam" value={property.bathrooms != null ? `${property.bathrooms}` : '---'} />
|
||||
<InfoItem label="So tang" value={property.floors != null ? `${property.floors}` : '---'} />
|
||||
<InfoItem label="Huong" value={getLabel(DIRECTIONS, property.direction) || '---'} />
|
||||
<InfoItem label="Nam xay" value={property.yearBuilt ? `${property.yearBuilt}` : '---'} />
|
||||
<InfoItem label="Phap ly" value={property.legalStatus || '---'} />
|
||||
<InfoItem label="Du an" value={property.projectName || '---'} />
|
||||
<InfoItem label="Loại BĐS" value={propertyTypeLabel || '---'} />
|
||||
<InfoItem label="Diện tích" value={`${property.areaM2} m\u00B2`} />
|
||||
<InfoItem label="Phòng ngủ" value={property.bedrooms != null ? `${property.bedrooms}` : '---'} />
|
||||
<InfoItem label="Phòng tắm" value={property.bathrooms != null ? `${property.bathrooms}` : '---'} />
|
||||
<InfoItem label="Số tầng" value={property.floors != null ? `${property.floors}` : '---'} />
|
||||
<InfoItem label="Hướng" value={getLabel(DIRECTIONS, property.direction) || '---'} />
|
||||
<InfoItem label="Năm xây" value={property.yearBuilt ? `${property.yearBuilt}` : '---'} />
|
||||
<InfoItem label="Pháp lý" value={property.legalStatus || '---'} />
|
||||
<InfoItem label="Dự án" value={property.projectName || '---'} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -189,7 +189,7 @@ export default function PublicListingDetailPage() {
|
||||
{property.amenities && property.amenities.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Tien ich</CardTitle>
|
||||
<CardTitle>Tiện ích</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
@@ -206,7 +206,7 @@ export default function PublicListingDetailPage() {
|
||||
{/* Map */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Vi tri tren ban do</CardTitle>
|
||||
<CardTitle>Vị trí trên bản đồ</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ListingMap
|
||||
@@ -222,7 +222,7 @@ export default function PublicListingDetailPage() {
|
||||
{/* Contact card */}
|
||||
<Card className="sticky top-20">
|
||||
<CardHeader>
|
||||
<CardTitle>Lien he</CardTitle>
|
||||
<CardTitle>Liên hệ</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -242,22 +242,22 @@ export default function PublicListingDetailPage() {
|
||||
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||
</svg>
|
||||
Goi ngay
|
||||
Gọi ngay
|
||||
</Button>
|
||||
</a>
|
||||
<Button variant="outline" className="w-full gap-2">
|
||||
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
||||
</svg>
|
||||
Nhan tin
|
||||
Nhắn tin
|
||||
</Button>
|
||||
|
||||
{agent && (
|
||||
<div className="border-t pt-3">
|
||||
<p className="text-xs text-muted-foreground">Moi gioi</p>
|
||||
<p className="text-xs text-muted-foreground">Môi giới</p>
|
||||
{agent.agency && <p className="text-sm font-medium">{agent.agency}</p>}
|
||||
{listing.commissionPct != null && (
|
||||
<p className="text-xs text-muted-foreground">Hoa hong: {listing.commissionPct}%</p>
|
||||
<p className="text-xs text-muted-foreground">Hoa hồng: {listing.commissionPct}%</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -270,20 +270,20 @@ export default function PublicListingDetailPage() {
|
||||
<div className="grid grid-cols-3 gap-4 text-center">
|
||||
<div>
|
||||
<p className="text-lg font-bold">{listing.viewCount}</p>
|
||||
<p className="text-xs text-muted-foreground">Luot xem</p>
|
||||
<p className="text-xs text-muted-foreground">Lượt xem</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-lg font-bold">{listing.saveCount}</p>
|
||||
<p className="text-xs text-muted-foreground">Luot luu</p>
|
||||
<p className="text-xs text-muted-foreground">Lượt lưu</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-lg font-bold">{listing.inquiryCount}</p>
|
||||
<p className="text-xs text-muted-foreground">Lien he</p>
|
||||
<p className="text-xs text-muted-foreground">Liên hệ</p>
|
||||
</div>
|
||||
</div>
|
||||
{listing.publishedAt && (
|
||||
<p className="mt-3 border-t pt-3 text-center text-xs text-muted-foreground">
|
||||
Dang ngay {new Date(listing.publishedAt).toLocaleDateString('vi-VN')}
|
||||
Đăng ngày {new Date(listing.publishedAt).toLocaleDateString('vi-VN')}
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
|
||||
@@ -14,7 +14,7 @@ const ListingMap = dynamic(
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="flex h-[calc(100vh-220px)] items-center justify-center rounded-lg bg-muted">
|
||||
<p className="text-sm text-muted-foreground">Dang tai ban do...</p>
|
||||
<p className="text-sm text-muted-foreground">Đang tải bản đồ...</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user