'use client'; import { Building2, Calendar, Download, Expand, FileText, Grid3X3, Home, MapPin, Phone, X, } from 'lucide-react'; import dynamic from 'next/dynamic'; import Image from 'next/image'; import * as React from 'react'; import { ProjectAiAdviceCard } from '@/components/du-an/project-ai-advice-card'; import { ImageGallery } from '@/components/listings/image-gallery'; import type { POICategory, POIItem } from '@/components/neighborhood'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { analyticsApi, type NearbyPOI } from '@/lib/analytics-api'; import { formatPrice } from '@/lib/currency'; import { PROJECT_PROPERTY_TYPE_LABELS, PROJECT_STATUS_COLORS, PROJECT_STATUS_LABELS, duAnApi, type ProjectDetail, } from '@/lib/du-an-api'; import { listingsApi, type NeighborhoodScoreResult } from '@/lib/listings-api'; import { composeWhyThisProject, deriveProjectPersonas, type ProjectPersona, } from '@/lib/project-personas'; import { cn } from '@/lib/utils'; function mapScoreToCategories(result: NeighborhoodScoreResult) { return [ { category: 'education', label: 'Giáo dục', score: result.educationScore }, { category: 'healthcare', label: 'Y tế', score: result.healthcareScore }, { category: 'transport', label: 'Giao thông', score: result.transportScore }, { category: 'shopping', label: 'Mua sắm', score: result.shoppingScore }, { category: 'environment', label: 'Môi trường', score: result.greeneryScore }, { category: 'safety', label: 'An ninh', score: result.safetyScore }, ]; } const PriceTrendChart = dynamic( () => import('@/components/charts/price-trend-chart').then((m) => m.PriceTrendChart), { ssr: false }, ); const NeighborhoodRadarChart = dynamic( () => import('@/components/neighborhood').then((m) => m.NeighborhoodRadarChart), { ssr: false }, ); const NeighborhoodPOIMap = dynamic( () => import('@/components/neighborhood').then((m) => m.NeighborhoodPOIMap), { ssr: false }, ); type Tab = 'amenities' | 'location' | 'price' | 'listings' | 'documents'; const TABS: { key: Tab; label: string }[] = [ { key: 'amenities', label: 'Tiện ích' }, { key: 'location', label: 'Vị trí' }, { key: 'price', label: 'Giá' }, { key: 'listings', label: 'Tin đăng' }, { key: 'documents', label: 'Tài liệu' }, ]; interface DuAnDetailClientProps { project: ProjectDetail; } export function DuAnDetailClient({ project }: DuAnDetailClientProps) { const [activeTab, setActiveTab] = React.useState('amenities'); const [inquiryForm, setInquiryForm] = React.useState({ name: '', phone: '', message: '', }); const [inquiryState, setInquiryState] = React.useState< 'idle' | 'loading' | 'success' | 'error' >('idle'); // Live enrichments — fetched from analytics endpoints. Both degrade // gracefully: if either endpoint fails, we fall back to the // admin-entered `project.pois` / `project.neighborhoodScores` payload. const [liveScore, setLiveScore] = React.useState(null); const [livePois, setLivePois] = React.useState(null); React.useEffect(() => { if (!project.district || !project.city) return; listingsApi .getNeighborhoodScore(project.district, project.city) .then(setLiveScore) .catch(() => {/* silent — LocationTab falls back to admin payload */}); }, [project.district, project.city]); React.useEffect(() => { const { latitude, longitude } = project; if (latitude == null || longitude == null) return; analyticsApi .getNearbyPOIs(latitude, longitude) .then((res) => { const mapped: POIItem[] = res.pois.map((p: NearbyPOI) => ({ id: p.id, name: p.name, category: p.category, lat: p.lat, lng: p.lng, distance: p.distance, })); setLivePois(mapped); }) .catch(() => {/* silent — map still renders without POIs */}); }, [project.latitude, project.longitude]); const statusLabel = PROJECT_STATUS_LABELS[project.status]; const statusColor = PROJECT_STATUS_COLORS[project.status]; const handleInquirySubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!inquiryForm.name.trim() || !inquiryForm.phone.trim()) return; setInquiryState('loading'); try { await duAnApi.submitInquiry(project.id, { name: inquiryForm.name.trim(), phone: inquiryForm.phone.trim(), message: inquiryForm.message.trim(), }); setInquiryState('success'); } catch { setInquiryState('error'); } }; return (
{/* Header */}
{statusLabel} {project.propertyTypes.map((t) => ( {PROJECT_PROPERTY_TYPE_LABELS[t]} ))}

{project.name}

{project.address}, {project.district}, {project.city} {project.developer.name} {project.completionDate && ( Bàn giao: {new Date(project.completionDate).toLocaleDateString('vi-VN')} )}
{/* Gallery */} m.type === 'image') .map((m) => ({ id: m.id, type: 'image' as const, url: m.url, order: m.order, caption: m.caption }))} /> {/* Quick stats */}
} label="Tổng diện tích" value={`${project.totalArea.toLocaleString('vi-VN')} m²`} /> } label="Số căn" value={`${project.totalUnits}`} /> } label="Số block" value={`${project.blocks.length}`} />
{/* Persona fit — admin chips (CĐT chọn) merged with derived personas */} {/* AI advisor card — on-demand Claude call for summary + pros/cons + personas */}
({ category: p.category })), ).map((d) => d.label), ]} />
{/* "Vì sao nên chọn dự án này" — admin narrative (preferred) or derived */}
{/* Main content */}
{/* Description */} Tổng quan dự án

{project.description}

{/* Blocks */} {project.blocks.length > 0 && ( Phân khu / Block
{project.blocks.map((block) => (

{block.name}

{block.totalUnits} căn {block.availableUnits} còn trống {block.floors} tầng
))}
)} {/* Master plan */} {/* Tabs */}
{TABS.map((tab) => ( ))}
{activeTab === 'amenities' && } {activeTab === 'location' && ( )} {activeTab === 'price' && } {activeTab === 'listings' && } {activeTab === 'documents' && }
{/* Sidebar */}
{/* Developer card */} Chủ đầu tư {project.developer.logoUrl ? ( {project.developer.name} ) : (
)}

{project.developer.name}

{project.developer.totalProjects} dự án

{/* Inquiry form */} Nhận tư vấn {inquiryState === 'success' ? (

Đã gửi thành công!

Chúng tôi sẽ liên hệ bạn sớm nhất.

) : (
{inquiryState === 'error' && (

Gửi thất bại. Vui lòng thử lại.

)}
setInquiryForm((f) => ({ ...f, name: e.target.value })) } required disabled={inquiryState === 'loading'} />
setInquiryForm((f) => ({ ...f, phone: e.target.value })) } required disabled={inquiryState === 'loading'} />