-
+
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.
-
)
-}
\ No newline at end of file
+}
diff --git a/components/layout/site-header.tsx b/components/layout/site-header.tsx
index a7928b4..c81ad17 100644
--- a/components/layout/site-header.tsx
+++ b/components/layout/site-header.tsx
@@ -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) {
- FORTUNE
+ HARIF
-
BETS
+
SPORT
@@ -146,9 +146,9 @@ export function SiteHeader({ onLoginClick, onRegisterClick }: SiteHeaderProps) {
- FORTUNE
+ HARIF
-
BETS
+
SPORT
@@ -288,9 +288,9 @@ export function SiteHeader({ onLoginClick, onRegisterClick }: SiteHeaderProps) {
- FORTUNE
+ HARIF
-
BETS
+
SPORT
setDrawerOpen(false)} className="text-white/60 hover:text-white text-2xl leading-none">ร
diff --git a/components/layout/sports-sidebar.tsx b/components/layout/sports-sidebar.tsx
index b940333..2252f95 100644
--- a/components/layout/sports-sidebar.tsx
+++ b/components/layout/sports-sidebar.tsx
@@ -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 (
@@ -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 (
+
+
Quick Filter
+
+ {QUICK_FILTER_OPTIONS.map(({ label, key }) => (
+ 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}
+
+ ))}
+
+
+ )
+}
+
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(sportFromUrl)
+ const [expandedCountries, setExpandedCountries] = useState>(new Set())
+ const [leaguesBySportId, setLeaguesBySportId] = useState>({})
+ const [loadingSportId, setLoadingSportId] = useState(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()
+ 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 (
@@ -51,21 +153,18 @@ export function SportsSidebar() {
Top Leagues
- {/* Popular Leagues */}
+ {/* Top Leagues */}
- {popularLeagues.map((league) => (
+ {TOP_LEAGUES.map((league) => (
- {league.logo ? (
-
- ) : (
-
โฝ
- )}
+
โฝ
โข
{league.name}
@@ -83,18 +182,8 @@ export function SportsSidebar() {
- {/* Quick Filter Section */}
-
-
Quick Filter
-
- {["All", "Today", "3h", "6h", "9h", "12h"].map((t) => (
- {t}
- ))}
-
-
+ {/* Quick Filter Section: passes first_start_time (RFC3339) to events API */}
+
{/* Search Event Section */}
@@ -108,29 +197,102 @@ export function SportsSidebar() {
- {/* Sport categories */}
+ {/* Nested: Sport โ Countries โ Leagues (mapped to sport_id & leagues API cc) */}
- {sportCategories.map((sport) => (
-
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-brand-surface text-white"
- : "text-white/70 hover:bg-brand-surface hover:text-white"
- )}
- >
-
-
{sport.icon}
-
{sport.name}
+ {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 (
+
+ {/* Sport row: click only expands/collapses to show countries (no navigation) */}
+
setExpandedSport(isExpanded ? null : sport.id)}
+ className={cn(
+ "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"
+ )}
+ >
+ {isExpanded ? : }
+
+ {sport.icon}
+ {sport.name}
+
+ {leagues.length > 0 && (
+ {leagues.length}
+ )}
+
+
+ {/* Countries (nested under sport) */}
+ {isExpanded && (
+
+ {loading ? (
+
Loadingโฆ
+ ) : (
+ countries.map(({ cc, name }) => {
+ const countryExpanded = expandedCountries.has(cc || "__intl__")
+ const countryKey = cc || "__intl__"
+ const leaguesInCountry = getLeaguesForCountry(sport.sport_id, cc)
+
+ return (
+
+
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"
+ )}
+ >
+
+ {cc ? (
+
+ ) : (
+
โ
+ )}
+
{name}
+
+ {countryExpanded ? : }
+
+
+ {/* Leagues (nested under country) */}
+ {countryExpanded && (
+
+ {leaguesInCountry.map((league) => (
+
+
โข
+
{league.name}
+
+
+ ))}
+
+ )}
+
+ )
+ })
+ )}
+
+ )}
-
- {sport.count}
-
-
-
- ))}
+ )
+ })}
{/* Bet Services */}