207 lines
9.5 KiB
TypeScript
207 lines
9.5 KiB
TypeScript
"use client"
|
||
|
||
import { useState } from "react"
|
||
import { useBetslipStore } from "@/lib/store/betslip-store"
|
||
import { X, Save, Trash2 } from "lucide-react"
|
||
import { cn } from "@/lib/utils"
|
||
|
||
const quickStakes = [10, 50, 100, 1000, 2000, 5000]
|
||
|
||
export function Betslip() {
|
||
const { bets, removeBet, clearBets, updateStake, getPotentialWin, getTotalOdds } = useBetslipStore()
|
||
const [globalStake, setGlobalStake] = useState("10")
|
||
|
||
const totalOdds = getTotalOdds()
|
||
const isMulti = bets.length > 1
|
||
const potentialWin = isMulti
|
||
? Number(globalStake) * totalOdds
|
||
: getPotentialWin()
|
||
|
||
return (
|
||
<div className="overflow-hidden">
|
||
<div className="space-y-3">
|
||
{bets.length === 0 ? (
|
||
<div className="py-10 px-4 text-center">
|
||
<p className="text-[11px] text-muted-foreground font-medium leading-relaxed">
|
||
No bet has been selected. To select a bet, please click on the respective odds
|
||
</p>
|
||
</div>
|
||
) : (
|
||
<>
|
||
{/* Bet cards */}
|
||
<div className="space-y-2">
|
||
{bets.map((bet) => (
|
||
<div key={bet.id} className="bg-brand-surface-light border border-border/30 p-2.5">
|
||
<div className="flex items-start justify-between gap-2">
|
||
<div className="flex-1 min-w-0">
|
||
<div className="text-[11px] font-bold text-white uppercase leading-tight truncate">
|
||
{bet.event}
|
||
</div>
|
||
<div className="text-[10px] text-white/50 uppercase mt-0.5 truncate">
|
||
{bet.league}
|
||
</div>
|
||
<div className="flex items-center justify-between gap-2 mt-1.5">
|
||
<span className="text-[10px] text-white/80">
|
||
{bet.market}: <span className="font-bold text-white">{bet.selection}</span>
|
||
</span>
|
||
<span className="text-[11px] font-bold text-brand-primary shrink-0">{bet.odds.toFixed(2)}</span>
|
||
</div>
|
||
{!isMulti && (
|
||
<div className="flex justify-between text-[10px] text-white/50 mt-0.5">
|
||
<span>Odds</span>
|
||
<span className="text-brand-primary font-bold">{bet.odds.toFixed(2)}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<button
|
||
onClick={() => removeBet(bet.id)}
|
||
className="text-white/50 hover:text-white transition-colors shrink-0 p-0.5"
|
||
aria-label="Remove bet"
|
||
>
|
||
<X className="size-3.5" />
|
||
</button>
|
||
</div>
|
||
|
||
{/* Single bet: stake on card */}
|
||
{!isMulti && (
|
||
<div className="mt-3 pt-3 border-t border-border/20">
|
||
<div className="flex items-center gap-1 mb-2">
|
||
<button
|
||
type="button"
|
||
onClick={() => updateStake(bet.id, Math.max(1, (bet.stake ?? 10) - 1))}
|
||
className="w-7 h-7 flex items-center justify-center rounded bg-white/10 text-white text-sm font-bold hover:bg-white/20"
|
||
>
|
||
−
|
||
</button>
|
||
<input
|
||
type="number"
|
||
value={bet.stake ?? 10}
|
||
onChange={(e) => updateStake(bet.id, Number(e.target.value) || 10)}
|
||
className="flex-1 h-7 bg-brand-bg border border-border/40 px-2 text-[11px] text-white text-center font-bold focus:outline-none focus:border-brand-primary"
|
||
min="1"
|
||
/>
|
||
<button
|
||
type="button"
|
||
onClick={() => updateStake(bet.id, (bet.stake ?? 10) + 1)}
|
||
className="w-7 h-7 flex items-center justify-center rounded bg-white/10 text-white text-sm font-bold hover:bg-white/20"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
<div className="flex flex-wrap gap-1.5">
|
||
{quickStakes.map((s) => (
|
||
<button
|
||
key={s}
|
||
onClick={() => updateStake(bet.id, s)}
|
||
className={cn(
|
||
"size-7 rounded-full text-[10px] font-bold transition-colors",
|
||
(bet.stake ?? 10) === s
|
||
? "bg-brand-primary text-black"
|
||
: "bg-brand-primary/20 text-brand-primary hover:bg-brand-primary/30"
|
||
)}
|
||
>
|
||
{s}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Single bet: potential winning below card */}
|
||
{!isMulti && (
|
||
<div className="flex justify-between items-center text-[11px] font-bold text-white">
|
||
<span className="text-white/70">Potential winning</span>
|
||
<span>{potentialWin.toFixed(2)} ETB</span>
|
||
</div>
|
||
)}
|
||
|
||
{/* Multiple bets: combined Odds, one stake, quick buttons, potential winning */}
|
||
{isMulti && (
|
||
<div className="bg-brand-surface-light border border-border/30 p-2.5 space-y-2.5">
|
||
<div className="flex justify-between text-[11px] font-bold text-white">
|
||
<span className="text-white/70">Odds</span>
|
||
<span className="text-brand-primary">{totalOdds.toFixed(2)}</span>
|
||
</div>
|
||
<div className="flex items-center gap-1">
|
||
<button
|
||
type="button"
|
||
onClick={() => setGlobalStake(String(Math.max(1, Number(globalStake) - 1)))}
|
||
className="w-7 h-7 flex items-center justify-center rounded bg-white/10 text-white text-sm font-bold hover:bg-white/20"
|
||
>
|
||
−
|
||
</button>
|
||
<input
|
||
type="number"
|
||
value={globalStake}
|
||
onChange={(e) => setGlobalStake(e.target.value)}
|
||
className="flex-1 h-7 bg-brand-bg border border-border/40 px-2 text-[11px] text-white text-center font-bold focus:outline-none focus:border-brand-primary"
|
||
min="1"
|
||
/>
|
||
<button
|
||
type="button"
|
||
onClick={() => setGlobalStake(String(Number(globalStake) + 1))}
|
||
className="w-7 h-7 flex items-center justify-center rounded bg-white/10 text-white text-sm font-bold hover:bg-white/20"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
<div className="flex flex-wrap gap-1.5">
|
||
{quickStakes.map((s) => (
|
||
<button
|
||
key={s}
|
||
onClick={() => setGlobalStake(String(s))}
|
||
className={cn(
|
||
"size-7 rounded-full text-[10px] font-bold transition-colors",
|
||
Number(globalStake) === s
|
||
? "bg-brand-primary text-black"
|
||
: "bg-brand-primary/20 text-brand-primary hover:bg-brand-primary/30"
|
||
)}
|
||
>
|
||
{s}
|
||
</button>
|
||
))}
|
||
</div>
|
||
<div className="flex justify-between text-[11px] font-bold text-white pt-0.5">
|
||
<span className="text-white/70">Potential winning</span>
|
||
<span className="text-brand-primary">{(Number(globalStake) * totalOdds).toFixed(2)} ETB</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="flex items-center gap-2 py-2 px-1 text-[10px] text-brand-primary/90">
|
||
<svg viewBox="0 0 24 24" className="size-4 shrink-0 fill-brand-primary" aria-hidden><path d="M12 2L1 21h22L12 2zm0 3.99L19.53 19H4.47L12 5.99zM11 10v4h2v-2h-2v-2zm0 6v2h2v-2h-2z"/></svg>
|
||
<span>You need to login to be able to place a bet.</span>
|
||
</div>
|
||
|
||
<div className="flex gap-2">
|
||
<button
|
||
type="button"
|
||
className="flex-1 flex items-center justify-center gap-1.5 py-2 px-3 rounded text-[11px] font-bold text-white bg-white/10 hover:bg-white/15 border border-border/40"
|
||
>
|
||
<Save className="size-3.5" />
|
||
Save
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={clearBets}
|
||
className="flex items-center justify-center gap-1.5 py-2 px-3 rounded text-[11px] font-bold text-white bg-red-600/80 hover:bg-red-600 border border-red-500/50"
|
||
>
|
||
<Trash2 className="size-3.5" />
|
||
Clear
|
||
</button>
|
||
<button
|
||
type="button"
|
||
className="flex-1 py-2 px-3 rounded text-[11px] font-bold text-white bg-green-600 hover:bg-green-500"
|
||
>
|
||
Bet
|
||
</button>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
} |