import React, { useState, useEffect, useCallback } from 'react'; import { useParams, useNavigate, Link } from 'react-router-dom'; import Markdown from 'react-markdown'; import { useAuth } from '../context/AuthContext'; import { useToast } from '../components/Toast'; import api from '../api/axios'; import { formatLocalDateTime, formatLocalTime } from '../utils/dateUtils'; import PopularityBadge from '../components/PopularityBadge'; function SessionDetail() { const { id } = useParams(); const navigate = useNavigate(); const { isAuthenticated } = useAuth(); const { error: showError, success } = useToast(); const [session, setSession] = useState(null); const [games, setGames] = useState([]); const [loading, setLoading] = useState(true); const [editing, setEditing] = useState(false); const [editedNotes, setEditedNotes] = useState(''); const [saving, setSaving] = useState(false); const [showDeleteNotesConfirm, setShowDeleteNotesConfirm] = useState(false); const [showDeleteSessionConfirm, setShowDeleteSessionConfirm] = useState(false); const [showChatImport, setShowChatImport] = useState(false); const [closingSession, setClosingSession] = useState(false); const loadSession = useCallback(async () => { try { const res = await api.get(`/sessions/${id}`); setSession(res.data); } catch (err) { if (err.response?.status === 404) { navigate('/history', { replace: true }); } console.error('Failed to load session', err); } }, [id, navigate]); const loadGames = useCallback(async () => { try { const res = await api.get(`/sessions/${id}/games`); setGames([...res.data].reverse()); } catch (err) { console.error('Failed to load session games', err); } }, [id]); useEffect(() => { Promise.all([loadSession(), loadGames()]).finally(() => setLoading(false)); }, [loadSession, loadGames]); useEffect(() => { if (!session || session.is_active !== 1) return; const interval = setInterval(() => { loadSession(); loadGames(); }, 3000); return () => clearInterval(interval); }, [session, loadSession, loadGames]); const handleSaveNotes = async () => { setSaving(true); try { await api.put(`/sessions/${id}/notes`, { notes: editedNotes }); await loadSession(); setEditing(false); success('Notes saved'); } catch (err) { showError('Failed to save notes'); } finally { setSaving(false); } }; const handleDeleteNotes = async () => { try { await api.delete(`/sessions/${id}/notes`); await loadSession(); setEditing(false); setShowDeleteNotesConfirm(false); success('Notes deleted'); } catch (err) { showError('Failed to delete notes'); } }; const handleDeleteSession = async () => { try { await api.delete(`/sessions/${id}`); success('Session deleted'); navigate('/history', { replace: true }); } catch (err) { showError('Failed to delete session: ' + (err.response?.data?.error || err.message)); } }; const handleCloseSession = async (sessionId, notes) => { try { await api.post(`/sessions/${sessionId}/close`, { notes }); await loadSession(); await loadGames(); setClosingSession(false); success('Session ended successfully'); } catch (err) { showError('Failed to close session'); } }; const handleExport = async (format) => { try { const response = await api.get(`/sessions/${id}/export?format=${format}`, { responseType: 'blob' }); const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', `session-${id}.${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) { showError('Failed to export session'); } }; const startEditing = () => { setEditedNotes(session.notes || ''); setEditing(true); }; if (loading) { return (
Loading...
); } if (!session) { return (
Session not found
); } return (
← Back to History

Session #{session.id}

{session.is_active === 1 && ( 🟢 Active )}

{formatLocalDateTime(session.created_at)} {' • '} {session.games_played} game{session.games_played !== 1 ? 's' : ''} played

{isAuthenticated && session.is_active === 1 && ( <> )} {isAuthenticated && ( <> )} {isAuthenticated && session.is_active === 0 && ( )}
setEditing(false)} onDeleteNotes={handleDeleteNotes} onShowDeleteConfirm={() => setShowDeleteNotesConfirm(true)} onHideDeleteConfirm={() => setShowDeleteNotesConfirm(false)} />
{showChatImport && (
setShowChatImport(false)} onImportComplete={() => { loadGames(); setShowChatImport(false); }} />
)}
{games.length === 0 ? (

No games played in this session

) : ( <>

Games Played ({games.length})

{games.map((game, index) => (
{games.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:
))}
)}
{closingSession && ( setClosingSession(false)} onConfirm={handleCloseSession} onShowChatImport={() => { setShowChatImport(true); setClosingSession(false); }} /> )} {showDeleteSessionConfirm && (

Delete Session?

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

)}
); } function NotesSection({ session, isAuthenticated, editing, editedNotes, saving, showDeleteNotesConfirm, onStartEditing, onSetEditedNotes, onSave, onCancel, onDeleteNotes, onShowDeleteConfirm, onHideDeleteConfirm, }) { if (editing) { return (

Session Notes