diff --git a/client/src/pages/MediaOutletAuction.tsx b/client/src/pages/MediaOutletAuction.tsx index f9249b4..405a45f 100644 --- a/client/src/pages/MediaOutletAuction.tsx +++ b/client/src/pages/MediaOutletAuction.tsx @@ -30,6 +30,11 @@ export default function MediaOutletAuction() { enabled: !!params?.slug }); + const { data: bids = [], isLoading: bidsLoading } = useQuery({ + 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() { + {/* Bid History */} + + + + + 입찰 내역 + {bids.length}건 + + + + {bidsLoading ? ( +
+ {Array.from({ length: 5 }).map((_, i) => ( +
+
+
+
+ ))} +
+ ) : bids.length > 0 ? ( +
+ {(() => { + // 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) => ( +
+
+
+
+ + {formatCurrency(parseFloat(bid.amount))} + + {bid.id === highestBid.id && ( + 최고가 + )} +
+
+ 입찰자: {bid.bidderId.slice(0, 8)}*** + {bid.qualityScore !== undefined && bid.qualityScore !== null && ( + 품질점수: {bid.qualityScore} + )} +
+
+
+
+ + {new Date(bid.createdAt!).toLocaleDateString('ko-KR', { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + })} + +
+
+ )); + })()} +
+ ) : ( +
+ +

아직 입찰 내역이 없습니다

+

첫 번째 입찰자가 되어보세요!

+
+ )} +
+
+ {/* Auction Description */} diff --git a/server/routes.ts b/server/routes.ts index c81e1aa..3368aa8 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -168,6 +168,16 @@ export async function registerRoutes(app: Express): Promise { } }); + app.get('/api/auctions/:id/bids', async (req, res) => { + try { + const bids = await storage.getBidsByAuctionId(req.params.id); + res.json(bids); + } catch (error) { + console.error("Error fetching bids:", error); + res.status(500).json({ message: "Failed to fetch bids" }); + } + }); + app.post('/api/auctions/:id/bid', isAuthenticated, async (req: any, res) => { try { const userId = req.user.claims.sub; diff --git a/server/storage.ts b/server/storage.ts index 5fe6834..d81e4a3 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -59,6 +59,7 @@ export interface IStorage { getAuctionByMediaOutlet(mediaOutletId: string): Promise; createAuction(auction: InsertAuction): Promise; placeBid(bid: InsertBid): Promise; + getBidsByAuctionId(auctionId: string): Promise; // Media outlet request operations getMediaOutletRequests(status?: string): Promise; @@ -251,6 +252,14 @@ export class DatabaseStorage implements IStorage { return newBid; } + + async getBidsByAuctionId(auctionId: string): Promise { + return await db + .select() + .from(bids) + .where(eq(bids.auctionId, auctionId)) + .orderBy(desc(bids.createdAt)); + } // Media outlet request operations async getMediaOutletRequests(status?: string): Promise {