- 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.
187 lines
8.2 KiB
TypeScript
187 lines
8.2 KiB
TypeScript
"use client"
|
|
|
|
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() {
|
|
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 (
|
|
<div className="bg-card rounded border border-border overflow-hidden">
|
|
{/* Header */}
|
|
<div className="bg-primary px-3 py-2 flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<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 && (
|
|
<button
|
|
onClick={clearBets}
|
|
className="text-primary-foreground/70 hover:text-primary-foreground transition-colors"
|
|
>
|
|
<Trash2 className="size-3.5" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* 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 ? (
|
|
<div className="py-6 text-center">
|
|
<div className="text-2xl mb-2">🎯</div>
|
|
<p className="text-[11px] text-muted-foreground leading-relaxed">
|
|
No bets selected. Click on odds to add selections.
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<>
|
|
{/* Bet items */}
|
|
<div className="space-y-2">
|
|
{bets.map((bet) => (
|
|
<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-1 min-w-0">
|
|
<div className="text-[11px] font-semibold text-foreground truncate">{bet.event}</div>
|
|
<div className="text-[10px] text-muted-foreground truncate">{bet.league}</div>
|
|
<div className="text-[10px] text-primary font-medium mt-0.5">
|
|
{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>
|
|
|
|
{/* Stake input for single */}
|
|
{(activeTab === "single" || bets.length === 1) && (
|
|
<div>
|
|
<div className="text-[10px] text-muted-foreground mb-1">Stake (ETB)</div>
|
|
<div className="flex gap-1">
|
|
<input
|
|
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>
|
|
)}
|
|
|
|
{/* 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>
|
|
)
|
|
} |