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 { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { TrendingUp, TrendingDown, DollarSign, Clock } from "lucide-react"; import { useToast } from "@/hooks/use-toast"; import { apiRequest, queryClient } from "@/lib/queryClient"; import type { Article, PredictionMarket } from "@shared/schema"; export default function Article() { const [, params] = useRoute("/articles/:slug"); const [, setLocation] = useLocation(); const { toast } = useToast(); const [betAmounts, setBetAmounts] = useState>({}); const { data: article, isLoading: articleLoading } = useQuery
({ queryKey: ["/api/articles", params?.slug], enabled: !!params?.slug }); const { data: markets = [], isLoading: marketsLoading } = useQuery({ queryKey: ["/api/articles", params?.slug, "markets"], enabled: !!params?.slug }); const placeBetMutation = useMutation({ mutationFn: async ({ marketId, side, amount }: { marketId: string; side: "yes" | "no"; amount: number }) => { return apiRequest(`/api/prediction-markets/${marketId}/bets`, { method: "POST", body: { side, amount } }); }, onSuccess: () => { toast({ title: "베팅 성공", description: "예측시장 베팅이 성공적으로 완료되었습니다." }); queryClient.invalidateQueries({ queryKey: ["/api/articles", params?.slug, "markets"] }); setBetAmounts({}); }, onError: (error: any) => { toast({ title: "베팅 실패", description: error.message || "베팅 처리 중 오류가 발생했습니다.", variant: "destructive" }); } }); const formatCurrency = (amount: string | number) => { return new Intl.NumberFormat('ko-KR', { style: 'currency', currency: 'KRW' }).format(Number(amount)); }; const formatPercentage = (value: number) => { return `${(value * 100).toFixed(1)}%`; }; const formatDate = (dateString: string) => { return new Intl.DateTimeFormat('ko-KR', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }).format(new Date(dateString)); }; const handleBetAmountChange = (marketId: string, value: string) => { setBetAmounts(prev => ({ ...prev, [marketId]: value })); }; const handlePlaceBet = (marketId: string, side: "yes" | "no") => { const amount = parseFloat(betAmounts[marketId] || "0"); if (amount <= 0) { toast({ title: "잘못된 금액", description: "베팅 금액을 올바르게 입력해주세요.", variant: "destructive" }); return; } placeBetMutation.mutate({ marketId, side, amount }); }; const parseArticleContent = (content: string) => { const sectionRegex = /##SECTION##(.+?)##/g; const parts: { type: 'section' | 'text'; content: string }[] = []; let lastIndex = 0; let match; while ((match = sectionRegex.exec(content)) !== null) { if (match.index > lastIndex) { parts.push({ type: 'text', content: content.slice(lastIndex, match.index) }); } parts.push({ type: 'section', content: match[1] }); lastIndex = match.index + match[0].length; } if (lastIndex < content.length) { parts.push({ type: 'text', content: content.slice(lastIndex) }); } return parts; }; if (articleLoading) { return (
SAPIENS setLocation("/")} />
); } if (!article) { return (

기사를 찾을 수 없습니다

); } return (
{/* Header */}
SAPIENS setLocation("/")} />
{/* Article Content */}

{article.title}

{article.excerpt && (

{article.excerpt}

)}
{parseArticleContent(article.content).map((part, index) => { if (part.type === 'section') { return (

{part.content}

); } return (

{part.content}

); })}
{/* Prediction Markets Section */}

관련 예측시장

{marketsLoading ? (
{Array.from({ length: 3 }).map((_, i) => (
))}
) : markets.length > 0 ? (
{markets.map((market) => ( {market.question} {formatDate(market.resolutionDate)}
{/* Yes Option */}
YES
현재 가격
{formatPercentage(market.yesPrice)}
handleBetAmountChange(market.id, e.target.value)} className="border-green-300 focus:border-green-500" data-testid={`input-bet-amount-${market.id}`} />
{/* No Option */}
NO
현재 가격
{formatPercentage(market.noPrice)}
handleBetAmountChange(market.id, e.target.value)} className="border-red-300 focus:border-red-500" data-testid={`input-bet-amount-no-${market.id}`} />
{/* Market Stats */}
{formatCurrency(market.totalVolume)}
총 거래량
{market.totalBets}
총 베팅 수
{formatDate(market.resolutionDate)}
결과 발표일
))}
) : (

관련 예측시장이 없습니다

이 기사와 관련된 예측시장이 아직 생성되지 않았습니다.

)}
); }