Fortune-PlayLogic/components/betting/match-detail-view.tsx
brooktewabe 8941c45555 Refactor live events list and match detail view components
- Updated LiveEventsList to use live data from the live store and improved event rendering with links to event details.
- Enhanced MatchDetailView to accept API sections for dynamic market rendering and improved state management for expanded sections.
- Modified SportsNav to utilize search parameters for active sport highlighting.
- Refactored TopMatches to fetch live match data and odds from the API, replacing static fallback data.
- Improved UI elements for better responsiveness and user experience across components.
2026-03-02 19:08:52 +03:00

289 lines
10 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState } from "react"
import Link from "next/link"
import { useBetslipStore } from "@/lib/store/betslip-store"
import {
getEventDetailMarkets,
getCardsBookingsMarkets,
type Event,
type DetailMarketSection,
} from "@/lib/mock-data"
type ApiSection = { id: string; title: string; outcomes: { label: string; odds: number }[] }
import { cn } from "@/lib/utils"
import { ChevronDown, ChevronUp } from "lucide-react"
const MARKET_CATEGORIES = [
"Betbuilder",
"All",
"Main",
"Goals",
"Handicap",
"1st Half",
"2nd Half",
"Combo",
"Chance Mix",
"Home",
"Half Time / Full Time",
"Away",
"Correct Score",
"Asian Markets",
"Corners",
"Minutes",
"Cards/Bookings",
"Points Handicap",
"Total Points",
"Team 1",
"Team 2",
"Other",
"Handicap Goals",
"Total Goals",
"Combo",
"Specials",
]
function MarketSectionBlock({
section,
event,
marketName,
isExpanded,
onToggle,
}: {
section: DetailMarketSection
event: Event
marketName: string
isExpanded: boolean
onToggle: () => void
}) {
const { bets, addBet } = useBetslipStore()
const hasOutcomes = section.outcomes.length > 0
return (
<div className="bg-brand-surface-light border-b border-white/5">
<button
type="button"
onClick={onToggle}
className="w-full flex items-center justify-between px-3 py-2.5 text-left hover:bg-white/5 transition-colors"
>
<span className="text-[11px] font-bold text-white uppercase">
{section.title}
</span>
{isExpanded ? (
<ChevronUp className="size-4 text-white/60" />
) : (
<ChevronDown className="size-4 text-white/60" />
)}
</button>
{isExpanded && hasOutcomes && (
<div className="px-3 pb-3 space-y-1.5">
{section.outcomes.length > 2 && section.outcomes.length % 2 === 0 ? (
<div className="grid grid-cols-2 gap-x-4 gap-y-1.5">
{section.outcomes.map((outcome, i) => {
const oddsStr = typeof outcome.odds === "number" ? outcome.odds.toFixed(2) : String(outcome.odds)
const betId = `${event.id}-${section.id}-${i}-${outcome.label.replace(/\s/g, "-").toLowerCase()}-${oddsStr}`
const isSelected = bets.some((b) => b.id === betId)
return (
<div key={`${outcome.label}-${i}-${oddsStr}`} className="flex items-center justify-between gap-2">
<span className="text-[11px] text-white/90 truncate">{outcome.label}</span>
<button
type="button"
onClick={() =>
addBet({
id: betId,
event: `${event.homeTeam} - ${event.awayTeam}`,
league: `${event.sport} - ${event.country} - ${event.league}`,
market: marketName,
selection: outcome.label,
odds: outcome.odds,
})
}
className={cn(
"min-w-[52px] px-2 py-1 rounded text-[11px] font-bold tabular-nums text-center transition-all shrink-0",
isSelected ? "bg-brand-primary text-black" : "text-brand-primary hover:bg-white/5"
)}
>
{outcome.odds.toFixed(2)}
</button>
</div>
)
})}
</div>
) : (
section.outcomes.map((outcome, i) => {
const oddsStr = typeof outcome.odds === "number" ? outcome.odds.toFixed(2) : String(outcome.odds)
const betId = `${event.id}-${section.id}-${i}-${outcome.label.replace(/\s/g, "-").toLowerCase()}-${oddsStr}`
const isSelected = bets.some((b) => b.id === betId)
return (
<div
key={`${outcome.label}-${i}-${oddsStr}`}
className="flex items-center justify-between gap-3 py-1"
>
<span className="text-[11px] text-white/90">{outcome.label}</span>
<button
type="button"
onClick={() =>
addBet({
id: betId,
event: `${event.homeTeam} - ${event.awayTeam}`,
league: `${event.sport} - ${event.country} - ${event.league}`,
market: marketName,
selection: outcome.label,
odds: outcome.odds,
})
}
className={cn(
"min-w-[52px] px-2 py-1 rounded text-[11px] font-bold tabular-nums text-center transition-all shrink-0",
isSelected ? "bg-brand-primary text-black" : "text-brand-primary hover:bg-white/5"
)}
>
{outcome.odds.toFixed(2)}
</button>
</div>
)
})
)}
</div>
)}
</div>
)
}
export function MatchDetailView({
event,
apiSections,
}: {
event: Event
apiSections?: ApiSection[] | null
}) {
useBetslipStore((s) => s.bets)
const mockDetailMarkets = getEventDetailMarkets(event.id)
const cardsBookings = getCardsBookingsMarkets(event.id)
const detailMarkets: DetailMarketSection[] = (apiSections?.length
? apiSections.map((s) => ({ id: s.id, title: s.title, outcomes: s.outcomes }))
: mockDetailMarkets) as DetailMarketSection[]
const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>(() => ({
"bookings-1x2": true,
"sending-off": true,
"1st-booking": true,
"1st-half-bookings-1x2": true,
"booking-points-ou": true,
"1st-half-1st-booking": true,
...(apiSections?.length
? Object.fromEntries(detailMarkets.slice(0, 8).map((s) => [s.id, true]))
: {}),
}))
const [activeCategory, setActiveCategory] = useState("Main")
const toggleSection = (id: string) => {
setExpandedSections((prev) => ({ ...prev, [id]: !prev[id] }))
}
const breadcrumbLeague =
event.league === "Premier League"
? "England - Premier League"
: event.league
? `${event.country} - ${event.league}`
: "Event"
const isCardsBookings = activeCategory === "Cards/Bookings"
const allSections = isCardsBookings ? [...cardsBookings.left, ...cardsBookings.right] : detailMarkets
const mid = Math.ceil(allSections.length / 2)
const leftSections = allSections.slice(0, mid)
const rightSections = allSections.slice(mid)
return (
<div className="flex flex-col bg-brand-bg rounded overflow-hidden">
{/* Breadcrumb: back arrow, ellipsis, path */}
<div className="bg-brand-surface px-3 py-2 border-b border-border/20">
<Link
href="/"
className="flex items-center gap-2 text-[11px] font-bold text-white/70 hover:text-brand-primary transition-colors"
>
<span className="text-white/80">&lt;</span>
<span>...</span>
<span>Football {breadcrumbLeague} / {event.homeTeam} vs. {event.awayTeam}</span>
</Link>
<h1 className="text-[15px] font-bold text-white mt-2">
{breadcrumbLeague}
</h1>
</div>
{/* Match header: team names in boxes and below */}
<div className="bg-brand-surface px-4 py-5 border-b border-border/20">
<div className="flex items-center justify-center gap-10">
<div className="flex flex-col items-center gap-2 min-w-0">
<div className="w-20 min-h-20 rounded-md bg-brand-bg border border-white/10 flex items-center justify-center px-2 py-3 text-center">
<span className="text-[11px] font-black text-white leading-tight line-clamp-3">
{event.homeTeam}
</span>
</div>
<span className="text-[13px] font-bold text-white text-center truncate max-w-[120px]">{event.homeTeam}</span>
</div>
<span className="text-[12px] font-black text-white/50 uppercase shrink-0">VS</span>
<div className="flex flex-col items-center gap-2 min-w-0">
<div className="w-20 min-h-20 rounded-md bg-brand-bg border border-white/10 flex items-center justify-center px-2 py-3 text-center">
<span className="text-[11px] font-black text-white leading-tight line-clamp-3">
{event.awayTeam}
</span>
</div>
<span className="text-[13px] font-bold text-white text-center truncate max-w-[120px]">{event.awayTeam}</span>
</div>
</div>
</div>
{/* Category tabs: wrap into 23 rows, not scrollable */}
<div className="flex flex-wrap gap-1.5 p-2 bg-brand-bg border-b border-border/20">
{MARKET_CATEGORIES.map((label) => (
<button
key={label}
type="button"
onClick={() => setActiveCategory(label)}
className={cn(
"px-3 py-1.5 text-[10px] font-bold uppercase whitespace-nowrap rounded transition-colors",
activeCategory === label
? "bg-brand-surface-light text-white border border-white/10"
: "text-white/60 hover:text-white hover:bg-white/5"
)}
>
{label}
</button>
))}
</div>
{/* Two-column grid of market sections (split evenly so both columns are used) */}
<div className="flex-1 min-h-0 overflow-y-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-0 bg-brand-surface-light">
{/* Left column */}
<div className="border-r border-white/5">
{leftSections.map((section) => (
<MarketSectionBlock
key={section.id}
section={section}
event={event}
marketName={section.title}
isExpanded={expandedSections[section.id] ?? false}
onToggle={() => toggleSection(section.id)}
/>
))}
</div>
{/* Right column */}
<div>
{rightSections.map((section) => (
<MarketSectionBlock
key={section.id}
section={section}
event={event}
marketName={section.title}
isExpanded={expandedSections[section.id] ?? false}
onToggle={() => toggleSection(section.id)}
/>
))}
</div>
</div>
</div>
</div>
)
}