import React, { useState, useEffect } from 'react'; import axios from 'axios'; const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; interface ChatMessage { role: string; content: string; timestamp: string; } interface ChatSession { session_id: string; title: string; created_at: string; updated_at: string; domain_id: string; model_id?: string; messages: ChatMessage[]; } interface ChatHistorySidebarProps { onSessionSelect: (sessionId: string) => void; currentSessionId?: string; onNewChat: () => void; } const ChatHistorySidebar: React.FC = ({ onSessionSelect, currentSessionId, onNewChat }) => { const [sessions, setSessions] = useState([]); const [loading, setLoading] = useState(true); const [editingId, setEditingId] = useState(null); const [editTitle, setEditTitle] = useState(''); useEffect(() => { loadSessions(); }, []); const loadSessions = async () => { try { const response = await axios.get(`${API_URL}/api/chat/sessions/`); setSessions(response.data); } catch (error) { console.error('Failed to load chat sessions:', error); } finally { setLoading(false); } }; const handleDeleteSession = async (sessionId: string, e: React.MouseEvent) => { e.stopPropagation(); if (!confirm('このチャットを削除しますか?')) return; try { await axios.delete(`${API_URL}/api/chat/sessions/${sessionId}`); setSessions(sessions.filter(s => s.session_id !== sessionId)); if (currentSessionId === sessionId) { onNewChat(); } } catch (error) { console.error('Failed to delete session:', error); } }; const handleEditStart = (session: ChatSession, e: React.MouseEvent) => { e.stopPropagation(); setEditingId(session.session_id); setEditTitle(session.title); }; const handleEditSave = async (sessionId: string) => { if (!editTitle.trim()) return; try { await axios.put(`${API_URL}/api/chat/sessions/${sessionId}`, { title: editTitle }); setSessions(sessions.map(s => s.session_id === sessionId ? { ...s, title: editTitle } : s )); setEditingId(null); } catch (error) { console.error('Failed to update session title:', error); } }; const handleEditCancel = () => { setEditingId(null); setEditTitle(''); }; const formatDate = (dateString: string) => { const date = new Date(dateString); const now = new Date(); const diff = now.getTime() - date.getTime(); const days = Math.floor(diff / (1000 * 60 * 60 * 24)); if (days === 0) { return date.toLocaleTimeString('ja-JP', { hour: '2-digit', minute: '2-digit' }); } else if (days === 1) { return '昨日'; } else if (days < 7) { return `${days}日前`; } else { return date.toLocaleDateString('ja-JP', { month: 'short', day: 'numeric' }); } }; return (
{/* Header */}

NULL-AI

{/* Chat List */}
{loading ? (
読み込み中...
) : sessions.length === 0 ? (
チャット履歴はありません
) : ( sessions.map(session => (
onSessionSelect(session.session_id)} style={{ padding: '12px', marginBottom: '4px', backgroundColor: currentSessionId === session.session_id ? '#2a2a2a' : 'transparent', borderRadius: '8px', cursor: 'pointer', transition: 'background-color 0.2s', position: 'relative', border: currentSessionId === session.session_id ? '1px solid #444' : '1px solid transparent' }} onMouseOver={(e) => { if (currentSessionId !== session.session_id) { e.currentTarget.style.backgroundColor = '#252525'; } }} onMouseOut={(e) => { if (currentSessionId !== session.session_id) { e.currentTarget.style.backgroundColor = 'transparent'; } }} > {editingId === session.session_id ? (
e.stopPropagation()}> setEditTitle(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleEditSave(session.session_id); if (e.key === 'Escape') handleEditCancel(); }} autoFocus style={{ width: '100%', padding: '4px 8px', backgroundColor: '#333', border: '1px solid #555', borderRadius: '4px', color: '#fff', fontSize: '14px' }} />
) : ( <>
{session.title}
{formatDate(session.updated_at)}
)}
)) )}
); }; export default ChatHistorySidebar;