92 lines
3.2 KiB
TypeScript
92 lines
3.2 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { getChatHistory, type ChatMessage } from '@/lib/copilot';
|
|
|
|
interface ChatHistoryProps {
|
|
onSelectSession: (sessionId: string) => void;
|
|
onClose: () => void;
|
|
}
|
|
|
|
interface SessionGroup {
|
|
sessionId: string;
|
|
firstMessage: string;
|
|
messageCount: number;
|
|
lastActivity: string;
|
|
}
|
|
|
|
export function ChatHistory({ onSelectSession, onClose }: ChatHistoryProps) {
|
|
const [sessions, setSessions] = useState<SessionGroup[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
const loadHistory = async () => {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const result = await getChatHistory(1, 100);
|
|
const sessionMap = new Map<string, ChatMessage[]>();
|
|
for (const msg of result.items) {
|
|
const existing = sessionMap.get(msg.session_id) || [];
|
|
existing.push(msg);
|
|
sessionMap.set(msg.session_id, existing);
|
|
}
|
|
const groups: SessionGroup[] = [];
|
|
for (const [sessionId, msgs] of sessionMap) {
|
|
const userMsg = msgs.find((m) => m.role === 'user');
|
|
groups.push({
|
|
sessionId,
|
|
firstMessage: userMsg?.content || 'Unbekannt',
|
|
messageCount: msgs.length,
|
|
lastActivity: msgs[msgs.length - 1]?.created_at || '',
|
|
});
|
|
}
|
|
groups.sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));
|
|
setSessions(groups);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Verlauf konnte nicht geladen werden');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
loadHistory();
|
|
}, []);
|
|
|
|
const handleSelect = useCallback((sessionId: string) => {
|
|
onSelectSession(sessionId);
|
|
}, [onSelectSession]);
|
|
|
|
return (
|
|
<div className="w-64 border-r border-gray-200 pr-4" data-testid="chat-history">
|
|
<div className="flex items-center justify-between mb-3">
|
|
<h2 className="font-semibold text-sm">Konversationen</h2>
|
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 text-sm" data-testid="close-history">
|
|
✕
|
|
</button>
|
|
</div>
|
|
{loading && <p className="text-sm text-gray-400" data-testid="history-loading">Wird geladen...</p>}
|
|
{error && <p className="text-sm text-red-600" data-testid="history-error">{error}</p>}
|
|
{!loading && !error && sessions.length === 0 && (
|
|
<p className="text-sm text-gray-400" data-testid="history-empty">Keine Konversationen vorhanden.</p>
|
|
)}
|
|
{!loading && !error && sessions.length > 0 && (
|
|
<ul className="space-y-1">
|
|
{sessions.map((session) => (
|
|
<li key={session.sessionId}>
|
|
<button
|
|
onClick={() => handleSelect(session.sessionId)}
|
|
className="w-full text-left p-2 rounded hover:bg-gray-100 text-sm"
|
|
data-testid={`history-session-${session.sessionId}`}
|
|
>
|
|
<p className="font-medium truncate">{session.firstMessage}</p>
|
|
<p className="text-xs text-gray-400">{session.messageCount} Nachrichten</p>
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|