Event layout page update.
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useMemo, useRef } from 'react'
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useAuthStore } from '@/store'
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
@ -8,8 +8,6 @@ import { TeamSide } from '@/types/spread-bet'
|
||||
import {
|
||||
Clock,
|
||||
Activity,
|
||||
DollarSign,
|
||||
Users,
|
||||
Target,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
@ -43,6 +41,22 @@ function formatTimeUntil(gameTime: string): { text: string; urgent: boolean } {
|
||||
return { text: `${seconds}s`, urgent: true }
|
||||
}
|
||||
|
||||
// Helper to generate half-point spreads only (to prevent ties)
|
||||
function generateHalfPointSpreads(min: number, max: number): number[] {
|
||||
const spreads: number[] = []
|
||||
// Round min up to nearest .5
|
||||
let start = Math.ceil(min * 2) / 2
|
||||
if (start % 1 === 0) start += 0.5
|
||||
// Round max down to nearest .5
|
||||
let end = Math.floor(max * 2) / 2
|
||||
if (end % 1 === 0) end -= 0.5
|
||||
|
||||
for (let s = start; s <= end; s += 1) {
|
||||
spreads.push(s)
|
||||
}
|
||||
return spreads
|
||||
}
|
||||
|
||||
export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelProps) => {
|
||||
const { isAuthenticated } = useAuthStore()
|
||||
const queryClient = useQueryClient()
|
||||
@ -52,9 +66,6 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
const [stakeAmount, setStakeAmount] = useState('')
|
||||
const [activeTab, setActiveTab] = useState<'chart' | 'grid'>('chart')
|
||||
|
||||
// Ref for above spread line container
|
||||
const aboveLineRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Update countdown every second
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
@ -96,31 +107,25 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
return max
|
||||
}, [event.spread_grid])
|
||||
|
||||
// Generate ALL spreads from min to max, split at official line for order book
|
||||
const { aboveLine, belowLine } = useMemo(() => {
|
||||
// Generate half-point spreads and split at official line for order book
|
||||
const { aboveLine, belowLine, allSpreads } = useMemo(() => {
|
||||
const spreads = generateHalfPointSpreads(event.min_spread, event.max_spread)
|
||||
const above: number[] = []
|
||||
const below: number[] = []
|
||||
|
||||
// Generate all possible spreads in range
|
||||
for (let s = event.min_spread; s <= event.max_spread; s += 0.5) {
|
||||
spreads.forEach(s => {
|
||||
if (s > event.official_spread) above.push(s)
|
||||
else if (s < event.official_spread) below.push(s)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
aboveLine: above.sort((a, b) => b - a), // High to low (highest at top)
|
||||
aboveLine: above.sort((a, b) => b - a), // High to low (9.5, 8.5, 7.5...)
|
||||
belowLine: below.sort((a, b) => b - a), // High to low (closest to line at top)
|
||||
allSpreads: spreads,
|
||||
}
|
||||
}, [event.min_spread, event.max_spread, event.official_spread])
|
||||
|
||||
// Auto-scroll above spread line to bottom after render
|
||||
useEffect(() => {
|
||||
if (aboveLineRef.current) {
|
||||
aboveLineRef.current.scrollTop = aboveLineRef.current.scrollHeight
|
||||
}
|
||||
}, [aboveLine])
|
||||
|
||||
// Get all bets for chart and recent activity (including matched)
|
||||
// Get all bets with spread info for chart and recent activity
|
||||
const allBetsWithSpread = useMemo(() => {
|
||||
const bets: (SpreadGridBet & { spread: number })[] = []
|
||||
Object.entries(event.spread_grid).forEach(([spread, spreadBets]) => {
|
||||
@ -136,26 +141,25 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
return allBetsWithSpread
|
||||
}, [allBetsWithSpread])
|
||||
|
||||
// Chart data - volume per spread
|
||||
// Chart data - volume per spread (half-points only)
|
||||
const chartData = useMemo(() => {
|
||||
const data: { spread: number; homeVolume: number; awayVolume: number; total: number }[] = []
|
||||
|
||||
// Get all possible spreads in range
|
||||
for (let s = event.min_spread; s <= event.max_spread; s += 0.5) {
|
||||
allSpreads.forEach(s => {
|
||||
const bets = event.spread_grid[s.toString()] || []
|
||||
const homeVol = bets.filter(b => b.team === 'home').reduce((sum, b) => sum + b.stake, 0)
|
||||
const awayVol = bets.filter(b => b.team === 'away').reduce((sum, b) => sum + b.stake, 0)
|
||||
data.push({ spread: s, homeVolume: homeVol, awayVolume: awayVol, total: homeVol + awayVol })
|
||||
}
|
||||
})
|
||||
|
||||
return data
|
||||
}, [event.spread_grid, event.min_spread, event.max_spread])
|
||||
}, [event.spread_grid, allSpreads])
|
||||
|
||||
const chartMaxVolume = useMemo(() => {
|
||||
return Math.max(...chartData.map(d => d.total), 1)
|
||||
}, [chartData])
|
||||
|
||||
// Available bets to take
|
||||
// Available bets to take at selected spread
|
||||
const availableBets = useMemo(() => {
|
||||
const bets = event.spread_grid[selectedSpread.toString()] || []
|
||||
return bets.filter(b => b.status === 'open' && b.can_take)
|
||||
@ -217,6 +221,15 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
takeBetMutation.mutate(betId)
|
||||
}
|
||||
|
||||
// Adjust spread by 1 point (next half-point)
|
||||
const adjustSpread = (delta: number) => {
|
||||
const newSpread = selectedSpread + delta
|
||||
// Ensure it's a valid half-point spread within range
|
||||
if (newSpread >= event.min_spread && newSpread <= event.max_spread && newSpread % 1 !== 0) {
|
||||
setSelectedSpread(newSpread)
|
||||
}
|
||||
}
|
||||
|
||||
// Quick stake buttons
|
||||
const quickStakes = [25, 50, 100, 250, 500]
|
||||
|
||||
@ -275,16 +288,11 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
)
|
||||
}
|
||||
|
||||
// Grid view - shows all spreads in a grid format
|
||||
// Grid view - shows all half-point spreads in a grid format
|
||||
const renderGridView = () => {
|
||||
const spreads: number[] = []
|
||||
for (let s = event.min_spread; s <= event.max_spread; s += 0.5) {
|
||||
spreads.push(s)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-10 gap-2">
|
||||
{spreads.map(spread => {
|
||||
{allSpreads.map(spread => {
|
||||
const bets = event.spread_grid[spread.toString()] || []
|
||||
const homeBets = bets.filter(b => b.team === 'home')
|
||||
const awayBets = bets.filter(b => b.team === 'away')
|
||||
@ -321,86 +329,86 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
{/* Header - Event Info */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-blue-700 p-4 text-white">
|
||||
{/* Compact Header - Exchange Style */}
|
||||
<div className="bg-gray-800 px-4 py-3 text-white">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-6">
|
||||
{/* Left: Teams and Spread */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Home Team */}
|
||||
<div className="text-center">
|
||||
<div className="w-14 h-14 bg-white/20 rounded-full flex items-center justify-center mb-1">
|
||||
<span className="text-2xl font-bold">{event.home_team.charAt(0)}</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-green-600/20 rounded-full flex items-center justify-center">
|
||||
<span className="text-green-400 font-bold">{event.home_team.charAt(0)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-sm">{event.home_team}</p>
|
||||
<p className="text-gray-400 text-xs">HOME</p>
|
||||
</div>
|
||||
<p className="font-semibold">{event.home_team}</p>
|
||||
<p className="text-blue-200 text-xs">HOME</p>
|
||||
</div>
|
||||
|
||||
{/* VS / Spread */}
|
||||
<div className="text-center px-6">
|
||||
<div className="text-blue-200 text-xs mb-1">OFFICIAL SPREAD</div>
|
||||
<div className="text-3xl font-bold">
|
||||
{/* Spread */}
|
||||
<div className="text-center px-6 border-l border-r border-gray-700">
|
||||
<div className="text-gray-400 text-xs">SPREAD</div>
|
||||
<div className="text-yellow-400 text-2xl font-bold">
|
||||
{event.official_spread > 0 ? '+' : ''}{event.official_spread}
|
||||
</div>
|
||||
<div className="text-blue-200 text-xs mt-1">{event.league}</div>
|
||||
<div className="text-gray-400 text-xs">{event.league}</div>
|
||||
</div>
|
||||
|
||||
{/* Away Team */}
|
||||
<div className="text-center">
|
||||
<div className="w-14 h-14 bg-white/20 rounded-full flex items-center justify-center mb-1">
|
||||
<span className="text-2xl font-bold">{event.away_team.charAt(0)}</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-red-600/20 rounded-full flex items-center justify-center">
|
||||
<span className="text-red-400 font-bold">{event.away_team.charAt(0)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-sm">{event.away_team}</p>
|
||||
<p className="text-gray-400 text-xs">AWAY</p>
|
||||
</div>
|
||||
<p className="font-semibold">{event.away_team}</p>
|
||||
<p className="text-blue-200 text-xs">AWAY</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Game Time */}
|
||||
<div className="text-right">
|
||||
{/* Center: Market Stats */}
|
||||
<div className="flex items-center gap-6 px-6 border-l border-gray-700">
|
||||
<div className="text-center">
|
||||
<p className="text-gray-400 text-xs">Volume</p>
|
||||
<p className="font-mono font-semibold">${marketStats.totalVolume.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-gray-400 text-xs">Open</p>
|
||||
<p className="font-mono font-semibold text-green-400">{marketStats.openBets}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<TrendingUp size={14} className="text-green-400" />
|
||||
<span className="text-green-400 font-mono text-sm">${marketStats.homeVolume.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<TrendingDown size={14} className="text-red-400" />
|
||||
<span className="text-red-400 font-mono text-sm">${marketStats.awayVolume.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: Countdown */}
|
||||
<div className="flex items-center gap-3 pl-6 border-l border-gray-700">
|
||||
<div className={`
|
||||
inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold
|
||||
${timeUntil.urgent ? 'bg-red-500 animate-pulse' : 'bg-white/20'}
|
||||
flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold
|
||||
${timeUntil.urgent ? 'bg-red-500/20 text-red-400 animate-pulse' : 'bg-gray-700 text-gray-300'}
|
||||
`}>
|
||||
<Clock size={16} />
|
||||
{timeUntil.text}
|
||||
<span>{timeUntil.text}</span>
|
||||
</div>
|
||||
<p className="text-blue-200 text-xs mt-2">
|
||||
{new Date(event.game_time).toLocaleDateString('en-US', {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Bar */}
|
||||
<div className="flex items-center justify-between mt-4 pt-4 border-t border-white/20 text-sm">
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<DollarSign size={14} className="text-blue-200" />
|
||||
<span className="text-blue-200">Volume:</span>
|
||||
<span className="font-semibold">${marketStats.totalVolume.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Activity size={14} className="text-blue-200" />
|
||||
<span className="text-blue-200">Bets:</span>
|
||||
<span className="font-semibold">{marketStats.totalBets}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Users size={14} className="text-blue-200" />
|
||||
<span className="text-blue-200">Open:</span>
|
||||
<span className="font-semibold text-green-300">{marketStats.openBets}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<TrendingUp size={14} className="text-green-300" />
|
||||
<span className="text-green-300">${marketStats.homeVolume.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<TrendingDown size={14} className="text-red-300" />
|
||||
<span className="text-red-300">${marketStats.awayVolume.toLocaleString()}</span>
|
||||
<div className="text-right">
|
||||
<p className="text-gray-400 text-xs">
|
||||
{new Date(event.game_time).toLocaleDateString('en-US', {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</p>
|
||||
<p className="text-gray-300 text-xs">
|
||||
{new Date(event.game_time).toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -409,16 +417,16 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
{/* Main Content - fixed height container */}
|
||||
<div className="grid grid-cols-12 divide-x divide-gray-200 h-[600px] overflow-hidden">
|
||||
{/* Left - Order Book */}
|
||||
<div className="col-span-3 flex flex-col overflow-hidden">
|
||||
<div className="p-4 pb-2">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2">
|
||||
<Activity size={16} className="text-gray-400" />
|
||||
<div className="col-span-3 flex flex-col overflow-hidden bg-gray-50">
|
||||
<div className="p-3 border-b bg-white">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center gap-2 text-sm">
|
||||
<Activity size={14} className="text-gray-400" />
|
||||
Order Book
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{/* Header */}
|
||||
<div className="grid grid-cols-7 gap-1 py-2 px-4 text-xs text-gray-500 border-b">
|
||||
{/* Column Header */}
|
||||
<div className="grid grid-cols-7 gap-1 py-1.5 px-2 text-xs text-gray-500 border-b bg-white">
|
||||
<span className="text-right">{event.away_team.slice(0, 4)}</span>
|
||||
<span className="text-right">Vol</span>
|
||||
<span></span>
|
||||
@ -428,15 +436,11 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
<span className="text-left">{event.home_team.slice(0, 4)}</span>
|
||||
</div>
|
||||
|
||||
{/* Above official line - fills available space, auto-scrolls to bottom */}
|
||||
<div ref={aboveLineRef} className="flex-1 overflow-y-auto border-b border-gray-100 px-2">
|
||||
{aboveLine.length > 0 ? (
|
||||
aboveLine.map(spread => renderOrderRow(spread))
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full text-gray-400 text-xs">
|
||||
No spreads above line
|
||||
</div>
|
||||
)}
|
||||
{/* Above official line - aligned to bottom */}
|
||||
<div className="flex-1 overflow-y-auto flex flex-col justify-end border-b border-gray-200 bg-white">
|
||||
<div className="px-2">
|
||||
{aboveLine.map(spread => renderOrderRow(spread))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Official Line */}
|
||||
@ -444,22 +448,18 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
{renderOrderRow(event.official_spread)}
|
||||
</div>
|
||||
|
||||
{/* Below official line - fills available space */}
|
||||
<div className="flex-1 overflow-y-auto px-2">
|
||||
{belowLine.length > 0 ? (
|
||||
belowLine.map(spread => renderOrderRow(spread))
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full text-gray-400 text-xs">
|
||||
No spreads below line
|
||||
</div>
|
||||
)}
|
||||
{/* Below official line - aligned to top */}
|
||||
<div className="flex-1 overflow-y-auto bg-white">
|
||||
<div className="px-2">
|
||||
{belowLine.map(spread => renderOrderRow(spread))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Center - Chart/Grid with Tabs */}
|
||||
<div className="col-span-6 flex flex-col overflow-hidden">
|
||||
{/* Tabs */}
|
||||
<div className="flex border-b">
|
||||
<div className="flex border-b bg-white">
|
||||
<button
|
||||
onClick={() => setActiveTab('chart')}
|
||||
className={`flex items-center gap-2 px-4 py-3 text-sm font-medium border-b-2 transition-colors ${
|
||||
@ -507,7 +507,6 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
</div>
|
||||
|
||||
{chartData.map((d) => {
|
||||
// Calculate heights as percentage of max, ensuring max bar fills 100%
|
||||
const totalHeightPercent = (d.total / chartMaxVolume) * 100
|
||||
const homeHeightPercent = d.total > 0 ? (d.homeVolume / d.total) * totalHeightPercent : 0
|
||||
const awayHeightPercent = d.total > 0 ? (d.awayVolume / d.total) * totalHeightPercent : 0
|
||||
@ -545,7 +544,7 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
{/* X-axis labels */}
|
||||
<div className="flex justify-between mt-1 text-xs text-gray-500 px-2 flex-shrink-0">
|
||||
<span>{event.min_spread}</span>
|
||||
<span>Spread</span>
|
||||
<span>Spread (half-points)</span>
|
||||
<span>+{event.max_spread}</span>
|
||||
</div>
|
||||
|
||||
@ -600,7 +599,7 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
) : (
|
||||
/* Grid View */
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">All Spreads</h3>
|
||||
<h3 className="font-semibold text-gray-900 mb-4">All Spreads (half-points only)</h3>
|
||||
{renderGridView()}
|
||||
</div>
|
||||
)}
|
||||
@ -654,10 +653,10 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
|
||||
{/* Spread Selection */}
|
||||
<div className="mb-4">
|
||||
<label className="text-gray-600 text-xs block mb-1">Spread</label>
|
||||
<label className="text-gray-600 text-xs block mb-1">Spread (half-points only)</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => setSelectedSpread(s => Math.max(event.min_spread, s - 0.5))}
|
||||
onClick={() => adjustSpread(-1)}
|
||||
className="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 font-bold"
|
||||
>
|
||||
−
|
||||
@ -671,7 +670,7 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setSelectedSpread(s => Math.min(event.max_spread, s + 0.5))}
|
||||
onClick={() => adjustSpread(1)}
|
||||
className="px-4 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 font-bold"
|
||||
>
|
||||
+
|
||||
@ -722,13 +721,15 @@ export const TradingPanel = ({ event, onBetCreated, onBetTaken }: TradingPanelPr
|
||||
{availableBets.length > 0 && (
|
||||
<div className="border-t pt-4">
|
||||
<h4 className="text-sm font-semibold text-gray-700 mb-2">
|
||||
Take Existing Bet ({availableBets.length} available)
|
||||
Take Existing Bet ({availableBets.length} available at {selectedSpread > 0 ? '+' : ''}{selectedSpread})
|
||||
</h4>
|
||||
<div className="space-y-2 max-h-32 overflow-y-auto">
|
||||
{availableBets.map(bet => (
|
||||
<div key={bet.bet_id} className="flex items-center justify-between p-2 bg-white border rounded-lg">
|
||||
<div>
|
||||
<p className="font-semibold text-sm">${bet.stake.toFixed(0)}</p>
|
||||
<p className={`font-semibold text-sm ${bet.team === 'home' ? 'text-green-600' : 'text-red-600'}`}>
|
||||
${bet.stake.toFixed(0)} on {bet.team === 'home' ? event.home_team : event.away_team}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">by {bet.creator_username}</p>
|
||||
</div>
|
||||
<button
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
Crown,
|
||||
Users,
|
||||
Rocket,
|
||||
Zap,
|
||||
Share2,
|
||||
Settings,
|
||||
Receipt
|
||||
@ -44,12 +43,6 @@ const MORE_DROPDOWN = [
|
||||
description: 'Discover and gain access to new bets',
|
||||
icon: Rocket
|
||||
},
|
||||
{
|
||||
path: '/megadrop',
|
||||
label: 'Megadrop',
|
||||
description: 'Lock your bets and complete quests for boosted airdrop rewards',
|
||||
icon: Zap
|
||||
},
|
||||
]
|
||||
|
||||
// Reusable dropdown hook
|
||||
@ -225,8 +218,8 @@ export const Header = () => {
|
||||
|
||||
{/* Left-justified navigation links */}
|
||||
<nav className="flex items-center gap-6 ml-8">
|
||||
<Link to="/sports" className="text-gray-700 hover:text-primary transition-colors">
|
||||
Markets
|
||||
<Link to="/" className="text-gray-700 hover:text-primary transition-colors">
|
||||
Events
|
||||
</Link>
|
||||
<Link to="/live" className="text-gray-700 hover:text-primary transition-colors">
|
||||
Live
|
||||
|
||||
Reference in New Issue
Block a user