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>
Complete public pricing page showing all 4 subscription plans (FREE,
AGENT_PRO, INVESTOR, ENTERPRISE) with billing cycle toggle, feature
comparison table, VND formatting, and Vietnamese/English i18n support.
Also adds pricing link to public navigation header and footer.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add comprehensive SEO support for property listing pages to improve
organic search visibility and social sharing.
Changes:
- Convert listing detail page from client-only to server component wrapper
with generateMetadata() for per-listing title, description, OG tags,
canonical URLs, and hreflang alternates
- Add JSON-LD structured data (Schema.org RealEstateListing) with price,
location, property specs, and breadcrumb markup
- Add Website JSON-LD with SearchAction to root layout
- Upgrade sitemap.xml to dynamically include all active listings across
both locales (vi, en) with ISR revalidation
- Improve robots.txt with pagination/sort exclusions and GPTBot block
- Create server-side fetch utility (listings-server.ts) for SSR data
- Extract client UI into ListingDetailClient component
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add locale-prefixed routes for admin, auth, dashboard, and public pages.
Add error, loading, and not-found pages for locale context. Add language
switcher UI component for Vietnamese/English toggle.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Remove duplicate root-level route groups ((public)/, (auth)/, (dashboard)/,
(admin)/, auth/) that shadowed the [locale]/ i18n-aware versions. All routes
now live exclusively under [locale]/ with next-intl middleware handling locale
detection and redirect.
- Root layout.tsx → pass-through (delegates html/body to [locale]/layout.tsx)
- [locale]/layout.tsx now imports globals.css
- Root error.tsx, not-found.tsx get html wrapper for safety fallback
- Remove redundant root loading.tsx
- 38 duplicate route files removed
Co-Authored-By: Paperclip <noreply@paperclip.ing>