'use client';
import dynamic from 'next/dynamic';
import Link from 'next/link';
import * as React from 'react';
import { ImageGallery } from '@/components/listings/image-gallery';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AiEstimateButton } from '@/components/valuation/ai-estimate-button';
import type { ListingDetail } from '@/lib/listings-api';
import { PROPERTY_TYPES, DIRECTIONS, TRANSACTION_TYPES } from '@/lib/validations/listings';
const ListingMap = dynamic(
() => import('@/components/map/listing-map').then((mod) => mod.ListingMap),
{
ssr: false,
loading: () => (
{'\u0110ang t\u1ea3i b\u1ea3n \u0111\u1ed3...'}
),
},
);
function formatPrice(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');
}
function getLabel(list: readonly { value: string; label: string }[], value: string | null) {
if (!value) return null;
return list.find((item) => item.value === value)?.label ?? value;
}
interface ListingDetailClientProps {
listing: ListingDetail;
}
export function ListingDetailClient({ listing }: ListingDetailClientProps) {
const { property, seller, agent } = listing;
const transactionLabel = getLabel(TRANSACTION_TYPES, listing.transactionType);
const propertyTypeLabel = getLabel(PROPERTY_TYPES, property.propertyType);
return (
{/* Breadcrumb */}
{/* Header */}
{transactionLabel && (
{transactionLabel}
)}
{propertyTypeLabel && {propertyTypeLabel}}
{property.title}
{property.address}, {property.ward}, {property.district}, {property.city}
{formatPrice(listing.priceVND)} VND
{listing.pricePerM2 != null && (
~{listing.pricePerM2.toLocaleString('vi-VN')} VND/m²
)}
{listing.rentPriceMonthly && (
Thu\u00ea: {formatPrice(listing.rentPriceMonthly)}/th\u00e1ng
)}
{/* Image gallery */}
{/* Quick specs bar */}
{property.bedrooms != null && (
)}
{property.bathrooms != null && (
)}
{property.floors != null && (
)}
{property.direction && (
)}
{/* Main content */}
{/* Description */}
Mô tả
{property.description}
{/* Details */}
Thông tin chi tiết
{/* Amenities */}
{property.amenities && property.amenities.length > 0 && (
Tiện ích
{property.amenities.map((a) => (
{a}
))}
)}
{/* Map */}
Vị trí trên bản đồ
{/* Sidebar */}
{/* Contact card */}
Liên hệ
{seller.fullName}
{seller.phone}
{agent && (
Môi giới
{agent.agency &&
{agent.agency}
}
{listing.commissionPct != null && (
Hoa hồng: {listing.commissionPct}%
)}
)}
{/* AI Estimate */}
{/* Stats */}
{listing.viewCount}
Lượt xem
{listing.saveCount}
Lượt lưu
{listing.inquiryCount}
Liên hệ
{listing.publishedAt && (
Đăng ngày {new Date(listing.publishedAt).toLocaleDateString('vi-VN')}
)}
);
}
function QuickStat({ icon, label, value }: { icon: string; label: string; value: string }) {
const icons: Record = {
area: (
),
bed: (
),
bath: (
),
floors: (
),
compass: (
),
};
return (
);
}
function InfoItem({ label, value }: { label: string; value: string }) {
return (
);
}