feat(web): centralise Vietnamese price formatting across all pages
Create a single `currency.ts` utility with `formatPrice`, `formatVND`, `formatPricePerM2`, and `parseVND` to replace 9+ duplicated inline formatters. This fixes inconsistent decimal handling (1.5M was truncated to "1 triệu") and standardises price/m² display. Integrated across property cards, listing detail, dashboard, analytics, payments, pricing, and admin moderation pages with 19 new unit tests. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
generateBreadcrumbJsonLd,
|
||||
generateListingJsonLd,
|
||||
} from '@/components/seo/json-ld';
|
||||
import { formatPrice } from '@/lib/currency';
|
||||
import { fetchListingById } from '@/lib/listings-server';
|
||||
import { PROPERTY_TYPES, TRANSACTION_TYPES } from '@/lib/validations/listings';
|
||||
|
||||
@@ -20,13 +21,6 @@ function getLabel(list: readonly { value: string; label: string }[], value: stri
|
||||
return list.find((item) => item.value === value)?.label ?? value;
|
||||
}
|
||||
|
||||
function formatPriceShort(priceVND: string): string {
|
||||
const num = Number(priceVND);
|
||||
if (num >= 1_000_000_000) return `${(num / 1_000_000_000).toFixed(1)} t\u1ef7`;
|
||||
if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(0)} tri\u1ec7u`;
|
||||
return num.toLocaleString('vi-VN');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata (runs server-side, provides <title>, <meta>, OG, canonical)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -44,7 +38,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
|
||||
const { property } = listing;
|
||||
const propertyTypeLabel = getLabel(PROPERTY_TYPES, property.propertyType);
|
||||
const transactionLabel = getLabel(TRANSACTION_TYPES, listing.transactionType);
|
||||
const priceStr = formatPriceShort(listing.priceVND);
|
||||
const priceStr = formatPrice(listing.priceVND);
|
||||
const fullAddress = [property.address, property.ward, property.district, property.city]
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
|
||||
Reference in New Issue
Block a user