layout components
This commit is contained in:
parent
0354a182f3
commit
1a1361ee7f
|
|
@ -9,7 +9,7 @@ export type GameCategory =
|
|||
| "favourite"
|
||||
| "recently-played"
|
||||
| "most-popular"
|
||||
| "fortune-special"
|
||||
| "harif-special"
|
||||
| "for-you"
|
||||
| "slots"
|
||||
| "crash-games"
|
||||
|
|
@ -30,7 +30,7 @@ const categories = [
|
|||
{ id: "favourite", name: "Favourite", icon: Heart },
|
||||
{ id: "recently-played", name: "Recently Played", icon: Clock },
|
||||
{ id: "most-popular", name: "Most Popular", icon: Star },
|
||||
{ id: "fortune-special", name: "Fortune Special", icon: Star },
|
||||
{ id: "harif-special", name: "Harif Special", icon: Zap },
|
||||
{ id: "for-you", name: "For You", icon: Star },
|
||||
{ id: "slots", name: "Slots", icon: Star },
|
||||
{ id: "crash-games", name: "Crash Games", icon: Star },
|
||||
|
|
|
|||
|
|
@ -14,15 +14,15 @@ function Logo() {
|
|||
<div key={i} className="w-[5px] h-[38px] bg-[#cc2222] -skew-x-12" />
|
||||
))}
|
||||
</div>
|
||||
{/* FORTUNE box */}
|
||||
{/* HARIF box */}
|
||||
<div className="bg-brand-accent px-3 py-1 -skew-x-12 flex items-center h-[38px]">
|
||||
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">
|
||||
FORTUNE
|
||||
HARIF
|
||||
</span>
|
||||
</div>
|
||||
{/* SPORT text */}
|
||||
<span className="text-2xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">
|
||||
BETS
|
||||
SPORT
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,17 +4,11 @@ import Link from "next/link"
|
|||
|
||||
export function SiteFooter() {
|
||||
return (
|
||||
<footer className="bg-brand-surface text-white pt-16">
|
||||
|
||||
{/* Centered Columns */}
|
||||
<div className="mx-auto max-w-5xl px-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-20 text-center md:text-left justify-items-center md:justify-items-start">
|
||||
|
||||
<footer className="bg-brand-surface text-white pt-12">
|
||||
<div className="container mx-auto px-6 grid grid-cols-1 md:grid-cols-4 gap-12 text-center md:text-left">
|
||||
{/* ABOUT */}
|
||||
<div>
|
||||
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">
|
||||
ABOUT
|
||||
</h3>
|
||||
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">ABOUT</h3>
|
||||
<ul className="space-y-2 text-[11px] text-white/60 font-medium tracking-tight">
|
||||
<li><Link href="/about" className="hover:text-primary transition-colors">About us</Link></li>
|
||||
<li><Link href="/privacy" className="hover:text-primary transition-colors">Privacy Policy</Link></li>
|
||||
|
|
@ -25,9 +19,7 @@ export function SiteFooter() {
|
|||
|
||||
{/* INFORMATION */}
|
||||
<div>
|
||||
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">
|
||||
INFORMATION
|
||||
</h3>
|
||||
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">INFORMATION</h3>
|
||||
<ul className="space-y-2 text-[11px] text-white/60 font-medium tracking-tight">
|
||||
<li><Link href="/terms" className="hover:text-primary transition-colors">Terms & Conditions</Link></li>
|
||||
<li><Link href="/faq" className="hover:text-primary transition-colors">FAQ</Link></li>
|
||||
|
|
@ -38,11 +30,9 @@ export function SiteFooter() {
|
|||
|
||||
{/* SPORTS */}
|
||||
<div>
|
||||
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">
|
||||
SPORTS
|
||||
</h3>
|
||||
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">SPORTS</h3>
|
||||
<ul className="space-y-2 text-[11px] text-white/60 font-medium tracking-tight">
|
||||
<li><Link href="/live" className="hover:text-primary transition-colors">Live betting</Link></li>
|
||||
<li><Link href="/live" className="hover:text-primary transition-colors text-blue-400">Live betting</Link></li>
|
||||
<li><Link href="/football" className="hover:text-primary transition-colors">Football</Link></li>
|
||||
<li><Link href="/basketball" className="hover:text-primary transition-colors">Basketball</Link></li>
|
||||
<li><Link href="/tennis" className="hover:text-primary transition-colors">Tennis</Link></li>
|
||||
|
|
@ -52,56 +42,41 @@ export function SiteFooter() {
|
|||
|
||||
{/* PLAY NOW */}
|
||||
<div>
|
||||
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">
|
||||
PLAY NOW
|
||||
</h3>
|
||||
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">PLAY NOW</h3>
|
||||
<ul className="space-y-2 text-[11px] text-white/60 font-medium tracking-tight">
|
||||
<li><Link href="/virtual" className="hover:text-primary transition-colors">Virtual</Link></li>
|
||||
<li><Link href="/special-games" className="hover:text-primary transition-colors">Special Games</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Logo Section */}
|
||||
<div className="flex flex-col items-center justify-center py-16 border-t border-white/5 mt-16 bg-brand-surface-light">
|
||||
<div className="flex flex-col items-center justify-center py-16 border-t border-white/5 mt-12 bg-brand-surface-light">
|
||||
<div className="flex items-center bg-brand-surface px-5 py-2">
|
||||
<div className="bg-brand-accent px-3 py-1 -skew-x-12">
|
||||
<span className="text-3xl font-black text-white italic tracking-tighter skew-x-12 inline-block">
|
||||
FORTUNE
|
||||
</span>
|
||||
<span className="text-3xl font-black text-white italic tracking-tighter skew-x-12 inline-block">HARIF</span>
|
||||
</div>
|
||||
<span className="text-3xl font-black text-brand-primary italic tracking-tighter ml-1">
|
||||
BETS
|
||||
</span>
|
||||
<span className="text-3xl font-black text-brand-primary italic tracking-tighter ml-1">SPORT</span>
|
||||
</div>
|
||||
|
||||
{/* Footer Links */}
|
||||
<div className="flex flex-wrap items-center justify-center gap-6 mt-12 text-[11px] font-bold tracking-tight text-white/80">
|
||||
<Link href="/affiliates" className="hover:text-primary uppercase transition-colors">
|
||||
Affiliates
|
||||
</Link>
|
||||
<Link href="/affiliates" className="hover:text-primary uppercase transition-colors">Affiliates</Link>
|
||||
<span className="size-1 bg-white/10 rounded-full" />
|
||||
<Link href="/complaints" className="hover:text-primary uppercase transition-colors">
|
||||
Complaints
|
||||
</Link>
|
||||
<Link href="/complaints" className="hover:text-primary uppercase transition-colors">Complaints</Link>
|
||||
<span className="size-1 bg-white/10 rounded-full" />
|
||||
<Link href="/deposits" className="hover:text-primary uppercase transition-colors">
|
||||
Deposits and Withdrawals
|
||||
</Link>
|
||||
<Link href="/deposits" className="hover:text-primary uppercase transition-colors">Deposits and Withdrawals</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cookie Text */}
|
||||
<div className="bg-brand-bg py-10 px-6 text-center">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="container mx-auto max-w-5xl">
|
||||
<p className="text-[10px] text-white/40 leading-relaxed font-medium uppercase tracking-tight">
|
||||
By accessing, or continuing to use or browse this site, you consent to our use of certain cookies to improve your experience with us. We only use cookies that will enhance your experience and will not interfere with your privacy. Please look at our Cookie Policy for further informations on our use of the cookie and how you can disable it or manage it if you so choose.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ const allNavItems = [
|
|||
{ href: "/poker", label: "POKER", isNew: true },
|
||||
{ href: "/race", label: "RACE", isNew: true },
|
||||
{ href: "/promo", label: "PROMO" },
|
||||
// { href: "/aviator", label: "AVIATOR" },
|
||||
{ href: "/aviator", label: "AVIATOR" },
|
||||
]
|
||||
|
||||
const drawerLinks = [
|
||||
|
|
@ -118,9 +118,9 @@ export function SiteHeader({ onLoginClick, onRegisterClick }: SiteHeaderProps) {
|
|||
<Link href="/" className="flex-1 flex items-center justify-center">
|
||||
<div className="flex items-center">
|
||||
<div className="bg-brand-accent px-2 py-0.5 -skew-x-12 flex items-center h-[28px]">
|
||||
<span className="text-xl font-black text-white italic tracking-tighter skew-x-12 leading-none">FORTUNE</span>
|
||||
<span className="text-xl font-black text-white italic tracking-tighter skew-x-12 leading-none">HARIF</span>
|
||||
</div>
|
||||
<span className="text-xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">BETS</span>
|
||||
<span className="text-xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">SPORT</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
|
|
@ -146,9 +146,9 @@ export function SiteHeader({ onLoginClick, onRegisterClick }: SiteHeaderProps) {
|
|||
<Link href="/" className="flex items-center shrink-0">
|
||||
<div className="flex items-center bg-brand-surface h-[60px] px-4 w-[280px] shrink-0 border-r border-white/5">
|
||||
<div className="bg-brand-accent px-3 py-1 -skew-x-12 flex items-center h-[34px]">
|
||||
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">FORTUNE</span>
|
||||
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">HARIF</span>
|
||||
</div>
|
||||
<span className="text-2xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">BETS</span>
|
||||
<span className="text-2xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">SPORT</span>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex items-center flex-1 justify-end px-4 h-full gap-0 bg-brand-surface">
|
||||
|
|
@ -288,9 +288,9 @@ export function SiteHeader({ onLoginClick, onRegisterClick }: SiteHeaderProps) {
|
|||
<div className="flex items-center justify-between px-4 py-3 bg-brand-surface border-b border-white/10">
|
||||
<div className="flex items-center">
|
||||
<div className="bg-brand-accent px-2 py-0.5 -skew-x-12 flex items-center h-[24px]">
|
||||
<span className="text-base font-black text-white italic tracking-tighter skew-x-12 leading-none">FORTUNE</span>
|
||||
<span className="text-base font-black text-white italic tracking-tighter skew-x-12 leading-none">HARIF</span>
|
||||
</div>
|
||||
<span className="text-base font-black text-brand-primary italic tracking-tighter ml-1 leading-none">BETS</span>
|
||||
<span className="text-base font-black text-brand-primary italic tracking-tighter ml-1 leading-none">SPORT</span>
|
||||
</div>
|
||||
<button onClick={() => setDrawerOpen(false)} className="text-white/60 hover:text-white text-2xl leading-none">×</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,32 @@
|
|||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useState, useEffect, useMemo } from "react"
|
||||
import Link from "next/link"
|
||||
import { popularLeagues } from "@/lib/mock-data"
|
||||
import { useSearchParams } from "next/navigation"
|
||||
import { TOP_LEAGUES, fetchLeagues } from "@/lib/store/betting-api"
|
||||
import { useBettingStore } from "@/lib/store/betting-store"
|
||||
import type { ApiLeague } from "@/lib/store/betting-types"
|
||||
import { SportEnum, type QuickFilterKey } from "@/lib/store/betting-types"
|
||||
import { getCountryName } from "@/lib/countries"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { ChevronsLeft } from "lucide-react"
|
||||
import { ChevronsLeft, ChevronDown, ChevronUp, Plus } from "lucide-react"
|
||||
|
||||
/** Sidebar sports: slug for URL, sport_id for /leagues?sport_id= (order and list from design) */
|
||||
const SIDEBAR_SPORTS = [
|
||||
{ id: "football", sport_id: SportEnum.SOCCER, name: "Football", icon: "⚽" },
|
||||
{ id: "basketball", sport_id: SportEnum.BASKETBALL, name: "Basketball", icon: "🏀" },
|
||||
{ id: "american-football", sport_id: SportEnum.AMERICAN_FOOTBALL, name: "American Football", icon: "🏈" },
|
||||
{ id: "baseball", sport_id: SportEnum.BASEBALL, name: "Baseball", icon: "⚾" },
|
||||
{ id: "cricket", sport_id: SportEnum.CRICKET, name: "Cricket", icon: "🏏" },
|
||||
{ id: "futsal", sport_id: SportEnum.FUTSAL, name: "Futsal", icon: "⚽" },
|
||||
{ id: "darts", sport_id: SportEnum.DARTS, name: "Darts", icon: "🎯" },
|
||||
{ id: "ice-hockey", sport_id: SportEnum.ICE_HOCKEY, name: "Ice Hockey", icon: "🏒" },
|
||||
{ id: "rugby-union", sport_id: SportEnum.RUGBY_UNION, name: "Rugby", icon: "🏉" },
|
||||
{ id: "rugby-league", sport_id: SportEnum.RUGBY_LEAGUE, name: "Rugby League", icon: "🏉" },
|
||||
{ id: "volleyball", sport_id: SportEnum.VOLLEYBALL, name: "Volleyball", icon: "🏐" },
|
||||
]
|
||||
|
||||
/** Soccer ball icon - outline style for white/green theme */
|
||||
function SoccerBallIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" className={className} fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
|
|
@ -18,23 +37,106 @@ function SoccerBallIcon({ className }: { className?: string }) {
|
|||
)
|
||||
}
|
||||
|
||||
const sportCategories = [
|
||||
{ id: "football", name: "Football", icon: "⚽", count: 1412 },
|
||||
{ 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 },
|
||||
const QUICK_FILTER_OPTIONS: { label: string; key: QuickFilterKey }[] = [
|
||||
{ label: "All", key: "all" },
|
||||
{ label: "Today", key: "today" },
|
||||
{ label: "3h", key: "3h" },
|
||||
{ label: "6h", key: "6h" },
|
||||
{ label: "9h", key: "9h" },
|
||||
{ label: "12h", key: "12h" },
|
||||
]
|
||||
|
||||
function QuickFilterSection() {
|
||||
const quickFilter = useBettingStore((s) => s.quickFilter)
|
||||
const setQuickFilter = useBettingStore((s) => s.setQuickFilter)
|
||||
return (
|
||||
<div className="bg-brand-surface p-3 border-b border-border/30">
|
||||
<span className="text-brand-primary text-[10.5px] uppercase font-black block mb-2 tracking-tight">Quick Filter</span>
|
||||
<div className="grid grid-cols-6 gap-px">
|
||||
{QUICK_FILTER_OPTIONS.map(({ label, key }) => (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
onClick={() => setQuickFilter(key)}
|
||||
className={cn(
|
||||
"text-[10px] py-1.5 font-bold transition-colors",
|
||||
quickFilter === key ? "bg-brand-surface-light text-white" : "bg-brand-surface text-white/50 hover:text-white"
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function SportsSidebar() {
|
||||
const [activeSport, setActiveSport] = useState("football")
|
||||
const searchParams = useSearchParams()
|
||||
const sportFromUrl = searchParams.get("sport") ?? "football"
|
||||
const leagueFromUrl = searchParams.get("league")
|
||||
|
||||
const [expandedSport, setExpandedSport] = useState<string | null>(sportFromUrl)
|
||||
const [expandedCountries, setExpandedCountries] = useState<Set<string>>(new Set())
|
||||
const [leaguesBySportId, setLeaguesBySportId] = useState<Record<number, ApiLeague[]>>({})
|
||||
const [loadingSportId, setLoadingSportId] = useState<number | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
setExpandedSport((prev) => (prev ?? sportFromUrl) || sportFromUrl)
|
||||
}, [sportFromUrl])
|
||||
|
||||
const currentSport = SIDEBAR_SPORTS.find((s) => s.id === sportFromUrl)
|
||||
const sportId = currentSport?.sport_id ?? SportEnum.SOCCER
|
||||
|
||||
useEffect(() => {
|
||||
if (!expandedSport) return
|
||||
const sport = SIDEBAR_SPORTS.find((s) => s.id === expandedSport)
|
||||
if (!sport || sport.sport_id in leaguesBySportId) return
|
||||
let cancelled = false
|
||||
setLoadingSportId(sport.sport_id)
|
||||
fetchLeagues(sport.sport_id)
|
||||
.then((res) => {
|
||||
if (!cancelled) setLeaguesBySportId((prev) => ({ ...prev, [sport.sport_id]: res.data ?? [] }))
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setLeaguesBySportId((prev) => ({ ...prev, [sport.sport_id]: [] }))
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoadingSportId(null)
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [expandedSport, leaguesBySportId])
|
||||
|
||||
const toggleCountry = (cc: string) => {
|
||||
setExpandedCountries((prev) => {
|
||||
const next = new Set(prev)
|
||||
if (next.has(cc)) next.delete(cc)
|
||||
else next.add(cc)
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
const getCountriesForSport = (sportIdNum: number) => {
|
||||
const leagues = leaguesBySportId[sportIdNum] ?? []
|
||||
const ccSet = new Set<string>()
|
||||
leagues.forEach((l) => ccSet.add((l.cc || "").trim().toLowerCase() || "__intl__"))
|
||||
return Array.from(ccSet)
|
||||
.map((cc) => ({
|
||||
cc: cc === "__intl__" ? "" : cc,
|
||||
name: cc === "__intl__" ? "International" : getCountryName(cc),
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
}
|
||||
|
||||
const getLeaguesForCountry = (sportIdNum: number, countryCc: string) => {
|
||||
const leagues = leaguesBySportId[sportIdNum] ?? []
|
||||
const cc = countryCc.toLowerCase()
|
||||
return leagues
|
||||
.filter((l) => ((l.cc || "").trim().toLowerCase() || "") === cc)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="hidden h-full w-[280px] shrink-0 bg-brand-surface-light lg:block overflow-y-auto border-r border-border/40 scrollbar-hide">
|
||||
|
|
@ -51,21 +153,18 @@ export function SportsSidebar() {
|
|||
Top Leagues
|
||||
</div>
|
||||
|
||||
{/* Popular Leagues */}
|
||||
{/* Top Leagues */}
|
||||
<div className="flex flex-col">
|
||||
{popularLeagues.map((league) => (
|
||||
{TOP_LEAGUES.map((league) => (
|
||||
<Link
|
||||
key={league.id}
|
||||
href={`/?league=${league.id}`}
|
||||
href={`/?sport=${sportFromUrl}&league=${league.id}`}
|
||||
scroll={false}
|
||||
className="w-full flex items-center justify-between px-3 py-2 text-left text-white/90 hover:bg-brand-surface transition-colors border-b border-border/10 group h-9"
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<div className="size-5 shrink-0 overflow-hidden rounded-sm flex items-center justify-center bg-white/5 border border-white/10 group-hover:border-white/20 transition-colors">
|
||||
{league.logo ? (
|
||||
<img src={league.logo} alt="" className="size-full object-contain" />
|
||||
) : (
|
||||
<span className="text-[11px]">⚽</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-white/50 text-[8px] font-bold select-none">•</span>
|
||||
<span className="text-[10.5px] font-bold leading-tight truncate max-w-[140px]">{league.name}</span>
|
||||
|
|
@ -83,18 +182,8 @@ export function SportsSidebar() {
|
|||
</Link>
|
||||
</Button>
|
||||
|
||||
{/* Quick Filter Section */}
|
||||
<div className="bg-brand-surface p-3 border-b border-border/30">
|
||||
<span className="text-brand-primary 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-brand-surface-light text-white" : "bg-brand-surface text-white/50 hover:text-white"
|
||||
)}>{t}</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* Quick Filter Section: passes first_start_time (RFC3339) to events API */}
|
||||
<QuickFilterSection />
|
||||
|
||||
{/* Search Event Section */}
|
||||
<div className="bg-brand-surface p-3 border-b border-border/40">
|
||||
|
|
@ -108,30 +197,103 @@ export function SportsSidebar() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sport categories */}
|
||||
{/* Nested: Sport → Countries → Leagues (mapped to sport_id & leagues API cc) */}
|
||||
<div className="divide-y divide-border/10 bg-brand-surface-light">
|
||||
{sportCategories.map((sport) => (
|
||||
{SIDEBAR_SPORTS.map((sport) => {
|
||||
const isExpanded = expandedSport === sport.id
|
||||
const leagues = leaguesBySportId[sport.sport_id] ?? []
|
||||
const loading = loadingSportId === sport.sport_id
|
||||
const countries = getCountriesForSport(sport.sport_id)
|
||||
|
||||
return (
|
||||
<div key={sport.id} className="border-b border-border/10">
|
||||
{/* Sport row: click only expands/collapses to show countries (no navigation) */}
|
||||
<button
|
||||
key={sport.id}
|
||||
onClick={() => setActiveSport(sport.id)}
|
||||
type="button"
|
||||
onClick={() => setExpandedSport(isExpanded ? null : 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-brand-surface text-white"
|
||||
: "text-white/70 hover:bg-brand-surface hover:text-white"
|
||||
"w-full flex items-center gap-1 py-2 pr-2 pl-1.5 text-left transition-colors h-9",
|
||||
isExpanded ? "bg-brand-surface text-brand-primary" : "text-white/80 hover:bg-brand-surface 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>
|
||||
<SoccerBallIcon className="size-3.5 text-white/30 shrink-0" />
|
||||
{isExpanded ? <ChevronUp className="size-3.5 shrink-0 text-current" /> : <ChevronDown className="size-3.5 shrink-0 text-current" />}
|
||||
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||
<span className="text-[12px] shrink-0">{sport.icon}</span>
|
||||
<span className="text-[10.5px] font-bold truncate">{sport.name}</span>
|
||||
</div>
|
||||
{leagues.length > 0 && (
|
||||
<span className="text-[9px] font-bold text-white/40 shrink-0">{leagues.length}</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Countries (nested under sport) */}
|
||||
{isExpanded && (
|
||||
<div className="bg-brand-surface-light/80 pl-4">
|
||||
{loading ? (
|
||||
<div className="py-2 text-[10px] text-white/50">Loading…</div>
|
||||
) : (
|
||||
countries.map(({ cc, name }) => {
|
||||
const countryExpanded = expandedCountries.has(cc || "__intl__")
|
||||
const countryKey = cc || "__intl__"
|
||||
const leaguesInCountry = getLeaguesForCountry(sport.sport_id, cc)
|
||||
|
||||
return (
|
||||
<div key={countryKey} className="border-b border-border/5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleCountry(countryKey)}
|
||||
className={cn(
|
||||
"w-full flex items-center justify-between gap-2 py-1.5 pr-2 text-left text-[10.5px] font-bold transition-colors",
|
||||
countryExpanded ? "text-brand-primary" : "text-white/80 hover:text-white"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{cc ? (
|
||||
<img
|
||||
src={`https://flagcdn.com/w20/${cc}.png`}
|
||||
alt=""
|
||||
width={20}
|
||||
height={14}
|
||||
className="shrink-0 rounded-sm object-cover w-5 h-[14px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="size-5 shrink-0 flex items-center justify-center text-[10px] text-white/50">◆</span>
|
||||
)}
|
||||
<span className="truncate">{name}</span>
|
||||
</div>
|
||||
{countryExpanded ? <ChevronUp className="size-3 shrink-0" /> : <ChevronDown className="size-3 shrink-0" />}
|
||||
</button>
|
||||
|
||||
{/* Leagues (nested under country) */}
|
||||
{countryExpanded && (
|
||||
<div className="pl-2 pb-1 max-h-48 overflow-y-auto">
|
||||
{leaguesInCountry.map((league) => (
|
||||
<Link
|
||||
key={league.id}
|
||||
href={`/?sport=${sport.id}&league=${league.id}`}
|
||||
scroll={false}
|
||||
className={cn(
|
||||
"flex items-center justify-between gap-1 py-1.5 pr-1 text-[10px] font-bold border-b border-border/5 hover:bg-brand-surface/50 transition-colors group",
|
||||
leagueFromUrl === String(league.id) ? "text-brand-primary" : "text-white/90"
|
||||
)}
|
||||
>
|
||||
<span className="text-white/50 text-[8px] group-hover:text-brand-primary">•</span>
|
||||
<span className="flex-1 truncate">{league.name}</span>
|
||||
<Plus className="size-3 shrink-0 text-white/40 group-hover:text-brand-primary" />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Bet Services */}
|
||||
<div className="mt-2 text-[11px] font-bold text-brand-primary px-3 py-2 uppercase border-y border-border/20 bg-brand-surface">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user