'use client'; import Image from 'next/image'; import Link from 'next/link'; import * as React from 'react'; import { ListingStatusBadge } from '@/components/listings/listing-status-badge'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Select } from '@/components/ui/select'; import { useListingsSearch } from '@/lib/hooks/use-listings'; import { type ListingDetail } from '@/lib/listings-api'; import { PROPERTY_TYPES, TRANSACTION_TYPES, LISTING_STATUSES } from '@/lib/validations/listings'; function formatPrice(priceVND: string): string { const num = Number(priceVND); if (num >= 1_000_000_000) return `${(num / 1_000_000_000).toFixed(1)} tỷ`; if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(0)} triệu`; return num.toLocaleString('vi-VN'); } function formatDate(dateStr: string | null): string { if (!dateStr) return 'N/A'; return new Date(dateStr).toLocaleDateString('vi-VN', { day: '2-digit', month: '2-digit', year: 'numeric', }); } type ViewMode = 'grid' | 'table'; export default function ListingsPage() { const [viewMode, setViewMode] = React.useState('grid'); const [filters, setFilters] = React.useState({ transactionType: '', propertyType: '', status: '' as string, page: 1, }); const searchParams = React.useMemo(() => { const params: Record = { page: filters.page, limit: 12 }; if (filters.transactionType) params['transactionType'] = filters.transactionType; if (filters.propertyType) params['propertyType'] = filters.propertyType; if (filters.status) params['status'] = filters.status; return params; }, [filters]); const { data: result, isLoading: loading } = useListingsSearch(searchParams); // Stats from current page data const stats = React.useMemo(() => { if (!result) return { total: 0, active: 0, pending: 0, views: 0 }; return { total: result.total, active: result.data.filter((l) => l.status === 'ACTIVE').length, pending: result.data.filter((l) => l.status === 'PENDING_REVIEW').length, views: result.data.reduce((s, l) => s + l.viewCount, 0), }; }, [result]); return (
{/* Header */}

Quản lý tin đăng

Quản lý, theo dõi và cập nhật các tin đăng của bạn

{/* Stats */}
Tổng tin đăng {loading ? '...' : stats.total} Đang hoạt động {loading ? '...' : stats.active} Chờ duyệt {loading ? '...' : stats.pending} Tổng lượt xem {loading ? '...' : stats.views}
{/* Filters + View Toggle */}
{/* Content */} {loading ? (
) : !result || result.data.length === 0 ? (

Chưa có tin đăng nào

) : viewMode === 'grid' ? ( /* Grid View */
{result.data.map((listing) => (
{listing.property.media.length > 0 ? ( {listing.property.title} ) : (
Chưa có ảnh
)}

{formatPrice(listing.priceVND)} VND

{listing.property.title}

{listing.property.district}, {listing.property.city}

{listing.property.areaM2} m² {listing.property.bedrooms != null && ( {listing.property.bedrooms} PN )} {listing.property.bathrooms != null && listing.property.bathrooms > 0 && ( {listing.property.bathrooms} PT )}
{listing.viewCount} lượt xem {listing.inquiryCount} liên hệ {listing.saveCount} đã lưu
))}
) : ( /* Table View */
{result.data.map((listing) => ( ))}
Tin đăng Loại Giá Diện tích Trạng thái Lượt xem Liên hệ Ngày đăng
{listing.property.media.length > 0 ? ( {listing.property.title} ) : (
N/A
)}

{listing.property.title}

{listing.property.district}, {listing.property.city}

{listing.property.propertyType} {formatPrice(listing.priceVND)} {listing.property.areaM2} m² {listing.viewCount} {listing.inquiryCount} {formatDate(listing.publishedAt ?? listing.createdAt)}
)} {/* Pagination */} {result && result.totalPages > 1 && (
Trang {result.page} / {result.totalPages}
)}
); }