import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Heart, MapPin, Eye, Star, MessageSquare, Share2, Flag, Pencil } from 'lucide-react'; import { Card } from '../components/ui/Card'; import { GradientButton } from '../components/ui/GradientButton'; import { Button } from '../components/ui/Button'; import { Badge } from '../components/ui/Badge'; import { Avatar } from '../components/ui/Avatar'; import { Modal } from '../components/ui/Modal'; import { Input } from '../components/ui/Input'; import { api } from '../api/client'; import { useAuth } from '../context/AuthContext'; import { formatCurrency, formatDate } from '../utils/format'; import type { Listing } from '../types'; export function ProductDetailPage() { const { id } = useParams(); const navigate = useNavigate(); const { user, isAuthenticated } = useAuth(); const [listing, setListing] = useState(null); const [loading, setLoading] = useState(true); const [isFav, setIsFav] = useState(false); const [showOffer, setShowOffer] = useState(false); const [showEdit, setShowEdit] = useState(false); const [offerAmount, setOfferAmount] = useState(''); const [offerMessage, setOfferMessage] = useState(''); const [offerError, setOfferError] = useState(''); // Edit form state const [editTitle, setEditTitle] = useState(''); const [editPrice, setEditPrice] = useState(''); const [editCondition, setEditCondition] = useState(''); const [editDescription, setEditDescription] = useState(''); const [editSaving, setEditSaving] = useState(false); const [editError, setEditError] = useState(''); useEffect(() => { if (!id) return; api.get(`/listings/${id}`) .then(data => { setListing(data); setIsFav(data.isFavorited ?? false); }) .catch(() => setListing(null)) .finally(() => setLoading(false)); }, [id]); const handleFavorite = async () => { if (!listing || !isAuthenticated) return; try { const res = await api.post<{ isFavorited: boolean }>(`/listings/${listing.id}/favorite`); setIsFav(res.isFavorited); } catch {} }; const handleSendOffer = async () => { if (!listing || !offerAmount) return; setOfferError(''); try { await api.post('/offers', { amount: parseFloat(offerAmount), message: offerMessage || undefined, listingId: listing.id, }); setShowOffer(false); setOfferAmount(''); setOfferMessage(''); } catch (err: unknown) { setOfferError(err instanceof Error ? err.message : 'Failed to send offer'); } }; const handleOpenEdit = () => { if (!listing) return; setEditTitle(listing.title); setEditPrice(String(listing.price)); setEditCondition(listing.condition); setEditDescription(listing.description); setEditError(''); setShowEdit(true); }; const handleSaveEdit = async () => { if (!listing) return; setEditSaving(true); setEditError(''); try { const updated = await api.put(`/listings/${listing.id}`, { title: editTitle, price: parseFloat(editPrice), condition: editCondition, description: editDescription, }); setListing(updated); setShowEdit(false); } catch (err) { setEditError(err instanceof Error ? err.message : 'Failed to save changes'); } finally { setEditSaving(false); } }; const handleDelete = async () => { if (!listing) return; if (!confirm('Are you sure you want to delete this listing?')) return; try { await api.delete(`/listings/${listing.id}`); navigate('/'); } catch {} }; const handleMessage = async () => { if (!listing || !isAuthenticated) return; try { const conversation = await api.post<{ id: string }>('/chat/conversations', { recipientId: listing.seller.id, listingId: listing.id, }); navigate('/dashboard/messages', { state: { conversationId: conversation.id } }); } catch {} }; if (loading) return
Loading...
; if (!listing) return
Listing not found
; const isOwner = user?.id === listing.sellerId; const conditionVariant = listing.condition === 'NEW' ? 'success' : listing.condition === 'LIKE_NEW' ? 'info' : 'default'; const hasImages = listing.images && listing.images.length > 0; const categoryEmoji = listing.category === 'FURNITURE' ? '\uD83E\uDE91' : listing.category === 'ELECTRONICS' ? '\uD83C\uDFA7' : listing.category === 'CLOTHING' ? '\uD83D\uDC55' : listing.category === 'HOME_GARDEN' ? '\u2615' : '\uD83D\uDCE6'; return (
{/* Images */}
{hasImages ? ( {listing.title} ) : ( {categoryEmoji} )}
{(hasImages ? listing.images.slice(0, 4) : [0, 1, 2, 3]).map((img, i) => (
{typeof img === 'object' && 'url' in img ? ( ) : ( {categoryEmoji} )}
))}
{/* Details */}

{listing.title}

{listing.condition.replace('_', ' ')} {listing.viewCount} views
{isOwner && ( )}

{formatCurrency(listing.price)} {listing.obo && or best offer}

{!isOwner && (
setShowOffer(true)}> Make Offer
)}

{listing.seller.fullName}

{listing.seller.rating !== undefined && ( <> {listing.seller.rating} )} Joined {formatDate(listing.seller.createdAt)}

Item Description

{listing.description}

Location

{listing.location}

{/* Make Offer Modal */} setShowOffer(false)} title="Make Offer" size="sm">

Enter your offer amount and message to the seller

{offerError &&

{offerError}

}
setOfferAmount(e.target.value)} />