import { useState } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { useRoute, useLocation } from "wouter"; 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 { Badge } from "@/components/ui/badge"; import { useToast } from "@/hooks/use-toast"; import { queryClient, apiRequest } from "@/lib/queryClient"; import { useAuth } from "@/hooks/useAuth"; import { Clock, Gavel, TrendingUp, User } from "lucide-react"; import type { MediaOutlet, Auction, Bid } from "@shared/schema"; export default function MediaOutletAuction() { const [, params] = useRoute("/media/:slug/auction"); const [, setLocation] = useLocation(); const [bidAmount, setBidAmount] = useState(""); const [qualityScore, setQualityScore] = useState(""); const { user, isAuthenticated } = useAuth(); const { toast } = useToast(); const { data: outlet, isLoading: outletLoading } = useQuery({ queryKey: ["/api/media-outlets", params?.slug], enabled: !!params?.slug }); const { data: auction, isLoading: auctionLoading } = useQuery({ queryKey: ["/api/media-outlets", params?.slug, "auction"], enabled: !!params?.slug }); const placeBidMutation = useMutation({ mutationFn: async (bidData: { amount: number; qualityScore?: number }) => { return apiRequest(`/api/media-outlets/${params?.slug}/auction/bids`, { method: "POST", body: JSON.stringify(bidData), }); }, onSuccess: () => { toast({ title: "입찰 성공", description: "입찰이 성공적으로 등록되었습니다.", }); setBidAmount(""); setQualityScore(""); queryClient.invalidateQueries({ queryKey: ["/api/media-outlets", params?.slug, "auction"] }); }, onError: (error: any) => { toast({ title: "입찰 실패", description: error.message || "입찰 중 오류가 발생했습니다.", variant: "destructive", }); }, }); const handlePlaceBid = () => { if (!bidAmount) { toast({ title: "입찰 금액 필요", description: "입찰 금액을 입력해주세요.", variant: "destructive", }); return; } const amount = parseFloat(bidAmount); if (isNaN(amount) || amount <= 0) { toast({ title: "유효하지 않은 금액", description: "올바른 입찰 금액을 입력해주세요.", variant: "destructive", }); return; } if (auction && amount <= auction.currentBid) { toast({ title: "입찰 금액 부족", description: `현재 최고 입찰가(${auction.currentBid}원)보다 높은 금액을 입력해주세요.`, variant: "destructive", }); return; } const bidData: { amount: number; qualityScore?: number } = { amount }; if (qualityScore) { const score = parseFloat(qualityScore); if (!isNaN(score) && score >= 0 && score <= 100) { bidData.qualityScore = score; } } placeBidMutation.mutate(bidData); }; const formatCurrency = (amount: number) => { return new Intl.NumberFormat('ko-KR', { style: 'currency', currency: 'KRW' }).format(amount); }; const formatTimeRemaining = (endDate: string) => { const end = new Date(endDate); const now = new Date(); const diff = end.getTime() - now.getTime(); if (diff <= 0) return "경매 종료"; const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); if (days > 0) return `${days}일 ${hours}시간 남음`; if (hours > 0) return `${hours}시간 ${minutes}분 남음`; return `${minutes}분 남음`; }; if (outletLoading || auctionLoading) { return (
); } if (!outlet) { return (

언론매체를 찾을 수 없습니다

요청하신 언론매체가 존재하지 않습니다.

); } if (!auction) { return (
S
SAPIENS

진행 중인 경매가 없습니다

{outlet.name}의 관리자 권한에 대한 경매가 현재 진행되고 있지 않습니다.

); } return (
{/* Header */}
S
SAPIENS
{/* Outlet Header */}
{outlet.imageUrl ? ( {outlet.name} ) : (
{outlet.name.charAt(0)}
)}

{outlet.name} 관리자 권한 경매

{outlet.description}

{outlet.category} {outlet.tags?.map((tag) => ( {tag} ))}
{/* Auction Information */} 경매 정보
현재 최고가
{formatCurrency(auction.currentBid)}
남은 시간
{formatTimeRemaining(auction.endDate)}
{auction.highestBidder && (
최고 입찰자
{auction.highestBidder.slice(0, 3)}***
)} {auction.qualityScore && (
품질 점수
{auction.qualityScore}/100
)}
{/* Bidding Form */} 입찰하기 {!isAuthenticated ? (

입찰하려면 로그인이 필요합니다.

) : (
setBidAmount(e.target.value)} placeholder={`${auction.currentBid + 1000} 이상`} min={auction.currentBid + 1} data-testid="input-bid-amount" />
setQualityScore(e.target.value)} placeholder="품질 점수를 입력하세요" min={0} max={100} data-testid="input-quality-score" />

* 입찰이 성공하면 취소할 수 없습니다.

)}
{/* Auction Description */} 경매 안내

이 경매는 {outlet.name}의 관리자 권한을 획득하기 위한 경매입니다. 관리자 권한을 획득하면 다음과 같은 기능을 사용할 수 있습니다:

  • 언론매체의 기사 관리 및 편집
  • 예측시장 이벤트 생성 및 관리
  • 언론매체 프로필 정보 수정
  • 구독자 및 팔로워 관리

경매는 {formatTimeRemaining(auction.endDate)} 후에 종료됩니다. 최고 입찰자가 관리자 권한을 획득하게 됩니다.

); }