mercy-gameweek

byDawud Raza

import React, { useState, useEffect } from "react"; import { base44 } from "@/api/base44Client"; import { Calendar, Trophy, CalendarClock, Radio, Target } from "lucide-react"; import StatCards from "@/components/StatCards"; import TopPerformers from "@/components/TopPerformers"; import GameweekFixtures from "@/components/GameweekFixtures"; import SidebarLeagueTable from "@/components/SidebarLeagueTable"; import FixtureTicker from "@/components/FixtureTicker"; import CalendarSync from "@/components/CalendarSync"; import { CalendarPlus } from "lucide-react"; import { fetchPlStandings } from "@/lib/plStandings"; export default function FixturesHub() { const [matches, setMatches] = useState([]); const [players, setPlayers] = useState([]); const [table, setTable] = useState([]); const [loading, setLoading] = useState(true); const [syncOpen, setSyncOpen] = useState(false); useEffect(() => { (async () => { try { const [pl] = await Promise.all([base44.entities.Player.list("-avg_points", 60)]); setPlayers(pl); try { const plRes = await base44.functions.invoke("plLive"); if (plRes.data?.matches?.length) { setMatches(plRes.data.matches); } else { setMatches(await base44.entities.Match.list("-kickoff", 50)); } } catch { setMatches(await base44.entities.Match.list("-kickoff", 50)); } fetchPlStandings() .then((r) => setTable(r.table || [])) .catch(() => {}); } finally { setLoading(false); } })(); }, []); if (loading) return ( <div className="flex items-center justify-center h-96"> <div className="w-8 h-8 border-4 border-gold-500/20 border-t-gold-500 rounded-full animate-spin" /> </div> ); const live = matches.filter((m) => m.status === "live"); const upcoming = matches.filter((m) => m.status === "scheduled"); const finished = matches.filter((m) => m.status === "finished"); const currentGw = live[0]?.gameweek || upcoming[0]?.gameweek || finished[0]?.gameweek || null; const gwMatches = currentGw ? matches.filter((m) => m.gameweek === currentGw) : []; const gwLive = gwMatches.filter((m) => m.status === "live"); const gwDone = gwMatches.filter((m) => m.status === "finished"); const gwGoals = gwMatches.reduce( (s, m) => s + (m.home_score || 0) + (m.away_score || 0), 0 ); const statItems = [ { label: currentGw ? `Gameweek ${currentGw}` : "Gameweek", value: currentGw ?? "—", icon: CalendarClock }, { label: "Fixtures", value: gwMatches.length, icon: Calendar }, { label: "Live Now", value: gwLive.length, icon: Radio, pulse: gwLive.length > 0 }, { label: "Goals (GW)", value: gwGoals, icon: Target }, ]; return ( <div className="space-y-6"> <div className="flex items-start justify-between gap-4 flex-wrap"> <div> <h1 className="text-3xl font-bold tracking-tight flex items-center gap-3"> <Calendar className="w-7 h-7 text-gold-400" /> Fixtures Hub </h1> <p className="text-slate-400 mt-2">Live gameweek fixtures, top performers, and the Premier League table.</p> </div> <button onClick={() => setSyncOpen(true)} className="inline-flex items-center gap-2 px-4 py-2.5 rounded-xl bg-gold-500 text-[#0a0e14] font-semibold text-sm hover:bg-gold-400 transition-colors shrink-0" > <CalendarPlus className="w-4 h-4" /> Sync to Calendar </button> </div> <StatCards items={statItems} /> <div className="grid lg:grid-cols-3 gap-6"> <div className="lg:col-span-2 space-y-6"> <TopPerformers players={players} gameweek={currentGw} /> <section> <div className="flex items-center gap-2 mb-4"> <Calendar className="w-5 h-5 text-gold-400" /> <h2 className="text-xl font-bold">Gameweek Fixtures</h2> </div> <GameweekFixtures matches={matches} /> </section> </div> <aside className="space-y-6"> <section> <div className="flex items-center gap-2 mb-4"> <Trophy className="w-5 h-5 text-gold-400" /> <h2 className="text-xl font-bold">League Table</h2> </div> <SidebarLeagueTable table={table} /> </section> <section className="rounded-2xl bg-[#0d131c] border border-gold-500/15 overflow-hidden"> <div className="flex items-center gap-2 px-4 py-3 bg-gradient-to-r from-gold-500/10 to-transparent border-b border-gold-500/15"> <Calendar className="w-4 h-4 text-gold-400" /> <span className="text-sm font-semibold text-gold-300">Fixture Ticker</span> </div> <div className="overflow-x-auto p-3"> <FixtureTicker matches={matches} /> </div> </section> </aside> </div> <CalendarSync open={syncOpen} onClose={() => setSyncOpen(false)} matches={matches} /> </div> ); }

LandingFixtures Hub
Landing

Comments (0)

No comments yet. Be the first!

Landing design preview
Landing: Open mercy-gameweek
Landing: Read companion description
Landing: Enter Fixtures Hub
Fixtures Hub: Wait for data load
Fixtures Hub: Read masthead gameweek
Fixtures Hub: 1. Read stat modules
Fixtures Hub: 2. Read gameweek placeholders
Fixtures Hub: 3. Follow gameweek fixtures
Fixtures Hub: 4. Read top performers
Fixtures Hub: 5. Read league table
Fixtures Hub: 6. Read empty league table
Fixtures Hub: 7. Scan fixture ticker
Fixtures Hub: 8. Press Sync to Calendar
Fixtures Hub: 9. Proceed with calendar sync
Fixtures Hub: 10. Dismiss sync dialog
Fixtures Hub: 11. Return to gameweek state
Landing design preview
Landing: Open mercy-gameweek
Landing: Read companion description
Landing: Enter Fixtures Hub
Fixtures Hub: Wait for data load
Fixtures Hub: Read masthead gameweek
Fixtures Hub: 1. Read stat modules
Fixtures Hub: 2. Read gameweek placeholders
Fixtures Hub: 3. Follow gameweek fixtures
Fixtures Hub: 4. Read top performers
Fixtures Hub: 5. Read league table
Fixtures Hub: 6. Read empty league table
Fixtures Hub: 7. Scan fixture ticker
Fixtures Hub: 8. Press Sync to Calendar
Fixtures Hub: 9. Proceed with calendar sync
Fixtures Hub: 10. Dismiss sync dialog
Fixtures Hub: 11. Return to gameweek state