event and virtual pages
This commit is contained in:
parent
7a6f8f4279
commit
0354a182f3
|
|
@ -1,10 +1,51 @@
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { getEventById } from "@/lib/mock-data"
|
import { getEventById } from "@/lib/mock-data"
|
||||||
import { MatchDetailView } from "@/components/betting/match-detail-view"
|
import { MatchDetailView } from "@/components/betting/match-detail-view"
|
||||||
|
import {
|
||||||
|
fetchEvents,
|
||||||
|
fetchOddsForEvent,
|
||||||
|
apiEventToAppEvent,
|
||||||
|
get1X2ForEvent,
|
||||||
|
apiOddsToSections,
|
||||||
|
} from "@/lib/betting-api"
|
||||||
|
import type { Event } from "@/lib/mock-data"
|
||||||
|
|
||||||
export default async function EventPage({ params }: { params: Promise<{ id: string }> }) {
|
export default async function EventPage({ params }: { params: Promise<{ id: string }> }) {
|
||||||
const { id } = await params
|
const { id } = await params
|
||||||
const event = getEventById(id)
|
let event: Event | undefined = getEventById(id)
|
||||||
|
let apiSections: { id: string; title: string; outcomes: { label: string; odds: number }[] }[] | undefined
|
||||||
|
|
||||||
|
const numericId = id.trim() !== "" && !Number.isNaN(Number(id)) ? Number(id) : null
|
||||||
|
if (numericId !== null) {
|
||||||
|
try {
|
||||||
|
const [eventsRes, oddsRes] = await Promise.all([
|
||||||
|
fetchEvents({ page_size: 500, page: 1 }),
|
||||||
|
fetchOddsForEvent(numericId),
|
||||||
|
])
|
||||||
|
const apiEvent = (eventsRes.data ?? []).find((e) => e.id === numericId)
|
||||||
|
if (apiEvent) {
|
||||||
|
event = apiEventToAppEvent(apiEvent, get1X2ForEvent(oddsRes.data ?? [], apiEvent.id))
|
||||||
|
} else {
|
||||||
|
event = {
|
||||||
|
id: String(numericId),
|
||||||
|
sport: "Football",
|
||||||
|
sportIcon: "⚽",
|
||||||
|
league: "",
|
||||||
|
country: "",
|
||||||
|
homeTeam: "Home",
|
||||||
|
awayTeam: "Away",
|
||||||
|
time: "",
|
||||||
|
date: "",
|
||||||
|
isLive: false,
|
||||||
|
markets: [],
|
||||||
|
totalMarkets: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apiSections = apiOddsToSections(oddsRes.data ?? [])
|
||||||
|
} catch {
|
||||||
|
if (!event) event = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -17,5 +58,5 @@ export default async function EventPage({ params }: { params: Promise<{ id: stri
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <MatchDetailView event={event} />
|
return <MatchDetailView event={event} apiSections={apiSections} />
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,15 @@ export default function LoginPage() {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{/* FORTUNE box */}
|
{/* HARIF box */}
|
||||||
<div className="bg-brand-accent px-3 py-1 -skew-x-12 flex items-center h-[38px]">
|
<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">
|
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">
|
||||||
FORTUNE
|
HARIF
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/* SPORT text */}
|
{/* SPORT text */}
|
||||||
<span className="text-2xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">
|
<span className="text-2xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">
|
||||||
BETS
|
SPORT
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -43,15 +43,15 @@ export default function RegisterPage() {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{/* FORTUNE box */}
|
{/* HARIF box */}
|
||||||
<div className="bg-brand-accent px-3 py-1 -skew-x-12 flex items-center h-[38px]">
|
<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">
|
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">
|
||||||
FORTUNE
|
HARIF
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/* SPORT text */}
|
{/* SPORT text */}
|
||||||
<span className="text-2xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">
|
<span className="text-2xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">
|
||||||
BETS
|
SPORT
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const rules = [
|
const rules = [
|
||||||
{
|
{
|
||||||
title: "General Betting Rules",
|
title: "General Betting Rules",
|
||||||
content: "All bets are subject to FortuneBets terms and conditions. By placing a bet, you agree to abide by these rules. The minimum bet amount is 5 ETB and the maximum payout is 500,000 ETB per bet.",
|
content: "All bets are subject to Harifsport terms and conditions. By placing a bet, you agree to abide by these rules. The minimum bet amount is 5 ETB and the maximum payout is 500,000 ETB per bet.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Live Betting",
|
title: "Live Betting",
|
||||||
|
|
@ -13,7 +13,7 @@ const rules = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Responsible Gambling",
|
title: "Responsible Gambling",
|
||||||
content: "FortuneBets is committed to responsible gambling. Users may set deposit limits, loss limits, or self-exclude at any time. Gambling should be entertaining, not a source of income.",
|
content: "Harifsport is committed to responsible gambling. Users may set deposit limits, loss limits, or self-exclude at any time. Gambling should be entertaining, not a source of income.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Account Rules",
|
title: "Account Rules",
|
||||||
|
|
|
||||||
|
|
@ -1,132 +1,154 @@
|
||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react";
|
||||||
import Image from "next/image"
|
import Image from "next/image";
|
||||||
import { GamingSidebar } from "@/components/games/gaming-sidebar"
|
import { GamingSidebar } from "@/components/games/gaming-sidebar";
|
||||||
import { GameRow } from "@/components/games/game-row"
|
import { GameRow } from "@/components/games/game-row";
|
||||||
import { GameCard } from "@/components/games/game-card"
|
import { GameCard } from "@/components/games/game-card";
|
||||||
import { Search, Heart, Clock, Star, Zap, Gamepad2, AlertCircle, LayoutGrid } from "lucide-react"
|
import {
|
||||||
import { cn } from "@/lib/utils"
|
Search,
|
||||||
import api from "@/lib/api"
|
Heart,
|
||||||
|
Clock,
|
||||||
|
Star,
|
||||||
|
Zap,
|
||||||
|
Gamepad2,
|
||||||
|
AlertCircle,
|
||||||
|
LayoutGrid,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import api from "@/lib/api";
|
||||||
|
|
||||||
interface Provider {
|
interface Provider {
|
||||||
provider_id: string
|
provider_id: string;
|
||||||
provider_name: string
|
provider_name: string;
|
||||||
logo_dark: string
|
logo_dark: string;
|
||||||
logo_light: string
|
logo_light: string;
|
||||||
enabled: boolean
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApiGame {
|
interface ApiGame {
|
||||||
gameId: string
|
gameId: string;
|
||||||
providerId: string
|
providerId: string;
|
||||||
provider: string
|
provider: string;
|
||||||
name: string
|
name: string;
|
||||||
category: string
|
category: string;
|
||||||
deviceType: string
|
deviceType: string;
|
||||||
hasDemo: boolean
|
hasDemo: boolean;
|
||||||
hasFreeBets: boolean
|
hasFreeBets: boolean;
|
||||||
demoUrl?: string
|
demoUrl?: string;
|
||||||
image?: string // In case it gets added
|
image?: string; // In case it gets added
|
||||||
thumbnail?: string
|
thumbnail?: string;
|
||||||
provider_id?: string // Fallback
|
provider_id?: string; // Fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_IMAGE = "https://st.pokgaming.com/gameThumbnails/246.jpg"
|
const DEFAULT_IMAGE = "https://st.pokgaming.com/gameThumbnails/246.jpg";
|
||||||
|
|
||||||
export default function VirtualPage() {
|
export default function VirtualPage() {
|
||||||
const [activeCategory, setActiveCategory] = useState("all")
|
const [activeCategory, setActiveCategory] = useState("all");
|
||||||
const [providers, setProviders] = useState<Provider[]>([])
|
const [providers, setProviders] = useState<Provider[]>([]);
|
||||||
const [games, setGames] = useState<ApiGame[]>([])
|
const [games, setGames] = useState<ApiGame[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchVirtualData = async () => {
|
const fetchVirtualData = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true);
|
||||||
const [providersRes, gamesRes] = await Promise.all([
|
const [providersRes, gamesRes] = await Promise.all([
|
||||||
api.get("/virtual-game/orchestrator/providers"),
|
api.get("/virtual-game/orchestrator/providers"),
|
||||||
api.get("/virtual-game/orchestrator/games", { params: { limit: 2000 } })
|
api.get("/virtual-game/orchestrator/games", {
|
||||||
])
|
params: { limit: 2000 },
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
const pData = providersRes.data
|
const pData = providersRes.data;
|
||||||
const gData = gamesRes.data
|
const gData = gamesRes.data;
|
||||||
|
|
||||||
const providersList = pData.providers || pData.data || pData || []
|
const providersList = pData.providers || pData.data || pData || [];
|
||||||
const gamesList = gData.data || gData.games || gData || []
|
const gamesList = gData.data || gData.games || gData || [];
|
||||||
|
|
||||||
setProviders(Array.isArray(providersList) ? providersList : [])
|
setProviders(Array.isArray(providersList) ? providersList : []);
|
||||||
setGames(Array.isArray(gamesList) ? gamesList : [])
|
setGames(Array.isArray(gamesList) ? gamesList : []);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("Failed to fetch virtual games data:", err)
|
console.error("Failed to fetch virtual games data:", err);
|
||||||
setError("Failed to load games data.")
|
setError("Failed to load games data.");
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fetchVirtualData()
|
fetchVirtualData();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
// Create Sidebar Categories dynamically from providers
|
// Create Sidebar Categories dynamically from providers
|
||||||
const sidebarCategories = [
|
const sidebarCategories = [
|
||||||
{ id: "all", name: "All Games", icon: LayoutGrid },
|
{ id: "all", name: "All Games", icon: LayoutGrid },
|
||||||
...providers.map(p => ({
|
...providers.map((p) => ({
|
||||||
id: p.provider_id,
|
id: p.provider_id,
|
||||||
name: p.provider_name,
|
name: p.provider_name,
|
||||||
icon: p.logo_dark || p.logo_light || Gamepad2
|
icon: p.logo_dark || p.logo_light || Gamepad2,
|
||||||
}))
|
})),
|
||||||
]
|
];
|
||||||
|
|
||||||
// Filter games based on active category
|
// Filter games based on active category
|
||||||
// If "all", group by provider
|
// If "all", group by provider
|
||||||
let displayedGames: any[] = []
|
let displayedGames: any[] = [];
|
||||||
let groupedGames: { title: string, games: any[] }[] = []
|
let groupedGames: { title: string; games: any[] }[] = [];
|
||||||
|
|
||||||
const mapApiGameToCard = (game: ApiGame) => ({
|
const mapApiGameToCard = (game: ApiGame) => ({
|
||||||
id: game.gameId,
|
id: game.gameId,
|
||||||
title: game.name,
|
title: game.name,
|
||||||
image: game.thumbnail || game.image || DEFAULT_IMAGE,
|
image: game.thumbnail || game.image || DEFAULT_IMAGE,
|
||||||
provider: game.provider
|
provider: game.provider,
|
||||||
})
|
});
|
||||||
|
|
||||||
if (activeCategory === "all") {
|
if (activeCategory === "all") {
|
||||||
// Group up to 12 games per provider for the rows
|
// Group up to 12 games per provider for the rows
|
||||||
providers.forEach(p => {
|
providers.forEach((p) => {
|
||||||
const providerIdStr = String(p.provider_id || "").trim().toLowerCase()
|
const providerIdStr = String(p.provider_id || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
const providerGames = games
|
const providerGames = games
|
||||||
.filter(g => {
|
.filter((g) => {
|
||||||
const gameProvId = String(g.providerId || g.provider_id || "").trim().toLowerCase()
|
const gameProvId = String(g.providerId || g.provider_id || "")
|
||||||
return gameProvId === providerIdStr
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
return gameProvId === providerIdStr;
|
||||||
})
|
})
|
||||||
.slice(0, 12)
|
.slice(0, 12)
|
||||||
.map(mapApiGameToCard)
|
.map(mapApiGameToCard);
|
||||||
|
|
||||||
if (providerGames.length > 0) {
|
if (providerGames.length > 0) {
|
||||||
groupedGames.push({
|
groupedGames.push({
|
||||||
title: p.provider_name,
|
title: p.provider_name,
|
||||||
games: providerGames
|
games: providerGames,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
displayedGames = games
|
displayedGames = games
|
||||||
.filter(g => {
|
.filter((g) => {
|
||||||
const gameProvId = String(g.providerId || g.provider_id || "").trim().toLowerCase()
|
const gameProvId = String(g.providerId || g.provider_id || "")
|
||||||
const matches = gameProvId === String(activeCategory).trim().toLowerCase()
|
.trim()
|
||||||
if (g.providerId?.toLowerCase().includes('pop') || g.provider_id?.toLowerCase().includes('pop')) {
|
.toLowerCase();
|
||||||
|
const matches =
|
||||||
|
gameProvId === String(activeCategory).trim().toLowerCase();
|
||||||
|
if (
|
||||||
|
g.providerId?.toLowerCase().includes("pop") ||
|
||||||
|
g.provider_id?.toLowerCase().includes("pop")
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
return matches
|
return matches;
|
||||||
})
|
})
|
||||||
.map(mapApiGameToCard)
|
.map(mapApiGameToCard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeCategoryData = providers.find(
|
const activeCategoryData = providers.find(
|
||||||
p => String(p.provider_id || "").trim().toLowerCase() === String(activeCategory).trim().toLowerCase()
|
(p) =>
|
||||||
)
|
String(p.provider_id || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase() === String(activeCategory).trim().toLowerCase(),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[calc(100vh-140px)] overflow-hidden bg-brand-bg">
|
<div className="flex h-[calc(100vh-140px)] overflow-hidden bg-brand-bg">
|
||||||
|
|
@ -180,7 +202,7 @@ export default function VirtualPage() {
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<h2 className="text-lg font-bold text-white uppercase border-l-4 border-brand-primary pl-4">
|
<h2 className="text-lg font-bold text-white uppercase border-l-4 border-brand-primary pl-4">
|
||||||
{activeCategoryData?.provider_name || 'Games'}
|
{activeCategoryData?.provider_name || "Games"}
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveCategory("all")}
|
onClick={() => setActiveCategory("all")}
|
||||||
|
|
@ -200,5 +222,5 @@ export default function VirtualPage() {
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
191
lib/store/betting-types.ts
Normal file
191
lib/store/betting-types.ts
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
/**
|
||||||
|
* Betting API and app types. Used by betting-store and betting-api.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Sport IDs for API (sport_id). Use for live and prematch. */
|
||||||
|
export const SportEnum = {
|
||||||
|
SOCCER: 1,
|
||||||
|
BASKETBALL: 18,
|
||||||
|
TENNIS: 13,
|
||||||
|
VOLLEYBALL: 91,
|
||||||
|
HANDBALL: 78,
|
||||||
|
BASEBALL: 16,
|
||||||
|
HORSE_RACING: 2,
|
||||||
|
GREYHOUNDS: 4,
|
||||||
|
ICE_HOCKEY: 17,
|
||||||
|
SNOOKER: 14,
|
||||||
|
AMERICAN_FOOTBALL: 12,
|
||||||
|
CRICKET: 3,
|
||||||
|
FUTSAL: 83,
|
||||||
|
DARTS: 15,
|
||||||
|
TABLE_TENNIS: 92,
|
||||||
|
BADMINTON: 94,
|
||||||
|
RUGBY_UNION: 8,
|
||||||
|
RUGBY_LEAGUE: 19,
|
||||||
|
AUSTRALIAN_RULES: 36,
|
||||||
|
BOWLS: 66,
|
||||||
|
BOXING: 9,
|
||||||
|
GAELIC_SPORTS: 75,
|
||||||
|
FLOORBALL: 90,
|
||||||
|
BEACH_VOLLEYBALL: 95,
|
||||||
|
WATER_POLO: 110,
|
||||||
|
SQUASH: 107,
|
||||||
|
E_SPORTS: 151,
|
||||||
|
MMA: 162,
|
||||||
|
SURFING: 148,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type SportId = (typeof SportEnum)[keyof typeof SportEnum]
|
||||||
|
|
||||||
|
export type ApiEvent = {
|
||||||
|
id: number
|
||||||
|
source_event_id: string
|
||||||
|
sport_id: number
|
||||||
|
match_name: string
|
||||||
|
home_team: string
|
||||||
|
away_team: string
|
||||||
|
home_team_id: number
|
||||||
|
away_team_id: number
|
||||||
|
home_team_image: string
|
||||||
|
away_team_image: string
|
||||||
|
league_id: number
|
||||||
|
league_name: string
|
||||||
|
league_cc: string
|
||||||
|
start_time: string
|
||||||
|
source: string
|
||||||
|
status: string
|
||||||
|
is_live: boolean
|
||||||
|
is_featured?: boolean
|
||||||
|
is_active?: boolean
|
||||||
|
total_odd_outcomes?: number
|
||||||
|
number_of_bets?: number
|
||||||
|
total_amount?: number
|
||||||
|
average_bet_amount?: number
|
||||||
|
total_potential_winnings?: number
|
||||||
|
score?: string
|
||||||
|
match_minute?: number
|
||||||
|
timer_status?: string
|
||||||
|
added_time?: number
|
||||||
|
match_period?: number
|
||||||
|
fetched_at?: string
|
||||||
|
updated_at?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiLeague = {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
cc: string
|
||||||
|
bet365_id?: number
|
||||||
|
sport_id: number
|
||||||
|
default_is_active: boolean
|
||||||
|
default_is_featured: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiOddsOutcome = {
|
||||||
|
id: string
|
||||||
|
name?: string
|
||||||
|
odds: string
|
||||||
|
header?: string
|
||||||
|
handicap?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiOdds = {
|
||||||
|
id: number
|
||||||
|
event_id: number
|
||||||
|
market_type: string
|
||||||
|
market_name: string
|
||||||
|
market_category: string
|
||||||
|
market_id: number
|
||||||
|
number_of_outcomes: number
|
||||||
|
raw_odds: ApiOddsOutcome[]
|
||||||
|
fetched_at: string
|
||||||
|
expires_at: string
|
||||||
|
is_active: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiTopLeaguesResponse = {
|
||||||
|
leagues: Array<{
|
||||||
|
league_id: number
|
||||||
|
league_name: string
|
||||||
|
league_cc: string
|
||||||
|
league_sport_id: number
|
||||||
|
events: ApiEvent[]
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventsParams = {
|
||||||
|
page?: number
|
||||||
|
page_size?: number
|
||||||
|
sport_id?: number
|
||||||
|
league_id?: number
|
||||||
|
/** RFC3339 datetime; filter events with start_time >= this */
|
||||||
|
first_start_time?: string
|
||||||
|
/** RFC3339 datetime; filter events with start_time <= this (e.g. for 3h/6h/12h windows) */
|
||||||
|
last_start_time?: string
|
||||||
|
/** When true, return only in-play/live events */
|
||||||
|
is_live?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Quick filter key for time-based event filtering */
|
||||||
|
export type QuickFilterKey = "all" | "today" | "3h" | "6h" | "9h" | "12h"
|
||||||
|
|
||||||
|
export type EventsResponse = {
|
||||||
|
data: ApiEvent[]
|
||||||
|
total?: number
|
||||||
|
page?: number
|
||||||
|
total_pages?: number
|
||||||
|
message?: string
|
||||||
|
status?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LeaguesResponse = {
|
||||||
|
data: ApiLeague[]
|
||||||
|
message?: string
|
||||||
|
status?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OddsResponse = {
|
||||||
|
data: ApiOdds[]
|
||||||
|
message?: string
|
||||||
|
status?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DetailMarketSectionFromApi = {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
outcomes: { label: string; odds: number }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MarketTabKey =
|
||||||
|
| "main"
|
||||||
|
| "goals"
|
||||||
|
| "handicap"
|
||||||
|
| "half_time"
|
||||||
|
| "correct_score"
|
||||||
|
| "1st_half"
|
||||||
|
| "2nd_half"
|
||||||
|
| "combo"
|
||||||
|
| "chance_mix"
|
||||||
|
| "home"
|
||||||
|
|
||||||
|
export type TabColumnCell = { id: string; label: string; odds: number }
|
||||||
|
|
||||||
|
export type AppEvent = {
|
||||||
|
id: string
|
||||||
|
sport: string
|
||||||
|
sportIcon: string
|
||||||
|
league: string
|
||||||
|
country: string
|
||||||
|
homeTeam: string
|
||||||
|
awayTeam: string
|
||||||
|
time: string
|
||||||
|
date: string
|
||||||
|
isLive: boolean
|
||||||
|
markets: { id: string; label: string; odds: number }[]
|
||||||
|
totalMarkets: number
|
||||||
|
rawOdds?: ApiOdds[]
|
||||||
|
/** Live: e.g. "2 - 1" */
|
||||||
|
score?: string
|
||||||
|
/** Live: match minute */
|
||||||
|
matchMinute?: number
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user