2026-03-23 02:21:37 -04:00
|
|
|
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
2026-03-23 00:17:32 -04:00
|
|
|
import { useNavigate } from 'react-router-dom';
|
2025-10-30 04:27:43 -04:00
|
|
|
import { useAuth } from '../context/AuthContext';
|
2025-10-30 13:27:55 -04:00
|
|
|
import { useToast } from '../components/Toast';
|
2025-10-30 04:27:43 -04:00
|
|
|
import api from '../api/axios';
|
2026-03-23 02:21:37 -04:00
|
|
|
import { formatLocalDate, isSunday } from '../utils/dateUtils';
|
2025-10-30 04:27:43 -04:00
|
|
|
|
|
|
|
|
function History() {
|
|
|
|
|
const { isAuthenticated } = useAuth();
|
2025-10-30 13:27:55 -04:00
|
|
|
const { error, success } = useToast();
|
2026-03-23 00:17:32 -04:00
|
|
|
const navigate = useNavigate();
|
2026-03-23 02:21:37 -04:00
|
|
|
|
2025-10-30 04:27:43 -04:00
|
|
|
const [sessions, setSessions] = useState([]);
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
2026-03-23 02:21:37 -04:00
|
|
|
const [totalCount, setTotalCount] = useState(0);
|
2025-10-30 04:27:43 -04:00
|
|
|
const [closingSession, setClosingSession] = useState(null);
|
2026-03-23 02:21:37 -04:00
|
|
|
|
|
|
|
|
const [filter, setFilter] = useState(() => localStorage.getItem('history-filter') || 'default');
|
|
|
|
|
const [limit, setLimit] = useState(() => localStorage.getItem('history-show-limit') || '5');
|
|
|
|
|
|
|
|
|
|
const [selectMode, setSelectMode] = useState(false);
|
|
|
|
|
const [selectedIds, setSelectedIds] = useState(new Set());
|
|
|
|
|
const [showBulkDeleteConfirm, setShowBulkDeleteConfirm] = useState(false);
|
|
|
|
|
|
|
|
|
|
const longPressTimer = useRef(null);
|
2025-10-30 04:27:43 -04:00
|
|
|
|
2025-10-30 17:52:44 -04:00
|
|
|
const loadSessions = useCallback(async () => {
|
|
|
|
|
try {
|
2026-03-23 02:21:37 -04:00
|
|
|
const response = await api.get('/sessions', {
|
|
|
|
|
params: { filter, limit }
|
|
|
|
|
});
|
2025-10-30 17:52:44 -04:00
|
|
|
setSessions(response.data);
|
2026-03-23 02:21:37 -04:00
|
|
|
setTotalCount(parseInt(response.headers['x-total-count'] || '0', 10));
|
2025-10-30 17:52:44 -04:00
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to load sessions', err);
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
2026-03-23 02:21:37 -04:00
|
|
|
}, [filter, limit]);
|
2025-10-30 17:52:44 -04:00
|
|
|
|
2025-10-30 04:27:43 -04:00
|
|
|
useEffect(() => {
|
|
|
|
|
loadSessions();
|
2025-10-30 17:52:44 -04:00
|
|
|
}, [loadSessions]);
|
2025-10-30 04:27:43 -04:00
|
|
|
|
2025-10-30 17:34:44 -04:00
|
|
|
useEffect(() => {
|
|
|
|
|
const interval = setInterval(() => {
|
|
|
|
|
loadSessions();
|
|
|
|
|
}, 3000);
|
|
|
|
|
return () => clearInterval(interval);
|
2025-10-30 17:52:44 -04:00
|
|
|
}, [loadSessions]);
|
2025-10-30 17:34:44 -04:00
|
|
|
|
2026-03-23 02:21:37 -04:00
|
|
|
const handleFilterChange = (newFilter) => {
|
|
|
|
|
setFilter(newFilter);
|
|
|
|
|
localStorage.setItem('history-filter', newFilter);
|
|
|
|
|
setSelectedIds(new Set());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleLimitChange = (newLimit) => {
|
|
|
|
|
setLimit(newLimit);
|
|
|
|
|
localStorage.setItem('history-show-limit', newLimit);
|
|
|
|
|
setSelectedIds(new Set());
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-30 04:27:43 -04:00
|
|
|
const handleCloseSession = async (sessionId, notes) => {
|
|
|
|
|
try {
|
|
|
|
|
await api.post(`/sessions/${sessionId}/close`, { notes });
|
|
|
|
|
await loadSessions();
|
|
|
|
|
setClosingSession(null);
|
2025-10-30 13:27:55 -04:00
|
|
|
success('Session ended successfully');
|
|
|
|
|
} catch (err) {
|
|
|
|
|
error('Failed to close session');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-23 02:21:37 -04:00
|
|
|
// Multi-select handlers
|
|
|
|
|
const toggleSelection = (sessionId) => {
|
|
|
|
|
setSelectedIds(prev => {
|
|
|
|
|
const next = new Set(prev);
|
|
|
|
|
if (next.has(sessionId)) {
|
|
|
|
|
next.delete(sessionId);
|
|
|
|
|
} else {
|
|
|
|
|
next.add(sessionId);
|
|
|
|
|
}
|
|
|
|
|
return next;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const exitSelectMode = () => {
|
|
|
|
|
setSelectMode(false);
|
|
|
|
|
setSelectedIds(new Set());
|
|
|
|
|
setShowBulkDeleteConfirm(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePointerDown = (sessionId) => {
|
|
|
|
|
if (!isAuthenticated || selectMode) return;
|
|
|
|
|
longPressTimer.current = setTimeout(() => {
|
|
|
|
|
setSelectMode(true);
|
|
|
|
|
setSelectedIds(new Set([sessionId]));
|
|
|
|
|
}, 500);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePointerUp = () => {
|
|
|
|
|
if (longPressTimer.current) {
|
|
|
|
|
clearTimeout(longPressTimer.current);
|
|
|
|
|
longPressTimer.current = null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleBulkAction = async (action) => {
|
|
|
|
|
try {
|
|
|
|
|
await api.post('/sessions/bulk', {
|
|
|
|
|
action,
|
|
|
|
|
ids: Array.from(selectedIds)
|
|
|
|
|
});
|
|
|
|
|
success(`${selectedIds.size} session${selectedIds.size !== 1 ? 's' : ''} ${action}d`);
|
|
|
|
|
setSelectedIds(new Set());
|
|
|
|
|
setShowBulkDeleteConfirm(false);
|
|
|
|
|
await loadSessions();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
error(err.response?.data?.error || `Failed to ${action} sessions`);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-30 04:27:43 -04:00
|
|
|
if (loading) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex justify-center items-center h-64">
|
2025-10-30 13:27:55 -04:00
|
|
|
<div className="text-xl text-gray-600 dark:text-gray-400">Loading...</div>
|
2025-10-30 04:27:43 -04:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2026-03-23 00:17:32 -04:00
|
|
|
<div className="max-w-2xl mx-auto">
|
2025-10-30 13:27:55 -04:00
|
|
|
<h1 className="text-4xl font-bold mb-8 text-gray-800 dark:text-gray-100">Session History</h1>
|
2025-10-30 04:27:43 -04:00
|
|
|
|
2026-03-23 00:17:32 -04:00
|
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
|
2026-03-23 02:21:37 -04:00
|
|
|
{/* Controls Bar */}
|
|
|
|
|
<div className="flex flex-wrap justify-between items-center gap-3 mb-4">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<div className="flex items-center gap-1.5">
|
|
|
|
|
<span className="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider">Filter:</span>
|
|
|
|
|
<select
|
|
|
|
|
value={filter}
|
|
|
|
|
onChange={(e) => handleFilterChange(e.target.value)}
|
|
|
|
|
className="px-2 py-1.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 cursor-pointer"
|
|
|
|
|
>
|
|
|
|
|
<option value="default">Sessions</option>
|
|
|
|
|
<option value="archived">Archived</option>
|
|
|
|
|
<option value="all">All</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-1.5">
|
|
|
|
|
<span className="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider">Show:</span>
|
|
|
|
|
<select
|
|
|
|
|
value={limit}
|
|
|
|
|
onChange={(e) => handleLimitChange(e.target.value)}
|
|
|
|
|
className="px-2 py-1.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 cursor-pointer"
|
|
|
|
|
>
|
|
|
|
|
<option value="5">5</option>
|
|
|
|
|
<option value="10">10</option>
|
|
|
|
|
<option value="25">25</option>
|
|
|
|
|
<option value="50">50</option>
|
|
|
|
|
<option value="all">All</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<span className="text-xs text-gray-400 dark:text-gray-500">
|
|
|
|
|
{totalCount} session{totalCount !== 1 ? 's' : ''} total
|
|
|
|
|
</span>
|
|
|
|
|
{isAuthenticated && (
|
|
|
|
|
<button
|
|
|
|
|
onClick={selectMode ? exitSelectMode : () => setSelectMode(true)}
|
|
|
|
|
className={`px-3 py-1.5 rounded text-sm font-medium transition ${
|
|
|
|
|
selectMode
|
|
|
|
|
? 'bg-indigo-600 dark:bg-indigo-700 text-white hover:bg-indigo-700 dark:hover:bg-indigo-800'
|
|
|
|
|
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{selectMode ? '✓ Done' : 'Select'}
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-10-30 04:27:43 -04:00
|
|
|
</div>
|
|
|
|
|
|
2026-03-23 02:21:37 -04:00
|
|
|
{/* Session List */}
|
2026-03-23 00:17:32 -04:00
|
|
|
{sessions.length === 0 ? (
|
|
|
|
|
<p className="text-gray-500 dark:text-gray-400">No sessions found</p>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="space-y-2">
|
2026-03-23 02:21:37 -04:00
|
|
|
{sessions.map(session => {
|
|
|
|
|
const isActive = session.is_active === 1;
|
|
|
|
|
const isSelected = selectedIds.has(session.id);
|
|
|
|
|
const isSundaySession = isSunday(session.created_at);
|
|
|
|
|
const isArchived = session.archived === 1;
|
|
|
|
|
const canSelect = selectMode && !isActive;
|
|
|
|
|
|
|
|
|
|
return (
|
2026-03-23 00:17:32 -04:00
|
|
|
<div
|
2026-03-23 02:21:37 -04:00
|
|
|
key={session.id}
|
|
|
|
|
className={`border rounded-lg transition ${
|
|
|
|
|
selectMode && isActive
|
|
|
|
|
? 'opacity-50 cursor-not-allowed border-gray-300 dark:border-gray-600'
|
|
|
|
|
: isSelected
|
|
|
|
|
? 'border-indigo-500 bg-indigo-50 dark:bg-indigo-900/20 cursor-pointer'
|
|
|
|
|
: 'border-gray-300 dark:border-gray-600 hover:border-indigo-400 dark:hover:border-indigo-500 cursor-pointer'
|
|
|
|
|
}`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (selectMode) {
|
|
|
|
|
if (!isActive) toggleSelection(session.id);
|
|
|
|
|
} else {
|
|
|
|
|
navigate(`/history/${session.id}`);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
onPointerDown={() => {
|
|
|
|
|
if (!isActive) handlePointerDown(session.id);
|
|
|
|
|
}}
|
|
|
|
|
onPointerUp={handlePointerUp}
|
|
|
|
|
onPointerLeave={handlePointerUp}
|
2026-03-23 00:17:32 -04:00
|
|
|
>
|
2026-03-23 02:21:37 -04:00
|
|
|
<div className="p-4">
|
|
|
|
|
<div className="flex items-start gap-3">
|
|
|
|
|
{selectMode && (
|
|
|
|
|
<div className={`mt-0.5 w-5 h-5 flex-shrink-0 rounded border-2 flex items-center justify-center ${
|
|
|
|
|
isActive
|
|
|
|
|
? 'border-gray-300 dark:border-gray-600 bg-gray-100 dark:bg-gray-700'
|
|
|
|
|
: isSelected
|
|
|
|
|
? 'border-indigo-600 bg-indigo-600'
|
|
|
|
|
: 'border-gray-300 dark:border-gray-600'
|
|
|
|
|
}`}>
|
|
|
|
|
{isSelected && (
|
|
|
|
|
<span className="text-white text-xs font-bold">✓</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-03-23 00:17:32 -04:00
|
|
|
)}
|
2026-03-23 02:21:37 -04:00
|
|
|
<div className="flex-1 min-w-0">
|
|
|
|
|
<div className="flex justify-between items-center mb-1">
|
|
|
|
|
<div className="flex items-center gap-2 flex-wrap">
|
|
|
|
|
<span className="font-semibold text-gray-800 dark:text-gray-100">
|
|
|
|
|
Session #{session.id}
|
|
|
|
|
</span>
|
|
|
|
|
{isActive && (
|
|
|
|
|
<span className="bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 text-xs px-2 py-0.5 rounded">
|
|
|
|
|
Active
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{isSundaySession && (
|
|
|
|
|
<span className="bg-amber-100 dark:bg-amber-900 text-amber-800 dark:text-amber-200 text-xs px-2 py-0.5 rounded font-semibold">
|
|
|
|
|
☀ Game Night
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{isArchived && (filter === 'all' || filter === 'archived') && (
|
|
|
|
|
<span className="bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 text-xs px-2 py-0.5 rounded">
|
|
|
|
|
Archived
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<span className="text-sm text-gray-500 dark:text-gray-400">
|
|
|
|
|
{session.games_played} game{session.games_played !== 1 ? 's' : ''}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-sm text-gray-500 dark:text-gray-400">
|
|
|
|
|
{formatLocalDate(session.created_at)}
|
|
|
|
|
{isSundaySession && (
|
|
|
|
|
<span className="text-gray-400 dark:text-gray-500"> · Sunday</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
{session.has_notes && session.notes_preview && (
|
|
|
|
|
<div className="mt-2 text-sm text-indigo-400 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/20 px-3 py-2 rounded border-l-2 border-indigo-500">
|
|
|
|
|
{session.notes_preview}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-03-23 00:17:32 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-23 02:21:37 -04:00
|
|
|
|
|
|
|
|
{!selectMode && isAuthenticated && isActive && (
|
|
|
|
|
<div className="px-4 pb-4 pt-0">
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
setClosingSession(session.id);
|
|
|
|
|
}}
|
|
|
|
|
className="w-full bg-orange-600 dark:bg-orange-700 text-white px-4 py-2 rounded text-sm hover:bg-orange-700 dark:hover:bg-orange-800 transition"
|
|
|
|
|
>
|
|
|
|
|
End Session
|
|
|
|
|
</button>
|
2026-03-23 00:17:32 -04:00
|
|
|
</div>
|
2025-10-30 13:27:55 -04:00
|
|
|
)}
|
2025-10-30 04:27:43 -04:00
|
|
|
</div>
|
2026-03-23 02:21:37 -04:00
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-03-23 00:17:32 -04:00
|
|
|
|
2026-03-23 02:21:37 -04:00
|
|
|
{/* Multi-select Action Bar */}
|
|
|
|
|
{selectMode && selectedIds.size > 0 && (
|
|
|
|
|
<div className="sticky bottom-4 mt-4 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 p-4 flex justify-between items-center">
|
|
|
|
|
<span className="text-sm font-semibold text-gray-700 dark:text-gray-300">
|
|
|
|
|
{selectedIds.size} selected
|
|
|
|
|
</span>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
{filter !== 'archived' && (
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleBulkAction('archive')}
|
|
|
|
|
className="px-4 py-2 bg-indigo-600 text-white rounded-lg text-sm hover:bg-indigo-700 transition"
|
|
|
|
|
>
|
|
|
|
|
Archive
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
{filter !== 'default' && (
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleBulkAction('unarchive')}
|
|
|
|
|
className="px-4 py-2 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700 transition"
|
|
|
|
|
>
|
|
|
|
|
Unarchive
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setShowBulkDeleteConfirm(true)}
|
|
|
|
|
className="px-4 py-2 bg-red-600 text-white rounded-lg text-sm hover:bg-red-700 transition"
|
|
|
|
|
>
|
|
|
|
|
Delete
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-03-23 00:17:32 -04:00
|
|
|
</div>
|
|
|
|
|
)}
|
2025-10-30 04:27:43 -04:00
|
|
|
</div>
|
|
|
|
|
|
2026-03-23 02:21:37 -04:00
|
|
|
{/* End Session Modal */}
|
2025-10-30 04:27:43 -04:00
|
|
|
{closingSession && (
|
2025-10-30 13:27:55 -04:00
|
|
|
<EndSessionModal
|
2025-10-30 04:27:43 -04:00
|
|
|
sessionId={closingSession}
|
|
|
|
|
onClose={() => setClosingSession(null)}
|
|
|
|
|
onConfirm={handleCloseSession}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2026-03-23 02:21:37 -04:00
|
|
|
|
|
|
|
|
{/* Bulk Delete Confirmation Modal */}
|
|
|
|
|
{showBulkDeleteConfirm && (
|
|
|
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
|
|
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-8 max-w-md w-full">
|
|
|
|
|
<h2 className="text-2xl font-bold mb-4 text-red-600 dark:text-red-400">
|
|
|
|
|
Delete {selectedIds.size} Session{selectedIds.size !== 1 ? 's' : ''}?
|
|
|
|
|
</h2>
|
|
|
|
|
<p className="text-gray-700 dark:text-gray-300 mb-6">
|
|
|
|
|
This will permanently delete {selectedIds.size} session{selectedIds.size !== 1 ? 's' : ''} and all associated games and chat logs. This action cannot be undone.
|
|
|
|
|
</p>
|
|
|
|
|
<div className="flex gap-4">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleBulkAction('delete')}
|
|
|
|
|
className="flex-1 bg-red-600 dark:bg-red-700 text-white py-3 rounded-lg hover:bg-red-700 dark:hover:bg-red-800 transition font-semibold"
|
|
|
|
|
>
|
|
|
|
|
Delete Permanently
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setShowBulkDeleteConfirm(false)}
|
|
|
|
|
className="flex-1 bg-gray-600 dark:bg-gray-700 text-white py-3 rounded-lg hover:bg-gray-700 dark:hover:bg-gray-800 transition"
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-10-30 04:27:43 -04:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 00:17:32 -04:00
|
|
|
function EndSessionModal({ sessionId, onClose, onConfirm }) {
|
2025-10-30 04:27:43 -04:00
|
|
|
const [notes, setNotes] = useState('');
|
|
|
|
|
|
|
|
|
|
return (
|
2025-10-30 13:27:55 -04:00
|
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
|
|
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-8 max-w-md w-full">
|
|
|
|
|
<h2 className="text-2xl font-bold mb-4 dark:text-gray-100">End Session #{sessionId}</h2>
|
2025-10-30 04:27:43 -04:00
|
|
|
<div className="mb-4">
|
2025-10-30 13:27:55 -04:00
|
|
|
<label className="block text-gray-700 dark:text-gray-300 font-semibold mb-2">
|
2025-10-30 04:27:43 -04:00
|
|
|
Session Notes (optional)
|
|
|
|
|
</label>
|
|
|
|
|
<textarea
|
|
|
|
|
value={notes}
|
|
|
|
|
onChange={(e) => setNotes(e.target.value)}
|
2025-10-30 13:27:55 -04:00
|
|
|
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg h-32 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
2025-10-30 04:27:43 -04:00
|
|
|
placeholder="Add any notes about this session..."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex gap-4">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => onConfirm(sessionId, notes)}
|
2025-10-30 13:27:55 -04:00
|
|
|
className="flex-1 bg-orange-600 dark:bg-orange-700 text-white py-3 rounded-lg hover:bg-orange-700 dark:hover:bg-orange-800 transition"
|
2025-10-30 04:27:43 -04:00
|
|
|
>
|
2025-10-30 13:27:55 -04:00
|
|
|
End Session
|
2025-10-30 04:27:43 -04:00
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={onClose}
|
2025-10-30 13:27:55 -04:00
|
|
|
className="flex-1 bg-gray-600 dark:bg-gray-700 text-white py-3 rounded-lg hover:bg-gray-700 dark:hover:bg-gray-800 transition"
|
2025-10-30 04:27:43 -04:00
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default History;
|