feat: add hero banner, live events list, and sports navigation components
- Implemented HeroBanner component for image carousel with navigation arrows and indicators. - Created LiveEventsList component to display live events with odds and match details. - Added SportsNav component for sport category navigation with icons. - Introduced TopMatches component to showcase highlighted matches with odds. - Updated InPlayHeader and QuickFilterBar for improved UI and functionality. - Enhanced ReloadTicket and SearchEvent components for better user experience. - Refactored SportsSidebar to include popular leagues and quick filter options. - Added new sport-home layout to integrate various betting components.
This commit is contained in:
parent
6376713398
commit
612bb9386b
|
|
@ -1,25 +1,29 @@
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { BarChart2, PrinterIcon, Trophy } from "lucide-react"
|
||||||
|
|
||||||
const services = ["Live Score", "Results", "Print Odds"]
|
const services = [
|
||||||
|
{ label: "Live Score", icon: BarChart2 },
|
||||||
|
{ label: "Results", icon: Trophy },
|
||||||
|
{ label: "Print Odds", icon: PrinterIcon },
|
||||||
|
]
|
||||||
|
|
||||||
export function BetServices() {
|
export function BetServices() {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3 rounded-md border bg-card/60 px-3 py-2 text-[11px]">
|
<div className="flex items-center gap-0 bg-secondary rounded border border-border overflow-hidden text-[11px]">
|
||||||
<span className="font-medium uppercase tracking-wide text-muted-foreground">
|
<span className="px-3 py-2 font-bold uppercase text-muted-foreground text-[10px] tracking-wider border-r border-border">
|
||||||
Bet Services
|
Services
|
||||||
</span>
|
</span>
|
||||||
<Separator orientation="vertical" className="h-4" />
|
{services.map((service, i) => {
|
||||||
<div className="flex flex-wrap gap-2">
|
const Icon = service.icon
|
||||||
{services.map((service) => (
|
return (
|
||||||
<button
|
<button
|
||||||
key={service}
|
key={service.label}
|
||||||
className="text-[11px] text-primary underline-offset-2 hover:underline"
|
className={`flex items-center gap-1.5 px-3 py-2 text-muted-foreground hover:text-primary hover:bg-muted transition-colors ${i < services.length - 1 ? "border-r border-border" : ""}`}
|
||||||
>
|
>
|
||||||
{service}
|
<Icon className="size-3" />
|
||||||
|
{service.label}
|
||||||
</button>
|
</button>
|
||||||
))}
|
)
|
||||||
</div>
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,64 +1,187 @@
|
||||||
import { useBetslipStore } from "@/lib/store/betslip-store";
|
"use client"
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { useState } from "react"
|
||||||
|
import { useBetslipStore } from "@/lib/store/betslip-store"
|
||||||
|
import { X, ChevronDown, Trash2 } from "lucide-react"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const quickStakes = [5, 10, 20, 50, 100, 200]
|
||||||
|
|
||||||
export function Betslip() {
|
export function Betslip() {
|
||||||
const { bets, removeBet, clearBets } = useBetslipStore();
|
const { bets, removeBet, clearBets, updateStake, getPotentialWin, getTotalOdds } = useBetslipStore()
|
||||||
|
const [activeTab, setActiveTab] = useState<"single" | "accumulator">("single")
|
||||||
|
const [globalStake, setGlobalStake] = useState("10")
|
||||||
|
|
||||||
|
const potentialWin = getPotentialWin()
|
||||||
|
const totalOdds = getTotalOdds()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<div className="bg-card rounded border border-border overflow-hidden">
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0">
|
{/* Header */}
|
||||||
<CardTitle className="text-sm font-semibold">
|
<div className="bg-primary px-3 py-2 flex items-center justify-between">
|
||||||
Betslip {bets.length}
|
<div className="flex items-center gap-2">
|
||||||
</CardTitle>
|
<span className="text-[11px] font-bold text-primary-foreground uppercase">Betslip</span>
|
||||||
|
{bets.length > 0 && (
|
||||||
|
<span className="bg-primary-foreground text-primary text-[10px] font-bold px-1.5 py-0.5 rounded-full min-w-[18px] text-center">
|
||||||
|
{bets.length}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{bets.length > 0 && (
|
{bets.length > 0 && (
|
||||||
<button
|
<button
|
||||||
onClick={clearBets}
|
onClick={clearBets}
|
||||||
className="text-xs text-muted-foreground hover:underline"
|
className="text-primary-foreground/70 hover:text-primary-foreground transition-colors"
|
||||||
>
|
>
|
||||||
Clear all
|
<Trash2 className="size-3.5" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent className="space-y-3 text-xs">
|
|
||||||
|
{/* Tabs */}
|
||||||
|
{bets.length > 1 && (
|
||||||
|
<div className="flex border-b border-border">
|
||||||
|
{(["single", "accumulator"] as const).map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
onClick={() => setActiveTab(tab)}
|
||||||
|
className={cn(
|
||||||
|
"flex-1 py-1.5 text-[10px] font-semibold uppercase transition-colors",
|
||||||
|
activeTab === tab
|
||||||
|
? "bg-muted text-foreground border-b-2 border-primary"
|
||||||
|
: "text-muted-foreground hover:text-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{tab}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="p-2 space-y-2">
|
||||||
{bets.length === 0 ? (
|
{bets.length === 0 ? (
|
||||||
<p className="text-muted-foreground">
|
<div className="py-6 text-center">
|
||||||
No bet has been selected. To select a bet, please click on the
|
<div className="text-2xl mb-2">🎯</div>
|
||||||
respective odds.
|
<p className="text-[11px] text-muted-foreground leading-relaxed">
|
||||||
</p>
|
No bets selected. Click on odds to add selections.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2">
|
<>
|
||||||
{bets.map((bet) => (
|
{/* Bet items */}
|
||||||
<div
|
<div className="space-y-2">
|
||||||
key={bet.id}
|
{bets.map((bet) => (
|
||||||
className="rounded border bg-card/40 p-2 text-xs"
|
<div key={bet.id} className="bg-secondary/50 rounded border border-border/50 p-2">
|
||||||
>
|
<div className="flex items-start justify-between gap-2 mb-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex-1 min-w-0">
|
||||||
<div>
|
<div className="text-[11px] font-semibold text-foreground truncate">{bet.event}</div>
|
||||||
<div className="font-medium">{bet.event}</div>
|
<div className="text-[10px] text-muted-foreground truncate">{bet.league}</div>
|
||||||
<div className="text-[10px] text-muted-foreground">
|
<div className="text-[10px] text-primary font-medium mt-0.5">
|
||||||
{bet.market} – {bet.selection}
|
{bet.market}: <span className="text-foreground">{bet.selection}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1.5 shrink-0">
|
||||||
|
<span className="text-sm font-bold text-primary">{bet.odds.toFixed(2)}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => removeBet(bet.id)}
|
||||||
|
className="text-muted-foreground hover:text-destructive transition-colors"
|
||||||
|
>
|
||||||
|
<X className="size-3.5" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
|
||||||
<div className="font-semibold">{bet.odds.toFixed(2)}</div>
|
{/* Stake input for single */}
|
||||||
<button
|
{(activeTab === "single" || bets.length === 1) && (
|
||||||
onClick={() => removeBet(bet.id)}
|
<div>
|
||||||
className="text-[10px] text-destructive hover:underline"
|
<div className="text-[10px] text-muted-foreground mb-1">Stake (ETB)</div>
|
||||||
>
|
<div className="flex gap-1">
|
||||||
Remove
|
<input
|
||||||
</button>
|
type="number"
|
||||||
|
value={bet.stake ?? 10}
|
||||||
|
onChange={(e) => updateStake(bet.id, Number(e.target.value))}
|
||||||
|
className="flex-1 bg-input border border-border rounded px-2 py-1 text-xs text-foreground w-full min-w-0 focus:outline-none focus:border-primary"
|
||||||
|
min="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-1 mt-1 flex-wrap">
|
||||||
|
{quickStakes.map((s) => (
|
||||||
|
<button
|
||||||
|
key={s}
|
||||||
|
onClick={() => updateStake(bet.id, s)}
|
||||||
|
className={cn(
|
||||||
|
"text-[10px] px-1.5 py-0.5 rounded border transition-colors",
|
||||||
|
(bet.stake ?? 10) === s
|
||||||
|
? "bg-primary text-primary-foreground border-primary"
|
||||||
|
: "border-border text-muted-foreground hover:border-primary hover:text-primary"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{s}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* Potential win */}
|
||||||
|
<div className="mt-1.5 flex justify-between text-[10px]">
|
||||||
|
<span className="text-muted-foreground">Potential win:</span>
|
||||||
|
<span className="text-primary font-semibold">
|
||||||
|
{((bet.stake ?? 10) * bet.odds).toFixed(2)} ETB
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Accumulator stake section */}
|
||||||
|
{activeTab === "accumulator" && bets.length > 1 && (
|
||||||
|
<div className="bg-secondary/50 rounded border border-border/50 p-2 space-y-2">
|
||||||
|
<div className="flex justify-between text-[11px]">
|
||||||
|
<span className="text-muted-foreground">Total Odds:</span>
|
||||||
|
<span className="font-bold text-primary">{totalOdds.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-[10px] text-muted-foreground mb-1">Stake (ETB)</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={globalStake}
|
||||||
|
onChange={(e) => setGlobalStake(e.target.value)}
|
||||||
|
className="w-full bg-input border border-border rounded px-2 py-1 text-xs text-foreground focus:outline-none focus:border-primary"
|
||||||
|
min="1"
|
||||||
|
/>
|
||||||
|
<div className="flex gap-1 mt-1 flex-wrap">
|
||||||
|
{quickStakes.map((s) => (
|
||||||
|
<button
|
||||||
|
key={s}
|
||||||
|
onClick={() => setGlobalStake(String(s))}
|
||||||
|
className={cn(
|
||||||
|
"text-[10px] px-1.5 py-0.5 rounded border transition-colors",
|
||||||
|
globalStake === String(s)
|
||||||
|
? "bg-primary text-primary-foreground border-primary"
|
||||||
|
: "border-border text-muted-foreground hover:border-primary hover:text-primary"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{s}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-1.5 flex justify-between text-[10px]">
|
||||||
|
<span className="text-muted-foreground">Potential win:</span>
|
||||||
|
<span className="text-primary font-semibold">
|
||||||
|
{(Number(globalStake) * totalOdds).toFixed(2)} ETB
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
)}
|
||||||
<Button className="w-full text-xs" size="sm">
|
|
||||||
Place bet
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
{/* Place bet button */}
|
||||||
|
<button className="w-full bg-primary text-primary-foreground text-[12px] font-bold py-2.5 rounded hover:opacity-90 active:scale-95 transition-all uppercase tracking-wide">
|
||||||
|
Place Bet
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,50 @@
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
"use client"
|
||||||
import { Input } from "@/components/ui/input"
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { useState } from "react"
|
||||||
|
|
||||||
export function CheckYourBet() {
|
export function CheckYourBet() {
|
||||||
return (
|
const [betId, setBetId] = useState("")
|
||||||
<Card>
|
const [result, setResult] = useState<null | "pending" | "won" | "lost">(null)
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-sm">Check your bet</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-2 text-xs">
|
|
||||||
<div className="text-[11px] text-muted-foreground">Your bet ID</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Input
|
|
||||||
placeholder="Bet ID"
|
|
||||||
className="h-8 text-xs placeholder:text-[11px]"
|
|
||||||
/>
|
|
||||||
<Button size="sm" className="text-xs">
|
|
||||||
Check
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const handleCheck = () => {
|
||||||
|
if (!betId.trim()) return
|
||||||
|
// Mock result
|
||||||
|
const results = ["pending", "won", "lost"] as const
|
||||||
|
setResult(results[Math.floor(Math.random() * results.length)])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-transparent border-t border-border/20 pt-4 pb-2">
|
||||||
|
<div className="px-1 text-center space-y-3">
|
||||||
|
<h3 className="text-[12px] font-bold uppercase text-[#ff9800]">Check Your Bet</h3>
|
||||||
|
<p className="text-[11px] text-white">Your bet ID</p>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={betId}
|
||||||
|
onChange={(e) => setBetId(e.target.value)}
|
||||||
|
className="flex-1 bg-[#121212] border border-border/40 px-2 py-2 text-[11px] text-white outline-none focus:border-primary"
|
||||||
|
onKeyDown={(e) => e.key === "Enter" && handleCheck()}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleCheck}
|
||||||
|
className="bg-[#ff9800] text-black px-2 py-1.5 flex items-center justify-center min-w-[32px] hover:bg-[#ffa726] transition-colors"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 24 24" className="size-5 fill-current"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{result && (
|
||||||
|
<div className={`text-[11px] font-semibold p-2 rounded text-center mt-2 ${
|
||||||
|
result === "won" ? "bg-primary/20 text-primary" :
|
||||||
|
result === "lost" ? "bg-destructive/20 text-destructive" :
|
||||||
|
"bg-secondary text-muted-foreground"
|
||||||
|
}`}>
|
||||||
|
{result === "won" && "🎉 Bet Won!"}
|
||||||
|
{result === "lost" && "❌ Bet Lost"}
|
||||||
|
{result === "pending" && "⏳ Bet Pending"}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,80 +1,310 @@
|
||||||
import { Button } from "@/components/ui/button"
|
"use client"
|
||||||
import { Card, CardContent } from "@/components/ui/card"
|
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
import { useState, useEffect } from "react"
|
||||||
|
import { useSearchParams } from "next/navigation"
|
||||||
import { useBetslipStore } from "@/lib/store/betslip-store"
|
import { useBetslipStore } from "@/lib/store/betslip-store"
|
||||||
|
import { mockEvents, popularLeagues, type Event } from "@/lib/mock-data"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { ChevronDown, BarChart2, TrendingUp, Plus } from "lucide-react"
|
||||||
|
|
||||||
const mockEvents = [
|
function OddsButton({ odds, onClick, isSelected }: {
|
||||||
{
|
odds: number
|
||||||
id: "1",
|
onClick: () => void
|
||||||
sport: "Soccer",
|
isSelected: boolean
|
||||||
name: "Team A vs Team B",
|
}) {
|
||||||
markets: [
|
|
||||||
{ id: "1x2_home", label: "1", odds: 1.85 },
|
|
||||||
{ id: "1x2_draw", label: "X", odds: 3.4 },
|
|
||||||
{ id: "1x2_away", label: "2", odds: 4.1 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
sport: "Basketball",
|
|
||||||
name: "City Wolves vs Lake Bears",
|
|
||||||
markets: [
|
|
||||||
{ id: "spread_home", label: "Home -4.5", odds: 1.9 },
|
|
||||||
{ id: "spread_away", label: "Away +4.5", odds: 1.9 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export function EventsList() {
|
|
||||||
const { addBet } = useBetslipStore()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="flex-1">
|
<button
|
||||||
<CardContent className="px-0 pb-4 pt-3">
|
onClick={onClick}
|
||||||
<ScrollArea className="h-[360px]">
|
className={cn(
|
||||||
<div className="space-y-2 px-3 pb-2">
|
"flex items-center justify-center py-2 px-0.5 border-r border-border/10 text-[10px] transition-all min-w-0 bg-[#262626] hover:bg-[#333] h-full",
|
||||||
{mockEvents.map((event) => (
|
isSelected && "bg-[#ff9800] text-black font-bold border-none"
|
||||||
<div
|
)}
|
||||||
key={event.id}
|
>
|
||||||
className="rounded-md border bg-card/50 p-2 text-xs"
|
<span className={cn("font-bold tracking-tighter", isSelected ? "text-black" : "text-[#ff9800]")}>{odds.toFixed(2)}</span>
|
||||||
>
|
</button>
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<div className="font-medium">{event.name}</div>
|
|
||||||
<div className="text-[11px] text-muted-foreground">
|
|
||||||
{event.sport}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
||||||
{event.markets.map((market) => (
|
|
||||||
<Button
|
|
||||||
key={market.id}
|
|
||||||
size="xs"
|
|
||||||
className="min-w-[60px] justify-between rounded-sm bg-secondary text-secondary-foreground text-[11px] hover:bg-primary hover:text-primary-foreground"
|
|
||||||
onClick={() =>
|
|
||||||
addBet({
|
|
||||||
id: `${event.id}-${market.id}`,
|
|
||||||
event: event.name,
|
|
||||||
market: event.sport,
|
|
||||||
selection: market.label,
|
|
||||||
odds: market.odds,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span>{market.label}</span>
|
|
||||||
<span className="font-semibold">
|
|
||||||
{market.odds.toFixed(2)}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</ScrollArea>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function EventRow({ event }: { event: Event }) {
|
||||||
|
const { bets, addBet } = useBetslipStore()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-[#1a1a1a] border-b border-border/20 hover:bg-[#222] transition-colors h-[38px] flex items-center">
|
||||||
|
{/* Small Icons & ID Column */}
|
||||||
|
<div className="flex items-center gap-1.5 px-2 w-[80px] shrink-0 border-r border-border/10 h-full">
|
||||||
|
<BarChart2 className="size-3 text-muted-foreground hover:text-primary cursor-pointer shrink-0" />
|
||||||
|
<TrendingUp className="size-3 text-muted-foreground hover:text-primary cursor-pointer shrink-0" />
|
||||||
|
<span className="text-[9.5px] text-[#ff9800] font-bold tabular-nums italic ml-0.5">{event.id || "01682"}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Time & Team Column */}
|
||||||
|
<div className="flex items-center gap-3 px-3 w-[240px] shrink-0 border-r border-border/10 h-full">
|
||||||
|
<div className="flex flex-col text-[9.5px] font-bold text-white leading-tight italic shrink-0 w-[45px]">
|
||||||
|
<span>11:00</span>
|
||||||
|
<span className="text-white/40 uppercase font-medium">PM</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-[10px] font-bold text-white truncate max-w-[180px]">{event.homeTeam} - {event.awayTeam}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Market Columns Grid (10 Markets) */}
|
||||||
|
<div className="flex-1 grid grid-cols-10 h-full shrink-0">
|
||||||
|
{event.markets.slice(0, 10).map((market) => {
|
||||||
|
const betId = `${event.id}-${market.id}`
|
||||||
|
const isSelected = bets.some((b) => b.id === betId)
|
||||||
|
return (
|
||||||
|
<OddsButton
|
||||||
|
key={market.id}
|
||||||
|
odds={market.odds}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onClick={() => addBet({
|
||||||
|
id: betId,
|
||||||
|
event: `${event.homeTeam} vs ${event.awayTeam}`,
|
||||||
|
league: event.league,
|
||||||
|
market: "1X2",
|
||||||
|
selection: `${event.homeTeam} (${market.label})`,
|
||||||
|
odds: market.odds,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* More Markets Button */}
|
||||||
|
<button className="w-10 flex items-center justify-center h-full hover:bg-white/5 transition-colors border-l border-border/10 group">
|
||||||
|
<Plus className="size-3 text-white group-hover:scale-110 transition-transform" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const MARKET_HEADERS = ["1", "x", "2", "Over (2.5)", "Under (2.5)", "1X", "12", "X2", "Yes", "No"];
|
||||||
|
|
||||||
|
export function EventsList({ filter = "All", sport = "all", search = "" }: {
|
||||||
|
filter?: string
|
||||||
|
sport?: string
|
||||||
|
search?: string
|
||||||
|
}) {
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
const leagueQuery = searchParams.get("league")
|
||||||
|
const [selectedLeague, setSelectedLeague] = useState<string | null>(leagueQuery)
|
||||||
|
const { bets, addBet } = useBetslipStore()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedLeague(leagueQuery)
|
||||||
|
}, [leagueQuery])
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
const url = new URL(window.location.href)
|
||||||
|
url.searchParams.delete("league")
|
||||||
|
window.history.pushState({}, "", url)
|
||||||
|
setSelectedLeague(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const events = selectedLeague
|
||||||
|
? mockEvents.filter(e => e.league.toLowerCase() === selectedLeague.toLowerCase())
|
||||||
|
: mockEvents.filter((e) => {
|
||||||
|
if (filter === "Live" && !e.isLive) return false
|
||||||
|
if (sport !== "all" && e.sport.toLowerCase() !== sport.toLowerCase()) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Common Header Rendering
|
||||||
|
const renderTableHeaders = () => (
|
||||||
|
<>
|
||||||
|
{/* Table Header Categories */}
|
||||||
|
<div className="bg-[#1a1a1a] border-b border-border/40 grid grid-cols-5 text-[11px] font-bold text-white uppercase text-center items-center h-9">
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">Main</div>
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">Goals</div>
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">Handicap</div>
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20 text-[9px] leading-tight">Half Time / Full Time</div>
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer">Correct Score</div>
|
||||||
|
</div>
|
||||||
|
{/* Sub Headers */}
|
||||||
|
<div className="bg-[#1a1a1a] border-b border-border/40 grid grid-cols-5 text-[10px] font-bold text-white uppercase text-center items-center h-8">
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">1st Half</div>
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">2nd Half</div>
|
||||||
|
<div className="h-full flex items-center justify-center bg-[#ff9800] text-black border-r border-border/10">Combo</div>
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">Chance Mix</div>
|
||||||
|
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer">Home</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderColumnHeaders = () => (
|
||||||
|
<div className="bg-[#222] border-b border-white/5 h-8 flex items-center text-[9px] font-black text-white/40 uppercase">
|
||||||
|
<div className="w-[180px] px-3 flex items-center gap-1.5 border-r border-border/10 h-full">Main</div>
|
||||||
|
<div className="w-[180px] flex items-center justify-center border-r border-border/10 h-full">Over/Under</div>
|
||||||
|
<div className="flex-1 grid grid-cols-10 text-center h-full items-center tracking-tighter">
|
||||||
|
{MARKET_HEADERS.map(h => <span key={h}>{h}</span>)}
|
||||||
|
</div>
|
||||||
|
<div className="w-10 border-l border-border/10 h-full" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderEventItem = (event: Event) => (
|
||||||
|
<div key={event.id} className="h-[34px] group flex items-center border-b border-white/5 bg-[#121212] hover:bg-white/5 transition-colors">
|
||||||
|
{/* Stats & Icons */}
|
||||||
|
<div className="w-[35px] flex items-center justify-center gap-1 px-2 border-r border-white/5 h-full opacity-40 group-hover:opacity-100">
|
||||||
|
<BarChart2 className="size-3 cursor-pointer hover:text-primary" />
|
||||||
|
</div>
|
||||||
|
{/* ID */}
|
||||||
|
<div className="w-[45px] text-[10px] font-black text-[#ff9800] italic tabular-nums text-center border-r border-white/5 h-full flex items-center justify-center">
|
||||||
|
{event.id}
|
||||||
|
</div>
|
||||||
|
{/* Time */}
|
||||||
|
<div className="w-[50px] flex flex-col items-center justify-center border-r border-white/5 h-full leading-none italic font-black text-[9px]">
|
||||||
|
<span>{event.time}</span>
|
||||||
|
<span className="text-[7px] text-white/30 uppercase mt-0.5">PM</span>
|
||||||
|
</div>
|
||||||
|
{/* Event Name */}
|
||||||
|
<div className="flex-1 px-4 text-[10.5px] font-black text-white truncate max-w-[200px]">
|
||||||
|
{event.homeTeam} - {event.awayTeam}
|
||||||
|
</div>
|
||||||
|
{/* Odds Grid */}
|
||||||
|
<div className="flex-1 grid grid-cols-10 h-full">
|
||||||
|
{event.markets.slice(0, 10).map((market) => {
|
||||||
|
const betId = `${event.id}-${market.id}`
|
||||||
|
const isSelected = bets.some((b) => b.id === betId)
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={market.id}
|
||||||
|
onClick={() => addBet({
|
||||||
|
id: betId,
|
||||||
|
event: `${event.homeTeam} vs ${event.awayTeam}`,
|
||||||
|
league: event.league,
|
||||||
|
market: "1X2",
|
||||||
|
selection: `${event.homeTeam} (${market.label})`,
|
||||||
|
odds: market.odds,
|
||||||
|
})}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center justify-center text-[10.5px] font-black tabular-nums transition-all border-r border-white/5",
|
||||||
|
isSelected ? "bg-[#ff9800] text-black" : "text-[#ff9800] hover:bg-white/5"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{market.odds.toFixed(2)}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{/* More Button */}
|
||||||
|
<button className="w-10 flex items-center justify-center h-full hover:bg-white/5 transition-colors border-l border-white/5 text-white/40">
|
||||||
|
<Plus className="size-3" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (selectedLeague) {
|
||||||
|
// Group by date for league view
|
||||||
|
const groupedEvents = events.reduce((acc, event) => {
|
||||||
|
if (!acc[event.date]) acc[event.date] = []
|
||||||
|
acc[event.date].push(event)
|
||||||
|
return acc
|
||||||
|
}, {} as Record<string, Event[]>)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col bg-[#121212] rounded overflow-hidden shadow-2xl">
|
||||||
|
{/* League Header / Breadcrumbs */}
|
||||||
|
<div className="bg-[#1a1a1a] px-3 py-2 flex items-center justify-between border-b border-border/20">
|
||||||
|
<div className="flex items-center gap-2 text-[11px] font-bold text-white/60">
|
||||||
|
<Plus className="size-3 cursor-pointer hover:text-white" />
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<span className="text-white font-black">•••</span>
|
||||||
|
<span className="uppercase">Football</span>
|
||||||
|
<span className="mx-1 opacity-20">|</span>
|
||||||
|
<span className="text-white uppercase">{selectedLeague === "LaLiga" ? "Spain - LaLiga" : selectedLeague}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button onClick={handleClose} className="text-white hover:text-primary transition-colors">
|
||||||
|
<Plus className="size-5 rotate-45" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Large Market Tab Grid */}
|
||||||
|
<div className="grid grid-cols-5 bg-[#121212] border-b border-border/10">
|
||||||
|
{[
|
||||||
|
{ label: "Main", active: true }, { label: "Goals" }, { label: "Handicap" }, { label: "Half Time / Full Time" }, { label: "Correct Score" },
|
||||||
|
{ label: "1st Half" }, { label: "2nd Half" }, { label: "Asian Markets" }, { label: "Corners" }, { label: "Home" }
|
||||||
|
].map((m, i) => (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
className={cn(
|
||||||
|
"h-8 border-r border-b border-border/10 flex items-center justify-center text-[10px] font-black uppercase transition-all",
|
||||||
|
m.active ? "bg-[#ff9800] text-black" : "text-white/60 hover:bg-[#222]"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{m.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Column Headers */}
|
||||||
|
{renderColumnHeaders()}
|
||||||
|
|
||||||
|
{/* Grouped Events */}
|
||||||
|
<div className="overflow-y-auto max-h-[700px]">
|
||||||
|
{Object.entries(groupedEvents).map(([date, dateEvents]) => (
|
||||||
|
<div key={date} className="flex flex-col">
|
||||||
|
<div className="bg-[#1a1a1a] px-2 py-1 text-[10px] font-black text-white border-b border-white/5">
|
||||||
|
{date}
|
||||||
|
</div>
|
||||||
|
{dateEvents.map(event => renderEventItem(event))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Home View (No League Selected)
|
||||||
|
const homeEventsByLeague = events.reduce((acc, event) => {
|
||||||
|
if (!acc[event.league]) acc[event.league] = []
|
||||||
|
acc[event.league].push(event)
|
||||||
|
return acc
|
||||||
|
}, {} as Record<string, Event[]>)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col bg-[#121212] rounded overflow-hidden">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{Object.entries(homeEventsByLeague).map(([leagueName, leagueEvents]) => (
|
||||||
|
<div key={leagueName} className="flex flex-col border-b border-white/5 last:border-none">
|
||||||
|
{/* League Box Header */}
|
||||||
|
<div className="bg-[#2a2a2a] px-3 py-1.5 text-[10px] font-bold text-[#ff9800] uppercase border-y border-border/20 flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-[14px]">
|
||||||
|
{popularLeagues.find(l => l.name === leagueName)?.icon ||
|
||||||
|
popularLeagues.find(l => l.id.toLowerCase() === leagueName.toLowerCase())?.icon ||
|
||||||
|
"⚽"}
|
||||||
|
</span>
|
||||||
|
<span>{leagueName === "LaLiga" ? "Spain - LaLiga" :
|
||||||
|
leagueName === "Premier League" ? "England - Premier League" :
|
||||||
|
leagueName === "Bundesliga" ? "Germany - Bundesliga" :
|
||||||
|
leagueName === "Ligue 1" ? "France - Ligue 1" :
|
||||||
|
leagueName}</span>
|
||||||
|
</div>
|
||||||
|
<ChevronDown className="size-4 text-white" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Column Headers for each league box */}
|
||||||
|
<div className="bg-[#222] px-3 py-1 flex items-center text-[8px] font-black text-white/40 uppercase border-b border-border/20">
|
||||||
|
<div className="w-[180px] flex gap-4">
|
||||||
|
<span className="w-5 text-center">Stats</span>
|
||||||
|
<span className="w-6">ID</span>
|
||||||
|
<span className="w-10">Time</span>
|
||||||
|
<span>Event</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 grid grid-cols-10 text-center tracking-tighter">
|
||||||
|
{MARKET_HEADERS.map(h => <span key={h}>{h}</span>)}
|
||||||
|
</div>
|
||||||
|
<div className="w-10" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Matches in this league */}
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{leagueEvents.map(event => renderEventItem(event))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
62
components/betting/hero-banner.tsx
Normal file
62
components/betting/hero-banner.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react"
|
||||||
|
import { ChevronLeft, ChevronRight } from "lucide-react"
|
||||||
|
|
||||||
|
const images = [
|
||||||
|
"https://api-new.harifsport.com/home/img/editable/casj-1822.jpg",
|
||||||
|
"https://api-new.harifsport.com/home/img/editable/casj-18s.jpg"
|
||||||
|
]
|
||||||
|
|
||||||
|
export function HeroBanner() {
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
setCurrentIndex((prev) => (prev + 1) % images.length)
|
||||||
|
}, 5000)
|
||||||
|
return () => clearInterval(timer)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full h-[240px] md:h-[300px] relative overflow-hidden rounded-none group shadow-lg bg-[#111]">
|
||||||
|
{images.map((src, index) => (
|
||||||
|
<img
|
||||||
|
key={src}
|
||||||
|
src={src}
|
||||||
|
alt={`Banner ${index + 1}`}
|
||||||
|
className={`absolute inset-0 object-contain w-full h-full transition-opacity duration-1000 ${
|
||||||
|
index === currentIndex ? "opacity-100" : "opacity-0"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Navigation Arrows */}
|
||||||
|
<button
|
||||||
|
onClick={() => setCurrentIndex((prev) => (prev - 1 + images.length) % images.length)}
|
||||||
|
className="absolute left-4 top-1/2 -translate-y-1/2 bg-black/60 p-2 text-white/80 hover:text-white transition-colors opacity-0 group-hover:opacity-100"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="size-6" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setCurrentIndex((prev) => (prev + 1) % images.length)}
|
||||||
|
className="absolute right-4 top-1/2 -translate-y-1/2 bg-black/60 p-2 text-white/80 hover:text-white transition-colors opacity-0 group-hover:opacity-100"
|
||||||
|
>
|
||||||
|
<ChevronRight className="size-6" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Indicators */}
|
||||||
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-1.5 px-3 py-1.5 rounded-full bg-black/30 backdrop-blur-sm">
|
||||||
|
{images.map((_, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
onClick={() => setCurrentIndex(index)}
|
||||||
|
className={`w-1.5 h-1.5 rounded-full transition-all ${
|
||||||
|
index === currentIndex ? "bg-[#ff9800] scale-125" : "bg-white/40"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
export function InPlayHeader() {
|
export function InPlayHeader() {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between border-b pb-2">
|
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||||
<h1 className="text-sm font-semibold uppercase tracking-wide text-foreground">
|
<div className="flex items-center gap-2">
|
||||||
IN-PLAY
|
<h1 className="text-sm font-black uppercase tracking-wide text-foreground">
|
||||||
</h1>
|
IN-PLAY
|
||||||
<span className="text-[11px] text-muted-foreground">Quick Filter</span>
|
</h1>
|
||||||
|
<span className="text-[10px] text-muted-foreground">/ Today's Events</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
195
components/betting/live-events-list.tsx
Normal file
195
components/betting/live-events-list.tsx
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useBetslipStore } from "@/lib/store/betslip-store"
|
||||||
|
import { mockEvents, type Event } from "@/lib/mock-data"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { BarChart2, TrendingUp, Monitor, Tv } from "lucide-react"
|
||||||
|
|
||||||
|
|
||||||
|
function LiveEventRow({ event, isNoOdds }: { event: Event, isNoOdds?: boolean }) {
|
||||||
|
const { bets, addBet } = useBetslipStore()
|
||||||
|
|
||||||
|
// Dummy data for demonstration
|
||||||
|
const score = event.homeScore !== undefined ? `${event.homeScore} - ${event.awayScore}` : "0 - 0"
|
||||||
|
const time = event.liveMinute ? `${event.liveMinute}:00` : "83:10"
|
||||||
|
const period = "H2"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-[#121212] border-b border-white/5 hover:bg-white/5 transition-colors h-[50px] flex items-center group">
|
||||||
|
{/* Match Info Column (Time & Score) */}
|
||||||
|
<div className="flex items-center gap-3 px-3 w-[360px] shrink-0 h-full border-r border-white/5">
|
||||||
|
<div className="flex flex-col text-[10px] font-black leading-tight italic w-[55px] shrink-0 tabular-nums">
|
||||||
|
<span className="text-[#ff9800]">{time}</span>
|
||||||
|
<span className="text-white/40">{period}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center min-w-0 flex-1 gap-2">
|
||||||
|
<span className="text-[11.5px] font-black text-white truncate italic uppercase">
|
||||||
|
{event.homeTeam} <span className="text-[#ff9800] mx-1 tabular-nums">{score}</span> {event.awayTeam}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 shrink-0 px-1 opacity-20 group-hover:opacity-100 transition-opacity">
|
||||||
|
<BarChart2 className="size-3.5 text-white" />
|
||||||
|
<Monitor className="size-3.5 text-white" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Odds Grid or Placeholder */}
|
||||||
|
<div className="flex-1 h-full flex items-center">
|
||||||
|
{isNoOdds ? (
|
||||||
|
<div className="flex-1 h-full bg-[#161616] flex items-center justify-center text-[10.5px] font-black text-white/20 uppercase italic tracking-tight">
|
||||||
|
Sorry, no odds for this match
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex-1 grid grid-cols-3 h-full">
|
||||||
|
{event.markets.slice(0, 3).map((m, idx) => {
|
||||||
|
const labels = ["Home", "Draw", "Away"]
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={m.id}
|
||||||
|
onClick={() => addBet({
|
||||||
|
id: `${event.id}-${m.id}`,
|
||||||
|
event: `${event.homeTeam} vs ${event.awayTeam}`,
|
||||||
|
league: event.league,
|
||||||
|
market: "1X2",
|
||||||
|
selection: `${m.label}`,
|
||||||
|
odds: m.odds,
|
||||||
|
})}
|
||||||
|
className="bg-[#1a1a1a] hover:bg-[#2a2a2a] flex items-center justify-between px-4 h-full border-r border-white/5 transition-colors group/btn"
|
||||||
|
>
|
||||||
|
<span className="text-[10px] font-black text-white/40 group-hover/btn:text-white uppercase">{labels[idx]}</span>
|
||||||
|
<span className="text-[11px] font-black text-[#ff9800] tabular-nums">{m.odds.toFixed(2)}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right Indicator */}
|
||||||
|
<div className="w-[4px] bg-[#ff9800] h-full" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const liveSports = [
|
||||||
|
{ id: "soccer", label: "Soccer", icon: "⚽", count: 25, active: true },
|
||||||
|
{ id: "basketball", label: "Basketball", icon: "🏀", count: 39 },
|
||||||
|
{ id: "ice-hockey", label: "Ice Hockey", icon: "🏒", count: 3 },
|
||||||
|
{ id: "tennis", label: "Tennis", icon: "🎾", count: 4 },
|
||||||
|
{ id: "handball", label: "Handball", icon: "🤾", count: 10 },
|
||||||
|
{ id: "rugby", label: "Rugby", icon: "🏉", count: 2 },
|
||||||
|
{ id: "table-tennis", label: "Table Tennis", icon: "🏓", count: 8 },
|
||||||
|
{ id: "volleyball", label: "Volleyball", icon: "🏐", count: 7 },
|
||||||
|
{ id: "futsal", label: "Futsal", icon: "⚽", count: 2 },
|
||||||
|
{ id: "esport-counter-strike", label: "ESport Cou...", icon: "🎮", count: 2 },
|
||||||
|
{ id: "esport-league-of-legends", label: "ESport Lea...", icon: "🎮", count: 1 },
|
||||||
|
{ id: "esport-dota-2", label: "ESport Dota", icon: "🎮", count: 1 },
|
||||||
|
{ id: "efootball", label: "eFootball", icon: "⚽", count: 4 },
|
||||||
|
{ id: "ebasketball", label: "eBasketball", icon: "🏀", count: 1 },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function LiveEventsList() {
|
||||||
|
// Enhanced mock data local to live view to match screenshot exactly
|
||||||
|
const liveMatches = [
|
||||||
|
{
|
||||||
|
league: "Algeria - Ligue 1",
|
||||||
|
flag: "https://flagcdn.com/w20/dz.png",
|
||||||
|
matches: [
|
||||||
|
{ ...mockEvents[0], id: "l1", homeTeam: "Paradou AC", awayTeam: "Ben Aknoun", homeScore: 3, awayScore: 5, liveMinute: 91, noOdds: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
league: "Australia - U23 Victoria NPL",
|
||||||
|
flag: "https://flagcdn.com/w20/au.png",
|
||||||
|
matches: [
|
||||||
|
{ ...mockEvents[1], id: "l2", homeTeam: "Oakleigh Cannons FC", awayTeam: "Altona Magic SC", homeScore: 5, awayScore: 1, liveMinute: 87, noOdds: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
league: "Australia - U23 Victoria Premier League 1",
|
||||||
|
flag: "https://flagcdn.com/w20/au.png",
|
||||||
|
matches: [
|
||||||
|
{ ...mockEvents[2], id: "l3", homeTeam: "Northcote City FC", awayTeam: "Western United FC", homeScore: 4, awayScore: 0, liveMinute: 83, noOdds: false },
|
||||||
|
{ ...mockEvents[3], id: "l4", homeTeam: "Melbourne Knights FC", awayTeam: "Melbourne Victory FC", homeScore: 0, awayScore: 3, liveMinute: 81, noOdds: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
league: "Australia - Victoria NPL, Women",
|
||||||
|
flag: "https://flagcdn.com/w20/au.png",
|
||||||
|
matches: [
|
||||||
|
{ ...mockEvents[4], id: "l5", homeTeam: "Preston Lions FC", awayTeam: "South Melbourne FC", homeScore: 1, awayScore: 1, liveMinute: 52, noOdds: true },
|
||||||
|
{ ...mockEvents[0], id: "l6", homeTeam: "Bentleigh Greens SC", awayTeam: "Box Hill United", homeScore: 0, awayScore: 6, liveMinute: 83, noOdds: true }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col min-h-screen bg-[#111]">
|
||||||
|
{/* Sport Navigation Carousel */}
|
||||||
|
<div className="bg-[#1a1a1a] border-b border-border/20 px-2 flex items-center h-[54px] overflow-x-auto scrollbar-hide">
|
||||||
|
<div className="flex items-center gap-0 h-full">
|
||||||
|
{/* Favourites & Prematch */}
|
||||||
|
<button className="flex flex-col items-center justify-center px-4 h-full border-r border-white/5 min-w-[70px]">
|
||||||
|
<span className="text-[14px]">⭐</span>
|
||||||
|
<span className="text-[9px] font-bold text-white/40 uppercase mt-0.5">Favourites</span>
|
||||||
|
</button>
|
||||||
|
<button className="flex flex-col items-center justify-center px-4 h-full border-r border-white/5 min-w-[70px]">
|
||||||
|
<span className="text-[14px]">⏱️</span>
|
||||||
|
<span className="text-[9px] font-bold text-white/40 uppercase mt-0.5">Prematch</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Live Sports */}
|
||||||
|
{liveSports.map((sport) => (
|
||||||
|
<button
|
||||||
|
key={sport.id}
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col items-center justify-center px-3 h-full border-r border-white/5 min-w-[75px] relative transition-colors",
|
||||||
|
sport.active ? "bg-black/20" : "hover:bg-white/5"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="absolute top-1 right-2 text-[8.5px] font-black text-white/40">{sport.count}</span>
|
||||||
|
<span className="text-[16px]">{sport.icon}</span>
|
||||||
|
<span className={cn(
|
||||||
|
"text-[9px] font-bold uppercase mt-1 tracking-tighter whitespace-nowrap",
|
||||||
|
sport.active ? "text-[#ff9800]" : "text-white/40"
|
||||||
|
)}>
|
||||||
|
{sport.label}
|
||||||
|
</span>
|
||||||
|
{sport.active && (
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 h-[2px] bg-[#ff9800]" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Category Header (Soccer) */}
|
||||||
|
<div className="bg-[#009688] px-3 py-1.5 flex items-center gap-2 border-l-[4px] border-[#ff9800]">
|
||||||
|
<span className="text-[16px]">⚽</span>
|
||||||
|
<h2 className="text-[14px] font-black text-white uppercase tracking-tight">Soccer</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Grouped Live Matches */}
|
||||||
|
<div className="flex flex-col mb-10">
|
||||||
|
{liveMatches.map((group, gIdx) => (
|
||||||
|
<div key={gIdx} className="flex flex-col">
|
||||||
|
{/* League Header */}
|
||||||
|
<div className="bg-[#1a1a1a] px-3 py-1 border-b border-border/10 flex items-center gap-2">
|
||||||
|
<img src={group.flag} width="14" alt={group.league} className="rounded-sm opacity-60" />
|
||||||
|
<span className="text-[9.5px] font-black text-white/60 uppercase tracking-widest leading-none">
|
||||||
|
{group.league}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Matches in this league */}
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{group.matches.map((match, mIdx) => (
|
||||||
|
<LiveEventRow key={match.id} event={match as any} isNoOdds={match.noOdds} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,32 @@
|
||||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
"use client"
|
||||||
|
|
||||||
const filters = ["All", "Today", "3h", "6h", "9h", "12h"]
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
export function QuickFilterBar() {
|
const filters = ["All", "Live", "Today", "3h", "6h", "12h"]
|
||||||
|
|
||||||
|
export function QuickFilterBar({ active, onChange }: {
|
||||||
|
active: string
|
||||||
|
onChange: (f: string) => void
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="All" className="w-full">
|
<div className="flex items-center gap-1 overflow-x-auto pb-0.5">
|
||||||
<TabsList className="h-8">
|
{filters.map((filter) => (
|
||||||
{filters.map((filter) => (
|
<button
|
||||||
<TabsTrigger key={filter} value={filter} className="px-3 text-xs">
|
key={filter}
|
||||||
{filter}
|
onClick={() => onChange(filter)}
|
||||||
</TabsTrigger>
|
className={cn(
|
||||||
))}
|
"shrink-0 px-3 py-1 rounded text-[11px] font-semibold transition-colors",
|
||||||
</TabsList>
|
active === filter
|
||||||
</Tabs>
|
? "bg-primary text-primary-foreground"
|
||||||
|
: "bg-secondary text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{filter}
|
||||||
|
{filter === "Live" && (
|
||||||
|
<span className="ml-1 live-dot inline-block size-1.5 rounded-full bg-[var(--hs-live-red)] align-middle" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,28 +1,27 @@
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
"use client"
|
||||||
import { Input } from "@/components/ui/input"
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { useState } from "react"
|
||||||
|
|
||||||
export function ReloadTicket() {
|
export function ReloadTicket() {
|
||||||
return (
|
const [code, setCode] = useState("")
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-sm">Reload Ticket</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-2 text-xs">
|
|
||||||
<div className="text-[11px] text-muted-foreground">
|
|
||||||
Insert the code to load
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Input
|
|
||||||
placeholder="Ticket code"
|
|
||||||
className="h-8 text-xs placeholder:text-[11px]"
|
|
||||||
/>
|
|
||||||
<Button size="sm" className="text-xs">
|
|
||||||
Reload
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-transparent border-t border-border/20 pt-4 pb-2">
|
||||||
|
<div className="px-1 text-center space-y-3">
|
||||||
|
<h3 className="text-[12px] font-bold uppercase text-[#ff9800]">Reload Ticket</h3>
|
||||||
|
<p className="text-[11px] text-white">Insert the code to load</p>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={code}
|
||||||
|
onChange={(e) => setCode(e.target.value)}
|
||||||
|
className="flex-1 bg-[#121212] border border-border/40 px-2 py-2 text-[11px] text-white outline-none focus:border-primary"
|
||||||
|
/>
|
||||||
|
<button className="bg-[#ff9800] text-black px-2 py-1.5 flex items-center justify-center min-w-[32px] hover:bg-[#ffa726] transition-colors">
|
||||||
|
<svg viewBox="0 0 24 24" className="size-5 fill-current"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
import { Input } from "@/components/ui/input"
|
"use client"
|
||||||
|
|
||||||
export function SearchEvent() {
|
import { Search } from "lucide-react"
|
||||||
|
|
||||||
|
export function SearchEvent({ value, onChange }: {
|
||||||
|
value: string
|
||||||
|
onChange: (v: string) => void
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-1 rounded-md border bg-card/60 p-3">
|
<div className="relative">
|
||||||
<div className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
|
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground" />
|
||||||
Search Event
|
<input
|
||||||
</div>
|
type="text"
|
||||||
<p className="text-[11px] text-muted-foreground">
|
value={value}
|
||||||
Insert the events name or at least one team in the form below
|
onChange={(e) => onChange(e.target.value)}
|
||||||
</p>
|
placeholder="Search by event, team or league..."
|
||||||
<Input
|
className="w-full bg-secondary border border-border rounded pl-8 pr-3 py-2 text-[12px] text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary transition-colors"
|
||||||
placeholder="Search by event or team"
|
|
||||||
className="h-8 text-xs placeholder:text-[11px]"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
27
components/betting/sport-home.tsx
Normal file
27
components/betting/sport-home.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useSearchParams } from "next/navigation"
|
||||||
|
import { HeroBanner } from "./hero-banner"
|
||||||
|
import { TopMatches } from "./top-matches"
|
||||||
|
import { SportsNav } from "./sports-nav"
|
||||||
|
import { HomeTabs } from "./home-tabs"
|
||||||
|
import { EventsList } from "./events-list"
|
||||||
|
|
||||||
|
export function SportHome() {
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
const isLeagueView = !!searchParams.get("league")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
{!isLeagueView && (
|
||||||
|
<>
|
||||||
|
<HeroBanner />
|
||||||
|
<TopMatches />
|
||||||
|
<SportsNav />
|
||||||
|
<HomeTabs />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<EventsList />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
33
components/betting/sports-nav.tsx
Normal file
33
components/betting/sports-nav.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||||
|
|
||||||
|
const sports = [
|
||||||
|
{ id: "football", name: "Football", icon: "⚽" },
|
||||||
|
{ id: "tennis", name: "Tennis", icon: "🎾" },
|
||||||
|
{ id: "basketball", name: "Basketball", icon: "🏀" },
|
||||||
|
{ id: "ice-hockey", name: "Ice Hockey", icon: "🏒" },
|
||||||
|
{ id: "mma", name: "MMA", icon: "🥋" },
|
||||||
|
{ id: "handball", name: "Handball", icon: "🤾" },
|
||||||
|
{ id: "darts", name: "Darts", icon: "🎯" },
|
||||||
|
{ id: "snooker", name: "Snooker", icon: "🎱" },
|
||||||
|
{ id: "cricket", name: "Cricket", icon: "🏏" },
|
||||||
|
{ id: "dota2", name: "Dota 2", icon: "🎮" },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function SportsNav() {
|
||||||
|
return (
|
||||||
|
<Tabs defaultValue="football" className="w-full">
|
||||||
|
<TabsList variant="hs-nav" className="h-auto">
|
||||||
|
{sports.map((sport) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={sport.id}
|
||||||
|
value={sport.id}
|
||||||
|
className="flex-col min-w-[70px] py-2 gap-1"
|
||||||
|
>
|
||||||
|
<span className="text-xl">{sport.icon}</span>
|
||||||
|
<span className="text-[10px] font-bold uppercase">{sport.name}</span>
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</Tabs>
|
||||||
|
)
|
||||||
|
}
|
||||||
102
components/betting/top-matches.tsx
Normal file
102
components/betting/top-matches.tsx
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { ChevronRight } from "lucide-react"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
|
const topMatches = [
|
||||||
|
{
|
||||||
|
id: "tm1",
|
||||||
|
league: "England - Premier League",
|
||||||
|
time: "05:00 PM",
|
||||||
|
homeTeam: "Nottingham Forest",
|
||||||
|
awayTeam: "Liverpool",
|
||||||
|
odds: { home: 4.09, draw: 3.93, away: 1.82 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tm2",
|
||||||
|
league: "England - Premier League",
|
||||||
|
time: "11:00 PM",
|
||||||
|
homeTeam: "Man City",
|
||||||
|
awayTeam: "Newcastle",
|
||||||
|
odds: { home: 1.50, draw: 5.17, away: 5.93 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tm3",
|
||||||
|
league: "England - Premier League",
|
||||||
|
time: "06:00 PM",
|
||||||
|
homeTeam: "Chelsea",
|
||||||
|
awayTeam: "Burnley",
|
||||||
|
odds: { home: 1.21, draw: 6.91, away: 11.50 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tm4",
|
||||||
|
league: "Spain - LaLiga",
|
||||||
|
time: "07:30 PM",
|
||||||
|
homeTeam: "Arsenal",
|
||||||
|
awayTeam: "Wolves",
|
||||||
|
odds: { home: 1.56, draw: 4.16, away: 5.80 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tm5",
|
||||||
|
league: "Italy - Serie A",
|
||||||
|
time: "09:45 PM",
|
||||||
|
homeTeam: "Inter Milan",
|
||||||
|
awayTeam: "Napoli",
|
||||||
|
odds: { home: 1.85, draw: 3.60, away: 4.20 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export function TopMatches() {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-3 overflow-x-auto pb-2 scrollbar-hide -mx-1 px-1">
|
||||||
|
{topMatches.map((match) => (
|
||||||
|
<div
|
||||||
|
key={match.id}
|
||||||
|
className="min-w-[280px] bg-[#222] border border-border/20 rounded-sm overflow-hidden flex flex-col relative group"
|
||||||
|
>
|
||||||
|
{/* Top Label Ribbon */}
|
||||||
|
<div className="absolute top-0 right-0 w-12 h-12 overflow-hidden z-10 pointer-events-none">
|
||||||
|
<div className="absolute top-[6px] right-[-14px] bg-[#ff9800] text-black text-[8px] font-black py-0.5 px-6 rotate-45 shadow-sm uppercase tracking-tighter">
|
||||||
|
TOP
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-[#1a1a1a] px-3 py-1.5 flex items-center justify-between text-[10px] text-muted-foreground border-b border-border/10">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className="font-bold text-[#ff9800]">{match.league}</span>
|
||||||
|
<span className="font-black text-white ml-1 italic">{match.time}</span>
|
||||||
|
</div>
|
||||||
|
<ChevronRight className="size-3 text-white/20 mr-4" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-3 flex flex-col gap-3">
|
||||||
|
<div className="flex items-center justify-between gap-1">
|
||||||
|
<div className="flex items-center gap-2 flex-1 min-w-0">
|
||||||
|
<div className="size-4 shrink-0 bg-white/5 rounded-sm flex items-center justify-center text-[9px] border border-white/10">⚽</div>
|
||||||
|
<span className="text-[11px] font-black text-white truncate uppercase italic">{match.homeTeam}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-[#ff9800] text-[9.5px] font-black italic shrink-0 px-2 opacity-60">VS</span>
|
||||||
|
<div className="flex items-center gap-2 flex-1 min-w-0 justify-end">
|
||||||
|
<span className="text-[11px] font-black text-white truncate uppercase italic text-right">{match.awayTeam}</span>
|
||||||
|
<div className="size-4 shrink-0 bg-white/5 rounded-sm flex items-center justify-center text-[9px] border border-white/10">⚽</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-3 gap-[1px] bg-white/5 mx-2 mb-2 rounded-sm overflow-hidden border border-white/5">
|
||||||
|
<button className="bg-[#1a1a1a] hover:bg-[#333] flex items-center justify-between px-2.5 py-1.5 transition-colors group/btn">
|
||||||
|
<span className="text-[10px] font-black text-white/40 group-hover/btn:text-white">1</span>
|
||||||
|
<span className="text-[11px] font-black text-[#ff9800] tabular-nums">{match.odds.home.toFixed(2)}</span>
|
||||||
|
</button>
|
||||||
|
<button className="bg-[#1a1a1a] hover:bg-[#333] flex items-center justify-between px-2.5 py-1.5 transition-colors border-x border-white/5 group/btn">
|
||||||
|
<span className="text-[10px] font-black text-white/40 group-hover/btn:text-white">X</span>
|
||||||
|
<span className="text-[11px] font-black text-[#ff9800] tabular-nums">{match.odds.draw.toFixed(2)}</span>
|
||||||
|
</button>
|
||||||
|
<button className="bg-[#1a1a1a] hover:bg-[#333] flex items-center justify-between px-2.5 py-1.5 transition-colors group/btn">
|
||||||
|
<span className="text-[10px] font-black text-white/40 group-hover/btn:text-white">2</span>
|
||||||
|
<span className="text-[11px] font-black text-[#ff9800] tabular-nums">{match.odds.away.toFixed(2)}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,32 +1,164 @@
|
||||||
const sports = [
|
"use client"
|
||||||
"Soccer",
|
|
||||||
"Basketball",
|
import { useState } from "react"
|
||||||
"Tennis",
|
import Link from "next/link"
|
||||||
"Ice Hockey",
|
import { popularLeagues } from "@/lib/mock-data"
|
||||||
"Volleyball",
|
import { cn } from "@/lib/utils"
|
||||||
"Handball",
|
import { Button } from "@/components/ui/button"
|
||||||
"Baseball",
|
|
||||||
"American Football",
|
const sportCategories = [
|
||||||
"Cricket",
|
{ id: "football", name: "Football", icon: "⚽", count: 1412 },
|
||||||
"Rugby",
|
{ id: "tennis", name: "Tennis", icon: "🎾", count: 67 },
|
||||||
|
{ id: "basketball", name: "Basketball", icon: "🏀", count: 255 },
|
||||||
|
{ id: "ice-hockey", name: "Ice Hockey", icon: "🏒", count: 238 },
|
||||||
|
{ id: "mma", name: "MMA", icon: "🥊", count: 51 },
|
||||||
|
{ id: "handball", name: "Handball", icon: "🤾", count: 92 },
|
||||||
|
{ id: "darts", name: "Darts", icon: "🎯", count: 25 },
|
||||||
|
{ id: "snooker", name: "Snooker", icon: "🎱", count: 3 },
|
||||||
|
{ id: "cricket", name: "Cricket", icon: "🏏", count: 42 },
|
||||||
|
{ id: "dota2", name: "Dota 2", icon: "🎮", count: 2 },
|
||||||
|
{ id: "rugby", name: "Rugby", icon: "🏉", count: 41 },
|
||||||
|
{ id: "volleyball", name: "Volleyball", icon: "🏐", count: 69 },
|
||||||
]
|
]
|
||||||
|
|
||||||
export function SportsSidebar() {
|
export function SportsSidebar() {
|
||||||
return (
|
const [activeSport, setActiveSport] = useState("football")
|
||||||
<aside className="hidden h-full w-52 shrink-0 border-r bg-sidebar px-3 py-3 text-sm text-sidebar-foreground lg:block">
|
|
||||||
<div className="mb-3 font-semibold uppercase tracking-wide text-xs text-muted-foreground">
|
|
||||||
Sports Menu
|
|
||||||
</div>
|
|
||||||
<ul className="space-y-1">
|
|
||||||
{sports.map((sport) => (
|
|
||||||
<li key={sport}>
|
|
||||||
<button className="w-full rounded px-2 py-1 text-left text-xs text-muted-foreground hover:bg-muted hover:text-foreground">
|
|
||||||
{sport}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</aside>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className="hidden h-full w-[240px] shrink-0 bg-[#262626] lg:block overflow-y-auto border-r border-border/40 scrollbar-hide">
|
||||||
|
{/* Sports Menu Header */}
|
||||||
|
<div className="bg-[#1a1a1a] px-3 py-2 text-[11px] font-black text-[#ff9800] uppercase tracking-tighter flex items-center justify-between border-b border-border/30">
|
||||||
|
Sports Menu
|
||||||
|
<svg viewBox="0 0 24 24" className="size-3.5 fill-current opacity-60"><path d="M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z"/></svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Top Leagues Header */}
|
||||||
|
<div className="bg-[#262626] px-3 py-2.5 text-[11px] font-black text-[#ff9800] uppercase text-center border-b border-border/20 flex items-center justify-center gap-2">
|
||||||
|
Top Leagues
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Popular Leagues */}
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{popularLeagues.map((league) => (
|
||||||
|
<Link
|
||||||
|
key={league.id}
|
||||||
|
href={`/?league=${league.id}`}
|
||||||
|
className="w-full flex items-center justify-between px-3 py-2 text-left text-white/90 hover:bg-[#333] transition-colors border-b border-border/10 group h-9"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2.5 min-w-0">
|
||||||
|
<div className="size-4 shrink-0 overflow-hidden rounded-sm flex items-center justify-center bg-white/10 group-hover:bg-white/20 transition-colors">
|
||||||
|
{league.logo ? (
|
||||||
|
<img src={league.logo} alt="" className="size-full object-contain" />
|
||||||
|
) : (
|
||||||
|
<span className="text-[10px] leading-none invert">⚽</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="text-[10.5px] font-bold leading-tight truncate max-w-[150px]">{league.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="size-3.5 rounded-full border border-white/20 flex items-center justify-center group-hover:border-[#ff9800] transition-colors shrink-0">
|
||||||
|
<div className="size-1.5 bg-white/40 rounded-full group-hover:bg-[#ff9800]" />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* In-Play Strip */}
|
||||||
|
<Button className="w-full bg-[#004d40] text-[#00bfa5] hover:bg-[#003d33] border-none py-2.5 h-auto text-[11px] font-black uppercase rounded-none tracking-[2px]">
|
||||||
|
<span className="size-2 rounded-full bg-[#ff9800] mr-2 live-dot shadow-[0_0_8px_#ff9800]"></span>
|
||||||
|
IN-PLAY
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* Quick Filter Section */}
|
||||||
|
<div className="bg-[#1a1a1a] p-3 border-b border-border/30">
|
||||||
|
<span className="text-[#ff9800] text-[10.5px] uppercase font-black block mb-2 tracking-tight">Quick Filter</span>
|
||||||
|
<div className="grid grid-cols-6 gap-[1px]">
|
||||||
|
{["All", "Today", "3h", "6h", "9h", "12h"].map((t) => (
|
||||||
|
<button key={t} className={cn(
|
||||||
|
"text-[10px] py-1.5 font-bold transition-colors",
|
||||||
|
t === "All" ? "bg-[#333] text-white" : "bg-[#2a2a2a] text-white/50 hover:text-white"
|
||||||
|
)}>{t}</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Search Event Section */}
|
||||||
|
<div className="bg-[#1a1a1a] p-3 border-b border-border/40">
|
||||||
|
<span className="text-[#ff9800] text-[10.5px] uppercase font-black block mb-1 tracking-tight">Search Event</span>
|
||||||
|
<p className="text-[9px] text-white/30 mb-2 leading-tight font-medium uppercase">Insert the events name or at least one team in the form below</p>
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<div className="relative">
|
||||||
|
<input type="text" className="bg-[#2a2a2a] border-none text-white text-[11px] px-3 py-2 w-full focus:ring-0 placeholder:text-white/20" placeholder="Search" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-[#ff9800] text-black hover:bg-[#e68900] py-2 h-auto rounded-none text-[11px] font-black uppercase tracking-wider">Search</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sport categories */}
|
||||||
|
<div className="divide-y divide-border/10 bg-[#262626]">
|
||||||
|
{sportCategories.map((sport) => (
|
||||||
|
<button
|
||||||
|
key={sport.id}
|
||||||
|
onClick={() => setActiveSport(sport.id)}
|
||||||
|
className={cn(
|
||||||
|
"w-full flex items-center justify-between px-3 py-2 text-left transition-colors border-b border-border/10 h-9",
|
||||||
|
activeSport === sport.id
|
||||||
|
? "bg-[#333] text-white"
|
||||||
|
: "text-white/70 hover:bg-[#333] hover:text-white"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-[12px] opacity-80 shrink-0">{sport.icon}</span>
|
||||||
|
<span className="text-[10.5px] font-bold tracking-tight">{sport.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-[10px] font-bold text-white/40">{sport.count}</span>
|
||||||
|
<span className="text-[11px] text-white/20">⚽</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bet Services */}
|
||||||
|
<div className="mt-2 text-[11px] font-bold text-[#ff9800] px-3 py-2 uppercase border-y border-border/20 bg-[#1a1a1a]">
|
||||||
|
Bet Services
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-3 gap-0 border-b border-border/10">
|
||||||
|
<Button variant="ghost" className="flex flex-col items-center justify-center py-4 h-auto rounded-none border-r border-border/10 hover:bg-[#333] group">
|
||||||
|
<svg viewBox="0 0 24 24" className="size-5 mb-1.5 fill-muted-foreground group-hover:fill-[#ff9800] transition-colors"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm3.3 14.71L11 12.41V7h2v4.59l3.71 3.71-1.42 1.41z"/></svg>
|
||||||
|
<span className="text-[9px] text-white font-medium">Live Score</span>
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" className="flex flex-col items-center justify-center py-4 h-auto rounded-none border-r border-border/10 hover:bg-[#333] group">
|
||||||
|
<svg viewBox="0 0 24 24" className="size-5 mb-1.5 fill-muted-foreground group-hover:fill-[#ff9800] transition-colors"><path d="M5 9.2h3V19H5zM10.6 5h2.8v14h-2.8zm5.6 8H19v6h-2.8z"/></svg>
|
||||||
|
<span className="text-[9px] text-white font-medium">Results</span>
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" className="flex flex-col items-center justify-center py-4 h-auto rounded-none hover:bg-[#333] group">
|
||||||
|
<svg viewBox="0 0 24 24" className="size-5 mb-1.5 fill-muted-foreground group-hover:fill-[#ff9800] transition-colors"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/></svg>
|
||||||
|
<span className="text-[9px] text-white font-medium">Print Odds</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-2 space-y-1">
|
||||||
|
<Button className="w-full bg-[#004d40] text-[#00bfa5] hover:bg-[#003d33] border-none py-2 h-auto text-[11px] font-bold uppercase rounded-none tracking-widest">
|
||||||
|
<span className="size-2 rounded-full bg-[#ff9800] mr-2 live-dot"></span>
|
||||||
|
IN-PLAY
|
||||||
|
</Button>
|
||||||
|
<div className="bg-[#1a1a1a] p-3 border border-border/10">
|
||||||
|
<span className="text-[#ff9800] text-[10px] uppercase font-bold block mb-2">Quick Filter</span>
|
||||||
|
<div className="grid grid-cols-6 gap-0.5">
|
||||||
|
{["All", "Today", "3h", "6h", "9h", "12h"].map((t) => (
|
||||||
|
<button key={t} className="text-[9px] py-1 bg-[#2a2a2a] text-white/70 hover:bg-[#333] transition-colors">{t}</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-[#1a1a1a] p-3 border border-border/10">
|
||||||
|
<span className="text-[#ff9800] text-[10px] uppercase font-bold block mb-2">Search Event</span>
|
||||||
|
<p className="text-[9px] text-white/40 mb-2 leading-tight">Insert the events name or at least one team in the form below</p>
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<input type="text" className="bg-[#2a2a2a] border-none text-white text-[10px] px-2 py-1.5 focus:ring-0" placeholder="Search" />
|
||||||
|
<Button className="bg-[#333] text-white hover:bg-[#444] py-1.5 h-auto rounded-none text-[10px] font-bold uppercase">Search</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user