example-remote-client/src/components/ConversationSidebar.tsx
Jerome 6605fbcab6
Rename logout to reset and clear all localStorage
- Renamed "Log Out" button to "Reset All Data" to better reflect functionality
- Renamed handleLogout to handleReset for consistency
- Updated confirmation dialog text to say "reset the application"
- Replaced selective localStorage clearing with localStorage.clear() for complete data removal
- Ensures all conversations, authentication, and MCP data is properly cleared

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-12 15:11:26 +01:00

160 lines
No EOL
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Conversation sidebar with conversation list and management
import { useConversation } from '@/contexts/ConversationContext';
import { useInference } from '@/contexts/InferenceContext';
import { useMCP } from '@/contexts/MCPContext';
export function ConversationSidebar() {
const {
conversations,
activeConversationId,
createConversation,
deleteConversation,
setActiveConversation,
} = useConversation();
const { clearProvider, provider } = useInference();
const { connections, removeMcpServer } = useMCP();
const handleNewConversation = () => {
createConversation();
};
const handleReset = () => {
if (confirm('Are you sure you want to reset the application? This will clear all data including conversations, authentication, and server connections.')) {
// Clear inference provider authentication
clearProvider();
// Remove all MCP servers (this will disconnect them)
connections.forEach(connection => {
removeMcpServer(connection.id);
});
// Clear all conversations
conversations.forEach(conversation => {
deleteConversation(conversation.id);
});
// Clear ALL localStorage data
localStorage.clear();
// Refresh the page to ensure clean state
window.location.reload();
}
};
const formatDate = (date: Date): string => {
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([], { hour: '2-digit', minute: '2-digit' });
} else if (days === 1) {
return 'Yesterday';
} else if (days < 7) {
return date.toLocaleDateString([], { weekday: 'short' });
} else {
return date.toLocaleDateString([], { month: 'short', day: 'numeric' });
}
};
const getStatusIndicator = (status: string) => {
switch (status) {
case 'thinking':
return '💭';
case 'calling_tools':
return '🔧';
case 'error':
return '❌';
default:
return '';
}
};
return (
<div className="flex-1 flex flex-col">
{/* New Conversation Button */}
<div className="p-4">
<button
onClick={handleNewConversation}
className="w-full px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
+ New Conversation
</button>
</div>
{/* Conversations List */}
<div className="flex-1 overflow-y-auto">
{conversations.length === 0 ? (
<div className="p-4 text-center text-gray-500 dark:text-gray-400">
<p>No conversations yet.</p>
<p className="text-sm mt-1">Click "New Conversation" to get started.</p>
</div>
) : (
<div className="space-y-1 px-2">
{conversations.map((conversation) => (
<div
key={conversation.id}
onClick={() => setActiveConversation(conversation.id)}
className={`p-3 rounded-lg cursor-pointer transition-colors group ${
activeConversationId === conversation.id
? 'bg-blue-100 dark:bg-blue-900/50 border border-blue-200 dark:border-blue-800'
: 'hover:bg-gray-100 dark:hover:bg-gray-700'
}`}
>
<div className="flex items-start justify-between">
<div className="flex-1 min-w-0">
<div className="flex items-center space-x-2">
<h3 className="text-sm font-medium text-gray-900 dark:text-white truncate">
{conversation.title}
</h3>
<span className="text-xs">
{getStatusIndicator(conversation.status)}
</span>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
{formatDate(conversation.updatedAt)}
</p>
{conversation.messages.length > 0 && (
<p className="text-xs text-gray-600 dark:text-gray-300 mt-1 truncate">
{(() => {
const lastMessage = conversation.messages[conversation.messages.length - 1];
const firstBlock = lastMessage?.content?.[0];
return firstBlock?.type === 'text' ? firstBlock.text : 'Tool interaction';
})()}
</p>
)}
</div>
<button
onClick={(e) => {
e.stopPropagation();
deleteConversation(conversation.id);
}}
className="opacity-0 group-hover:opacity-100 text-gray-400 hover:text-red-500 transition-all p-1"
title="Delete conversation"
>
×
</button>
</div>
</div>
))}
</div>
)}
</div>
{/* Reset Button */}
{provider && (
<div className="p-4 border-t border-gray-200 dark:border-gray-700">
<button
onClick={handleReset}
className="w-full px-4 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
>
Reset All Data
</button>
</div>
)}
</div>
);
}