diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a4e721d..debd1ea 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -18,6 +18,7 @@ import { Live } from './pages/Live' import { NewBets } from './pages/NewBets' import { Watchlist } from './pages/Watchlist' import { HowItWorks } from './pages/HowItWorks' +import { EventDetail } from './pages/EventDetail' const queryClient = new QueryClient({ defaultOptions: { @@ -60,6 +61,7 @@ function App() { } /> } /> } /> + } /> { + const { id } = useParams<{ id: string }>() + const eventId = parseInt(id || '0', 10) + const { isAuthenticated } = useAuthStore() + const queryClient = useQueryClient() + + const { data: event, isLoading, error } = useQuery({ + queryKey: ['sport-event', eventId, isAuthenticated], + queryFn: () => + isAuthenticated + ? sportEventsApi.getEventWithGrid(eventId) + : sportEventsApi.getPublicEventWithGrid(eventId), + enabled: eventId > 0, + }) + + const handleBetCreated = () => { + // Refetch event data to show new bet + queryClient.invalidateQueries({ queryKey: ['sport-event', eventId] }) + queryClient.invalidateQueries({ queryKey: ['public-sport-events'] }) + } + + const handleBetTaken = () => { + // Refetch event data to update bet status + queryClient.invalidateQueries({ queryKey: ['sport-event', eventId] }) + queryClient.invalidateQueries({ queryKey: ['public-sport-events'] }) + } + + if (isLoading) { + return ( +
+
+
+ + + +
+ +
+
+
+ ) + } + + if (error || !event) { + return ( +
+
+
+ + + +
+

Event not found

+
+
+
+ ) + } + + return ( +
+
+
+ + + +
+ +
+
+
+ ) +} diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 157c44a..9416bf2 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -3,16 +3,14 @@ import { Link, useNavigate } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { useAuthStore } from '@/store' import { sportEventsApi } from '@/api/sport-events' -import { SpreadGrid } from '@/components/bets/SpreadGrid' import { Button } from '@/components/common/Button' import { Loading } from '@/components/common/Loading' import { Header } from '@/components/layout/Header' -import { ChevronLeft, TrendingUp, Clock, ArrowRight } from 'lucide-react' +import { TrendingUp, Clock, ArrowRight } from 'lucide-react' export const Home = () => { const navigate = useNavigate() const { isAuthenticated } = useAuthStore() - const [selectedEventId, setSelectedEventId] = useState(null) const [email, setEmail] = useState('') // Use public API for events (works for both authenticated and non-authenticated) @@ -21,24 +19,6 @@ export const Home = () => { queryFn: () => sportEventsApi.getPublicEvents(), }) - // Use authenticated API for event details if logged in, otherwise public - const { data: selectedEvent, isLoading: isLoadingEvent } = useQuery({ - queryKey: ['sport-event', selectedEventId, isAuthenticated], - queryFn: () => - isAuthenticated - ? sportEventsApi.getEventWithGrid(selectedEventId!) - : sportEventsApi.getPublicEventWithGrid(selectedEventId!), - enabled: selectedEventId !== null, - }) - - const handleEventClick = (eventId: number) => { - setSelectedEventId(eventId) - } - - const handleBackToList = () => { - setSelectedEventId(null) - } - const handleSignUp = (e: React.FormEvent) => { e.preventDefault() navigate(`/register?email=${encodeURIComponent(email)}`) @@ -56,47 +36,6 @@ export const Home = () => { ) } - // Selected event view (spread grid) - if (selectedEventId) { - if (isLoadingEvent) { - return ( -
-
-
- -
- -
-
-
- ) - } - - if (selectedEvent) { - return ( -
-
-
- -
- {}} - onBetTaken={() => {}} - /> -
-
-
- ) - } - } - // Calculate total open bets across all events const totalOpenBets = events?.length || 0 @@ -131,11 +70,13 @@ export const Home = () => { )} - {isAuthenticated && ( + {isAuthenticated && events?.[0] && (
- + + +
)} @@ -262,9 +203,9 @@ export const Home = () => { const isUrgent = hoursUntil >= 0 && hoursUntil < 24 return ( - + ) })} diff --git a/frontend/src/pages/NewBets.tsx b/frontend/src/pages/NewBets.tsx index 24b8f6c..16672f4 100644 --- a/frontend/src/pages/NewBets.tsx +++ b/frontend/src/pages/NewBets.tsx @@ -12,7 +12,7 @@ export const NewBets = () => { queryFn: () => sportEventsApi.getPublicEvents(), }) - // Simulate recent bets from events + // Show events with simulated bet activity const recentBets = events?.slice(0, 10).map((event, index) => ({ id: index, event, @@ -39,7 +39,7 @@ export const NewBets = () => { {recentBets?.map((bet) => (
@@ -64,7 +64,7 @@ export const NewBets = () => { {bet.timeAgo} - +
diff --git a/frontend/src/pages/Sports.tsx b/frontend/src/pages/Sports.tsx index 3a42c25..1df7314 100644 --- a/frontend/src/pages/Sports.tsx +++ b/frontend/src/pages/Sports.tsx @@ -1,18 +1,32 @@ import { Link } from 'react-router-dom' +import { useQuery } from '@tanstack/react-query' import { Header } from '@/components/layout/Header' import { Button } from '@/components/common/Button' +import { Loading } from '@/components/common/Loading' +import { sportEventsApi } from '@/api/sport-events' import { Trophy, ArrowRight } from 'lucide-react' export const Sports = () => { + const { data: events, isLoading } = useQuery({ + queryKey: ['public-sport-events'], + queryFn: () => sportEventsApi.getPublicEvents(), + }) + const sports = [ - { name: 'Football', icon: '🏈', leagues: ['NFL', 'NCAA Football'] }, - { name: 'Basketball', icon: '🏀', leagues: ['NBA', 'NCAA Basketball'] }, - { name: 'Hockey', icon: '🏒', leagues: ['NHL'] }, - { name: 'Soccer', icon: '⚽', leagues: ['Premier League', 'La Liga', 'Bundesliga', 'MLS'] }, - { name: 'Baseball', icon: '⚾', leagues: ['MLB'] }, - { name: 'MMA', icon: '🥊', leagues: ['UFC', 'Bellator'] }, + { name: 'Football', icon: '🏈', filter: 'football' }, + { name: 'Basketball', icon: '🏀', filter: 'basketball' }, + { name: 'Hockey', icon: '🏒', filter: 'hockey' }, + { name: 'Soccer', icon: '⚽', filter: 'soccer' }, + { name: 'Baseball', icon: '⚾', filter: 'baseball' }, + { name: 'MMA', icon: '🥊', filter: 'mma' }, ] + // Group events by sport + const eventsBySport = sports.map(sport => ({ + ...sport, + events: events?.filter(e => e.sport.toLowerCase() === sport.filter) || [], + })) + return (
@@ -23,30 +37,49 @@ export const Sports = () => {

Choose your sport and start betting

-
- {sports.map((sport) => ( - -
- {sport.icon} -

{sport.name}

+ {isLoading ? ( + + ) : ( +
+ {eventsBySport.map((sport) => ( +
+
+ {sport.icon} +
+

{sport.name}

+

{sport.events.length} events

+
+
+ {sport.events.length > 0 ? ( +
+ {sport.events.slice(0, 3).map((event) => ( + +

+ {event.home_team} vs {event.away_team} +

+

{event.league}

+ + ))} + {sport.events.length > 3 && ( +

+ +{sport.events.length - 3} more events +

+ )} +
+ ) : ( +

No upcoming events

+ )}
-
- {sport.leagues.map((league) => ( - - {league} - - ))} -
- - ))} -
+ ))} +
+ )}
diff --git a/frontend/tests/screenshots/flow-01-homepage.png b/frontend/tests/screenshots/flow-01-homepage.png index 6b10cff..f0ab0c7 100644 Binary files a/frontend/tests/screenshots/flow-01-homepage.png and b/frontend/tests/screenshots/flow-01-homepage.png differ diff --git a/frontend/tests/screenshots/flow-03-logged-in.png b/frontend/tests/screenshots/flow-03-logged-in.png index cedd5c2..b505d6f 100644 Binary files a/frontend/tests/screenshots/flow-03-logged-in.png and b/frontend/tests/screenshots/flow-03-logged-in.png differ diff --git a/frontend/tests/screenshots/flow-04-events-home.png b/frontend/tests/screenshots/flow-04-events-home.png index 828664b..7411b45 100644 Binary files a/frontend/tests/screenshots/flow-04-events-home.png and b/frontend/tests/screenshots/flow-04-events-home.png differ diff --git a/frontend/tests/screenshots/flow-06-alice-login.png b/frontend/tests/screenshots/flow-06-alice-login.png index b4a3932..fb0ceec 100644 Binary files a/frontend/tests/screenshots/flow-06-alice-login.png and b/frontend/tests/screenshots/flow-06-alice-login.png differ