Fortune-PlayLogic/app/virtual/page.tsx
2026-03-01 14:24:51 +03:00

205 lines
6.6 KiB
TypeScript

"use client"
import { useState, useEffect } from "react"
import Image from "next/image"
import { GamingSidebar } from "@/components/games/gaming-sidebar"
import { GameRow } from "@/components/games/game-row"
import { GameCard } from "@/components/games/game-card"
import { Search, Heart, Clock, Star, Zap, Gamepad2, AlertCircle, LayoutGrid } from "lucide-react"
import { cn } from "@/lib/utils"
import api from "@/lib/api"
interface Provider {
provider_id: string
provider_name: string
logo_dark: string
logo_light: string
enabled: boolean
}
interface ApiGame {
gameId: string
providerId: string
provider: string
name: string
category: string
deviceType: string
hasDemo: boolean
hasFreeBets: boolean
demoUrl?: string
image?: string // In case it gets added
thumbnail?: string
provider_id?: string // Fallback
}
const DEFAULT_IMAGE = "https://st.pokgaming.com/gameThumbnails/246.jpg"
export default function VirtualPage() {
const [activeCategory, setActiveCategory] = useState("all")
const [providers, setProviders] = useState<Provider[]>([])
const [games, setGames] = useState<ApiGame[]>([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const fetchVirtualData = async () => {
try {
setIsLoading(true)
const [providersRes, gamesRes] = await Promise.all([
api.get("/virtual-game/orchestrator/providers"),
api.get("/virtual-game/orchestrator/games", { params: { limit: 2000 } })
])
const pData = providersRes.data
const gData = gamesRes.data
const providersList = pData.providers || pData.data || pData || []
const gamesList = gData.data || gData.games || gData || []
setProviders(Array.isArray(providersList) ? providersList : [])
setGames(Array.isArray(gamesList) ? gamesList : [])
} catch (err: any) {
console.error("Failed to fetch virtual games data:", err)
setError("Failed to load games data.")
} finally {
setIsLoading(false)
}
}
fetchVirtualData()
}, [])
// Create Sidebar Categories dynamically from providers
const sidebarCategories = [
{ id: "all", name: "All Games", icon: LayoutGrid },
...providers.map(p => ({
id: p.provider_id,
name: p.provider_name,
icon: p.logo_dark || p.logo_light || Gamepad2
}))
]
// Filter games based on active category
// If "all", group by provider
let displayedGames: any[] = []
let groupedGames: { title: string, games: any[] }[] = []
const mapApiGameToCard = (game: ApiGame) => ({
id: game.gameId,
title: game.name,
image: game.thumbnail || game.image || DEFAULT_IMAGE,
provider: game.provider
})
if (activeCategory === "all") {
// Group up to 12 games per provider for the rows
providers.forEach(p => {
const providerIdStr = String(p.provider_id || "").trim().toLowerCase()
const providerGames = games
.filter(g => {
const gameProvId = String(g.providerId || g.provider_id || "").trim().toLowerCase()
return gameProvId === providerIdStr
})
.slice(0, 12)
.map(mapApiGameToCard)
if (providerGames.length > 0) {
groupedGames.push({
title: p.provider_name,
games: providerGames
})
}
})
} else {
displayedGames = games
.filter(g => {
const gameProvId = String(g.providerId || g.provider_id || "").trim().toLowerCase()
const matches = gameProvId === String(activeCategory).trim().toLowerCase()
if (g.providerId?.toLowerCase().includes('pop') || g.provider_id?.toLowerCase().includes('pop')) {
}
return matches
})
.map(mapApiGameToCard)
}
const activeCategoryData = providers.find(
p => String(p.provider_id || "").trim().toLowerCase() === String(activeCategory).trim().toLowerCase()
)
return (
<div className="flex h-[calc(100vh-140px)] overflow-hidden bg-brand-bg">
{/* Sidebar */}
<GamingSidebar
title="Virtual"
subtitle="Check out our games!"
activeCategory={activeCategory}
onCategoryChange={setActiveCategory}
categories={sidebarCategories}
/>
{/* Main Content */}
<main className="flex-1 overflow-y-auto scrollbar-none pb-12">
{/* Banner */}
<div className="relative w-full aspect-[4/1] md:aspect-[5/1] overflow-hidden">
<Image
src="/aviator-bannsser.jpg"
alt="Virtual Games Banner"
fill
className="object-cover"
priority
/>
</div>
<div className="mt-0">
{isLoading ? (
<div className="p-8 text-center text-white/60 text-sm font-bold flex items-center justify-center gap-2">
<div className="size-4 rounded-full border-2 border-brand-primary border-t-transparent animate-spin" />
Loading games...
</div>
) : error ? (
<div className="p-8 text-center text-red-400 text-sm font-bold flex items-center justify-center gap-2">
<AlertCircle className="size-4" />
{error}
</div>
) : activeCategory === "all" ? (
// Show all categories
<div className="flex flex-col">
{groupedGames.map((category, index) => (
<GameRow
key={category.title}
title={category.title}
games={category.games}
rows={1}
/>
))}
</div>
) : (
// Show only selected category
<div className="p-4">
<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">
{activeCategoryData?.provider_name || 'Games'}
</h2>
<button
onClick={() => setActiveCategory("all")}
className="text-xs text-white/40 hover:text-white uppercase font-bold"
>
Back to Virtual
</button>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-6">
{displayedGames.map((game, idx) => (
<GameCard key={game.id || idx} {...game} />
))}
</div>
</div>
)}
</div>
</main>
</div>
)
}