109 lines
2.9 KiB
TypeScript
109 lines
2.9 KiB
TypeScript
"use client"
|
|
|
|
import { create } from "zustand"
|
|
import type { AppEvent, QuickFilterKey } from "./betting-types"
|
|
import {
|
|
fetchEvents,
|
|
fetchOddsForEvent,
|
|
apiEventToAppEvent,
|
|
getListMarketsFromOddsResponse,
|
|
getTimeRangeForQuickFilter,
|
|
type EventsParams,
|
|
} from "./betting-api"
|
|
|
|
const PAGE_SIZE = 12
|
|
|
|
type BettingState = {
|
|
events: AppEvent[]
|
|
page: number
|
|
total: number | null
|
|
loading: boolean
|
|
error: string | null
|
|
hasMore: boolean
|
|
sportId: number | null
|
|
leagueId: string | null
|
|
quickFilter: QuickFilterKey
|
|
setFilters: (sportId: number | null, leagueId: string | null) => void
|
|
setQuickFilter: (key: QuickFilterKey) => void
|
|
loadPage: (pageNum: number, append: boolean) => Promise<void>
|
|
loadMore: () => void
|
|
reset: () => void
|
|
}
|
|
|
|
const initialState = {
|
|
events: [],
|
|
page: 1,
|
|
total: null,
|
|
loading: true,
|
|
error: null as string | null,
|
|
hasMore: true,
|
|
sportId: null as number | null,
|
|
leagueId: null as string | null,
|
|
quickFilter: "all" as QuickFilterKey,
|
|
}
|
|
|
|
export const useBettingStore = create<BettingState>((set, get) => ({
|
|
...initialState,
|
|
|
|
setFilters: (sportId, leagueId) => {
|
|
const prev = get()
|
|
if (prev.sportId === sportId && prev.leagueId === leagueId) return
|
|
set({ sportId, leagueId, page: 1 })
|
|
get().loadPage(1, false)
|
|
},
|
|
|
|
setQuickFilter: (quickFilter) => {
|
|
set({ quickFilter, page: 1 })
|
|
get().loadPage(1, false)
|
|
},
|
|
|
|
loadPage: async (pageNum: number, append: boolean) => {
|
|
const { sportId, leagueId, quickFilter } = get()
|
|
const timeRange = getTimeRangeForQuickFilter(quickFilter)
|
|
set({ loading: true, error: null })
|
|
try {
|
|
const params: EventsParams = {
|
|
page: pageNum,
|
|
page_size: PAGE_SIZE,
|
|
sport_id: sportId ?? undefined,
|
|
league_id: leagueId ? Number(leagueId) : undefined,
|
|
...timeRange,
|
|
}
|
|
const res = await fetchEvents(params)
|
|
const apiEvents = res.data ?? []
|
|
const oddsResponses = await Promise.all(
|
|
apiEvents.map((e) => fetchOddsForEvent(e.id).catch(() => ({ data: [] as typeof res.data })))
|
|
)
|
|
const newEvents: AppEvent[] = apiEvents.map((e, i) => {
|
|
const oddsList = oddsResponses[i]?.data ?? []
|
|
const listMarkets = getListMarketsFromOddsResponse(oddsList)
|
|
const appEvent = apiEventToAppEvent(e, listMarkets) as AppEvent
|
|
appEvent.rawOdds = oddsList
|
|
return appEvent
|
|
})
|
|
set((s) => ({
|
|
events: append ? [...s.events, ...newEvents] : newEvents,
|
|
loading: false,
|
|
total: res.total ?? s.total,
|
|
hasMore: newEvents.length === PAGE_SIZE,
|
|
page: pageNum,
|
|
}))
|
|
} catch (err) {
|
|
set({
|
|
loading: false,
|
|
error: err instanceof Error ? err.message : "Failed to load events",
|
|
events: append ? get().events : [],
|
|
})
|
|
}
|
|
},
|
|
|
|
loadMore: () => {
|
|
const { page, loadPage } = get()
|
|
const next = page + 1
|
|
set({ page: next })
|
|
loadPage(next, true)
|
|
},
|
|
|
|
reset: () => set(initialState),
|
|
}))
|