diff --git a/frontend/src/pages/History.jsx b/frontend/src/pages/History.jsx index 1de7a39..4dbdea4 100644 --- a/frontend/src/pages/History.jsx +++ b/frontend/src/pages/History.jsx @@ -1,21 +1,18 @@ import React, { useState, useEffect, useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; import { useToast } from '../components/Toast'; import api from '../api/axios'; -import { formatLocalDateTime, formatLocalDate, formatLocalTime } from '../utils/dateUtils'; -import PopularityBadge from '../components/PopularityBadge'; +import { formatLocalDate } from '../utils/dateUtils'; function History() { const { isAuthenticated } = useAuth(); const { error, success } = useToast(); + const navigate = useNavigate(); const [sessions, setSessions] = useState([]); - const [selectedSession, setSelectedSession] = useState(null); - const [sessionGames, setSessionGames] = useState([]); const [loading, setLoading] = useState(true); - const [showChatImport, setShowChatImport] = useState(false); const [closingSession, setClosingSession] = useState(null); const [showAllSessions, setShowAllSessions] = useState(false); - const [deletingSession, setDeletingSession] = useState(null); const loadSessions = useCallback(async () => { try { @@ -28,124 +25,28 @@ function History() { } }, []); - const refreshSessionGames = useCallback(async (sessionId, silent = false) => { - try { - const response = await api.get(`/sessions/${sessionId}/games`); - // Reverse chronological order (most recent first) - create new array to avoid mutation - setSessionGames([...response.data].reverse()); - } catch (err) { - if (!silent) { - console.error('Failed to load session games', err); - } - } - }, []); - useEffect(() => { loadSessions(); }, [loadSessions]); - // Auto-select active session if navigating from picker - useEffect(() => { - if (sessions.length > 0 && !selectedSession) { - const activeSession = sessions.find(s => s.is_active === 1); - if (activeSession) { - loadSessionGames(activeSession.id); - } - } - }, [sessions, selectedSession]); - - // Poll for session list updates (to detect when sessions end/start) useEffect(() => { const interval = setInterval(() => { loadSessions(); }, 3000); - return () => clearInterval(interval); }, [loadSessions]); - // Poll for updates on active session games - useEffect(() => { - if (!selectedSession) return; - - const currentSession = sessions.find(s => s.id === selectedSession); - if (!currentSession || currentSession.is_active !== 1) return; - - // Refresh games every 3 seconds for active session - const interval = setInterval(() => { - refreshSessionGames(selectedSession, true); // silent refresh - }, 3000); - - return () => clearInterval(interval); - }, [selectedSession, sessions, refreshSessionGames]); - - const handleExport = async (sessionId, format) => { - try { - const response = await api.get(`/sessions/${sessionId}/export?format=${format}`, { - responseType: 'blob' - }); - - // Create download link - const url = window.URL.createObjectURL(new Blob([response.data])); - const link = document.createElement('a'); - link.href = url; - link.setAttribute('download', `session-${sessionId}.${format === 'json' ? 'json' : 'txt'}`); - document.body.appendChild(link); - link.click(); - link.parentNode.removeChild(link); - window.URL.revokeObjectURL(url); - - success(`Session exported as ${format.toUpperCase()}`); - } catch (err) { - console.error('Failed to export session', err); - error('Failed to export session'); - } - }; - - const loadSessionGames = async (sessionId, silent = false) => { - try { - const response = await api.get(`/sessions/${sessionId}/games`); - // Reverse chronological order (most recent first) - create new array to avoid mutation - setSessionGames([...response.data].reverse()); - if (!silent) { - setSelectedSession(sessionId); - } - } catch (err) { - if (!silent) { - console.error('Failed to load session games', err); - } - } - }; - const handleCloseSession = async (sessionId, notes) => { try { await api.post(`/sessions/${sessionId}/close`, { notes }); await loadSessions(); setClosingSession(null); - if (selectedSession === sessionId) { - // Reload the session details to show updated state - loadSessionGames(sessionId); - } success('Session ended successfully'); } catch (err) { error('Failed to close session'); } }; - const handleDeleteSession = async (sessionId) => { - try { - await api.delete(`/sessions/${sessionId}`); - await loadSessions(); - setDeletingSession(null); - if (selectedSession === sessionId) { - setSelectedSession(null); - setSessionGames([]); - } - success('Session deleted successfully'); - } catch (err) { - error('Failed to delete session: ' + (err.response?.data?.error || err.message)); - } - }; - if (loading) { return (
@@ -155,315 +56,97 @@ function History() { } return ( -
+

Session History

-
- {/* Sessions List */} -
-
-
-

Sessions

- {sessions.length > 3 && ( - - )} -
- - {sessions.length === 0 ? ( -

No sessions found

- ) : ( -
- {(showAllSessions ? sessions : sessions.slice(0, 3)).map(session => ( -
- {/* Main session info - clickable */} -
loadSessionGames(session.id)} - className="p-3 cursor-pointer" - > -
-
-
- - Session #{session.id} - - {session.is_active === 1 && ( - - Active - - )} -
-
- {formatLocalDate(session.created_at)} - - {session.games_played} game{session.games_played !== 1 ? 's' : ''} -
-
-
-
- - {/* Action buttons for authenticated users */} - {isAuthenticated && ( -
- {session.is_active === 1 ? ( - - ) : ( - - )} -
- )} -
- ))} -
- )} -
-
- - {/* Session Details */} -
- {selectedSession ? ( -
-
-
-
-

- Session #{selectedSession} -

- {sessions.find(s => s.id === selectedSession)?.is_active === 1 && ( - - 🟢 Active - - )} -
-

- {sessions.find(s => s.id === selectedSession)?.created_at && - formatLocalDateTime(sessions.find(s => s.id === selectedSession).created_at)} -

- {sessions.find(s => s.id === selectedSession)?.is_active === 1 && ( -

- Games update automatically -

- )} -
- -
- {isAuthenticated && sessions.find(s => s.id === selectedSession)?.is_active === 1 && ( - - )} - {isAuthenticated && ( - <> - - - - )} -
-
- - {showChatImport && ( - setShowChatImport(false)} - onImportComplete={() => { - loadSessionGames(selectedSession); - setShowChatImport(false); - }} - /> - )} - - {sessionGames.length === 0 ? ( -

No games played in this session

- ) : ( -
-

- Games Played ({sessionGames.length}) -

-
- {sessionGames.map((game, index) => ( -
-
-
-
- {sessionGames.length - index}. {game.title} -
-
{game.pack_name}
-
-
-
- {formatLocalTime(game.played_at)} -
- {game.manually_added === 1 && ( - - Manual - - )} -
-
- -
-
- Players: {game.min_players}-{game.max_players} -
-
- Type: {game.game_type || 'N/A'} -
-
- - Popularity: - - -
-
-
- ))} -
-
- )} -
- ) : ( -
-

Select a session to view details

-
+
+
+

Sessions

+ {sessions.length > 5 && ( + )}
+ + {sessions.length === 0 ? ( +

No sessions found

+ ) : ( +
+ {(showAllSessions ? sessions : sessions.slice(0, 5)).map(session => ( +
+
navigate(`/history/${session.id}`)} + className="p-4" + > +
+
+ + Session #{session.id} + + {session.is_active === 1 && ( + + Active + + )} +
+ + {session.games_played} game{session.games_played !== 1 ? 's' : ''} + +
+
+ {formatLocalDate(session.created_at)} +
+ {session.has_notes && session.notes_preview && ( +
+ {session.notes_preview} +
+ )} +
+ + {isAuthenticated && session.is_active === 1 && ( +
+ +
+ )} +
+ ))} +
+ )}
- {/* End Session Modal */} {closingSession && ( setClosingSession(null)} onConfirm={handleCloseSession} - onShowChatImport={() => { - setShowChatImport(true); - if (closingSession !== selectedSession) { - loadSessionGames(closingSession); - } - }} /> )} - - {/* Delete Confirmation Modal */} - {deletingSession && ( -
-
-

Delete Session?

-

- Are you sure you want to delete Session #{deletingSession}? - This will permanently delete all games and chat logs associated with this session. This action cannot be undone. -

-
- - -
-
-
- )}
); } -function EndSessionModal({ sessionId, sessionGames, onClose, onConfirm, onShowChatImport }) { +function EndSessionModal({ sessionId, onClose, onConfirm }) { const [notes, setNotes] = useState(''); - - // Check if any games have been voted on (popularity != 0) - const hasPopularityData = sessionGames.some(game => game.popularity_score !== 0); - const showPopularityWarning = sessionGames.length > 0 && !hasPopularityData; return (

End Session #{sessionId}

- - {/* Popularity Warning */} - {showPopularityWarning && ( -
-
- ⚠️ -
-

- No Popularity Data -

-

- You haven't imported chat reactions yet. Import now to track which games your players loved! -

- -
-
-
- )} -
-