Display bid history for media outlet auctions and fetch bid data

Add a new API endpoint and integrate bid history display into the media outlet auction page.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 069d4324-6c40-4355-955e-c714a50de1ea
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/069d4324-6c40-4355-955e-c714a50de1ea/6XTzcDL
This commit is contained in:
kimjaehyeon0101
2025-09-29 19:23:17 +00:00
parent d6682e32d9
commit 10d2fd7026
3 changed files with 107 additions and 0 deletions

View File

@ -30,6 +30,11 @@ export default function MediaOutletAuction() {
enabled: !!params?.slug
});
const { data: bids = [], isLoading: bidsLoading } = useQuery<Bid[]>({
queryKey: ["/api/auctions", auction?.id, "bids"],
enabled: !!auction?.id
});
const placeBidMutation = useMutation({
mutationFn: async (bidData: { amount: number; qualityScore?: number }) => {
return apiRequest("POST", `/api/media-outlets/${params?.slug}/auction/bids`, bidData);
@ -42,6 +47,7 @@ export default function MediaOutletAuction() {
setBidAmount("");
setQualityScore("");
queryClient.invalidateQueries({ queryKey: ["/api/media-outlets", params?.slug, "auction"] });
queryClient.invalidateQueries({ queryKey: ["/api/auctions", auction?.id, "bids"] });
},
onError: (error: any) => {
toast({
@ -363,6 +369,88 @@ export default function MediaOutletAuction() {
</Card>
</div>
{/* Bid History */}
<Card className="mt-8">
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<TrendingUp className="h-5 w-5" />
<span> </span>
<Badge variant="outline">{bids.length}</Badge>
</CardTitle>
</CardHeader>
<CardContent>
{bidsLoading ? (
<div className="space-y-4">
{Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="animate-pulse flex justify-between items-center p-3 border rounded">
<div className="h-4 bg-gray-300 rounded w-1/3"></div>
<div className="h-4 bg-gray-300 rounded w-1/4"></div>
</div>
))}
</div>
) : bids.length > 0 ? (
<div className="space-y-3 max-h-96 overflow-y-auto">
{(() => {
// Sort bids by time (newest first)
const sortedBids = [...bids].sort((a, b) =>
new Date(b.createdAt!).getTime() - new Date(a.createdAt!).getTime()
);
// Find highest bid by amount
const highestBid = bids.reduce((highest, current) =>
parseFloat(current.amount) > parseFloat(highest.amount) ? current : highest
, bids[0]);
return sortedBids.map((bid, index) => (
<div
key={bid.id}
className={`flex justify-between items-center p-4 border rounded-lg ${
index === 0 ? 'bg-green-50 border-green-200' : 'bg-gray-50 border-gray-200'
}`}
data-testid={`bid-item-${bid.id}`}
>
<div className="flex items-center space-x-4">
<div>
<div className="flex items-center space-x-2">
<span className="font-bold text-lg" data-testid={`bid-amount-${bid.id}`}>
{formatCurrency(parseFloat(bid.amount))}
</span>
{bid.id === highestBid.id && (
<Badge className="bg-green-600"></Badge>
)}
</div>
<div className="text-sm text-muted-foreground">
: <span data-testid={`bid-bidder-${bid.id}`}>{bid.bidderId.slice(0, 8)}***</span>
{bid.qualityScore !== undefined && bid.qualityScore !== null && (
<span className="ml-2">: {bid.qualityScore}</span>
)}
</div>
</div>
</div>
<div className="text-right text-sm text-muted-foreground">
<span data-testid={`bid-date-${bid.id}`}>
{new Date(bid.createdAt!).toLocaleDateString('ko-KR', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}
</span>
</div>
</div>
));
})()}
</div>
) : (
<div className="text-center py-8 text-muted-foreground">
<TrendingUp className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p> </p>
<p className="text-sm"> !</p>
</div>
)}
</CardContent>
</Card>
{/* Auction Description */}
<Card className="mt-8">
<CardHeader>