Updated events page to display all user bets.

This commit is contained in:
2026-01-10 14:01:21 -06:00
parent accd4487b0
commit 3cf9e594e9
14 changed files with 283 additions and 27 deletions

View File

@ -0,0 +1,198 @@
import { useState } from 'react'
import { Link } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import { useAuthStore } from '@/store'
import { spreadBetsApi } from '@/api/spread-bets'
import type { SpreadBetDetail } from '@/types/spread-bet'
import { SpreadBetStatus } from '@/types/spread-bet'
import { ExternalLink, Trophy, XCircle } from 'lucide-react'
interface MyOtherBetsProps {
currentEventId?: number
}
const STATUS_STYLES: Record<SpreadBetStatus, string> = {
[SpreadBetStatus.OPEN]: 'bg-green-100 text-green-700',
[SpreadBetStatus.MATCHED]: 'bg-yellow-100 text-yellow-700',
[SpreadBetStatus.COMPLETED]: 'bg-blue-100 text-blue-700',
[SpreadBetStatus.CANCELLED]: 'bg-gray-100 text-gray-700',
[SpreadBetStatus.DISPUTED]: 'bg-red-100 text-red-700',
}
function formatGameTime(gameTime: string): string {
const date = new Date(gameTime)
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
})
}
type TabType = 'active' | 'history'
export const MyOtherBets = ({ currentEventId }: MyOtherBetsProps) => {
const { isAuthenticated, user } = useAuthStore()
const [activeTab, setActiveTab] = useState<TabType>('active')
const { data: activeBets = [], isLoading: isLoadingActive } = useQuery({
queryKey: ['my-active-bets'],
queryFn: () => spreadBetsApi.getMyActiveBets(),
enabled: isAuthenticated,
})
const { data: historyBets = [], isLoading: isLoadingHistory } = useQuery({
queryKey: ['my-bet-history'],
queryFn: () => spreadBetsApi.getMyBetHistory(),
enabled: isAuthenticated && activeTab === 'history',
})
if (!isAuthenticated) {
return null
}
const isLoading = activeTab === 'active' ? isLoadingActive : isLoadingHistory
const bets = activeTab === 'active' ? activeBets : historyBets
const renderBetRow = (bet: SpreadBetDetail) => {
const isCurrentEvent = bet.event_id === currentEventId
const isWinner = bet.winner_id === user?.id
const isLoser = bet.status === SpreadBetStatus.COMPLETED && bet.winner_id && bet.winner_id !== user?.id
return (
<tr
key={bet.id}
className={`hover:bg-gray-50 transition-colors ${isCurrentEvent ? 'bg-blue-50' : ''}`}
>
<td className="px-4 py-3 text-sm">
<Link
to={`/events/${bet.event_id}`}
className="font-medium text-gray-900 hover:text-blue-600"
>
{bet.event_home_team} vs {bet.event_away_team}
</Link>
<div className="text-xs text-gray-500">{formatGameTime(bet.event_game_time)}</div>
</td>
<td className="px-4 py-3 text-sm">
<span className={`font-medium ${bet.team === 'home' ? 'text-green-600' : 'text-red-600'}`}>
{bet.team === 'home' ? bet.event_home_team : bet.event_away_team}
</span>
</td>
<td className="px-4 py-3 text-sm font-medium text-gray-900 text-center">
{bet.spread > 0 ? '+' : ''}{bet.spread}
</td>
<td className="px-4 py-3 text-sm font-medium text-gray-900 text-right">
${Number(bet.stake_amount).toLocaleString()}
</td>
<td className="px-4 py-3 text-sm text-center">
<div className="flex items-center justify-center gap-1">
{isCurrentEvent && (
<span className="text-xs px-2 py-0.5 rounded-full font-medium bg-blue-100 text-blue-700">
Current
</span>
)}
<span className={`text-xs px-2 py-0.5 rounded-full font-medium ${STATUS_STYLES[bet.status]}`}>
{bet.status}
</span>
</div>
</td>
<td className="px-4 py-3 text-sm text-right">
{bet.status === SpreadBetStatus.COMPLETED ? (
<div className="flex items-center justify-end gap-1">
{isWinner ? (
<>
<span className="font-medium text-green-600">
+${Number(bet.payout_amount || 0).toLocaleString()}
</span>
<Trophy size={14} className="text-yellow-500" />
</>
) : isLoser ? (
<>
<span className="font-medium text-red-600">
-${Number(bet.stake_amount).toLocaleString()}
</span>
<XCircle size={14} className="text-red-500" />
</>
) : (
<span className="text-gray-400">-</span>
)}
</div>
) : (
<span className="text-gray-400">-</span>
)}
</td>
</tr>
)
}
return (
<div className="bg-white rounded-xl shadow-sm border p-6">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-bold text-gray-900">My Bets</h2>
<Link
to="/my-bets"
className="text-sm text-primary hover:underline flex items-center gap-1"
>
View All <ExternalLink size={14} />
</Link>
</div>
{/* Tabs */}
<div className="flex gap-2 mb-4 border-b">
<button
onClick={() => setActiveTab('active')}
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'active'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
Active ({activeBets.length})
</button>
<button
onClick={() => setActiveTab('history')}
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'history'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
History {historyBets.length > 0 && `(${historyBets.length})`}
</button>
</div>
{/* Content */}
{isLoading ? (
<div className="space-y-2">
{[...Array(4)].map((_, i) => (
<div key={i} className="h-12 bg-gray-100 rounded animate-pulse" />
))}
</div>
) : bets.length === 0 ? (
<p className="text-gray-500 text-center py-8">
{activeTab === 'active'
? "You don't have any active bets."
: "No bet history yet."}
</p>
) : (
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-4 py-3">Event</th>
<th className="px-4 py-3">Team</th>
<th className="px-4 py-3 text-center">Spread</th>
<th className="px-4 py-3 text-right">Stake</th>
<th className="px-4 py-3 text-center">Status</th>
<th className="px-4 py-3 text-right">Result</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100">
{bets.map(renderBetRow)}
</tbody>
</table>
</div>
)}
</div>
)
}