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!

Architecture

Fixtures Hub Runtime Networkv1
Application Data Runtime
mercy-gameweek Application
plLive Function
Player Entity Access
Match Entity Access
Standings Retrieval
Player Entity
Match Entity
Browser
React SPA
Fixtures Hub
Landing
FPL Manager
Premier League Standings Source
User Calendar
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