Spaces:
Running
Running
import React, { useState, useEffect } from 'react'; | |
import { Toaster } from 'react-hot-toast'; | |
import ChatInterface from './components/ChatInterface'; | |
import Sidebar from './components/Sidebar'; | |
import WelcomeScreen from './components/WelcomeScreen'; | |
import { SunIcon, MoonIcon, HomeIcon } from '@heroicons/react/24/outline'; | |
import ConversationStorage from './utils/conversationStorage'; | |
function App() { | |
const [darkMode, setDarkMode] = useState(false); | |
const [sidebarOpen, setSidebarOpen] = useState(false); | |
const [chatStarted, setChatStarted] = useState(false); | |
const [conversations, setConversations] = useState([]); | |
const [activeConversationId, setActiveConversationId] = useState(null); | |
useEffect(() => { | |
// Check for saved theme preference or default to light mode | |
const savedTheme = localStorage.getItem('theme'); | |
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; | |
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) { | |
setDarkMode(true); | |
document.documentElement.classList.add('dark'); | |
} | |
// Load conversations from localStorage | |
const savedConversations = ConversationStorage.loadConversations(); | |
if (savedConversations.length > 0) { | |
setConversations(savedConversations); | |
setChatStarted(true); | |
// Set the most recent conversation as active | |
setActiveConversationId(savedConversations[0].id); | |
} | |
}, []); | |
const toggleDarkMode = () => { | |
setDarkMode(!darkMode); | |
if (darkMode) { | |
document.documentElement.classList.remove('dark'); | |
localStorage.setItem('theme', 'light'); | |
} else { | |
document.documentElement.classList.add('dark'); | |
localStorage.setItem('theme', 'dark'); | |
} | |
}; | |
const startNewChat = () => { | |
const newConversation = { | |
id: Date.now().toString(), | |
title: 'New Conversation', | |
messages: [], | |
createdAt: new Date(), | |
updatedAt: new Date(), | |
}; | |
// Save to localStorage | |
ConversationStorage.addConversation(newConversation); | |
setConversations(prev => [newConversation, ...prev]); | |
setActiveConversationId(newConversation.id); | |
setChatStarted(true); | |
setSidebarOpen(false); | |
}; | |
const deleteConversation = (conversationId) => { | |
// Delete from localStorage | |
ConversationStorage.deleteConversation(conversationId); | |
setConversations(prev => prev.filter(conv => conv.id !== conversationId)); | |
// If the deleted conversation was active, switch to another one | |
if (activeConversationId === conversationId) { | |
const remainingConversations = conversations.filter(conv => conv.id !== conversationId); | |
if (remainingConversations.length > 0) { | |
setActiveConversationId(remainingConversations[0].id); | |
} else { | |
setActiveConversationId(null); | |
setChatStarted(false); | |
} | |
} | |
}; | |
const updateConversations = (updatedConversations) => { | |
setConversations(updatedConversations); | |
// Save to localStorage | |
ConversationStorage.saveConversations(updatedConversations); | |
}; | |
const handleFirstMessage = (message) => { | |
if (!chatStarted) { | |
startNewChat(); | |
} | |
}; | |
const goBackToHome = () => { | |
setActiveConversationId(null); | |
setChatStarted(false); | |
setSidebarOpen(false); | |
}; | |
return ( | |
<div className={`min-h-screen transition-colors duration-200 ${ | |
darkMode | |
? 'bg-gray-900 text-white' | |
: 'bg-gray-50 text-gray-900' | |
}`}> | |
{/* Header */} | |
<header className={`fixed top-0 left-0 right-0 z-50 ${ | |
darkMode | |
? 'bg-gray-800/95 border-gray-700' | |
: 'bg-white/95 border-gray-200' | |
} backdrop-blur-sm border-b`}> | |
<div className="flex items-center justify-between px-4 py-3"> | |
<div className="flex items-center space-x-4"> | |
<button | |
onClick={() => setSidebarOpen(!sidebarOpen)} | |
className={`p-2 rounded-lg transition-colors ${ | |
darkMode | |
? 'hover:bg-gray-700 text-gray-300' | |
: 'hover:bg-gray-100 text-gray-600' | |
}`} | |
> | |
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" /> | |
</svg> | |
</button> | |
<button | |
onClick={goBackToHome} | |
className="text-lg font-semibold gradient-text hover:opacity-80 transition-opacity" | |
> | |
CA Study Assistant | |
</button> | |
</div> | |
<div className="flex items-center space-x-2"> | |
{chatStarted && ( | |
<button | |
onClick={goBackToHome} | |
className={`p-2 rounded-lg transition-colors ${ | |
darkMode | |
? 'hover:bg-gray-700 text-gray-300' | |
: 'hover:bg-gray-100 text-gray-600' | |
}`} | |
title="Back to Home" | |
> | |
<HomeIcon className="w-5 h-5" /> | |
</button> | |
)} | |
<button | |
onClick={toggleDarkMode} | |
className={`p-2 rounded-lg transition-colors ${ | |
darkMode | |
? 'hover:bg-gray-700 text-gray-300' | |
: 'hover:bg-gray-100 text-gray-600' | |
}`} | |
> | |
{darkMode ? ( | |
<SunIcon className="w-5 h-5" /> | |
) : ( | |
<MoonIcon className="w-5 h-5" /> | |
)} | |
</button> | |
</div> | |
</div> | |
</header> | |
{/* Sidebar */} | |
<Sidebar | |
open={sidebarOpen} | |
onClose={() => setSidebarOpen(false)} | |
conversations={conversations} | |
activeConversationId={activeConversationId} | |
onConversationSelect={setActiveConversationId} | |
onNewChat={startNewChat} | |
onDeleteConversation={deleteConversation} | |
onBackToHome={goBackToHome} | |
darkMode={darkMode} | |
/> | |
{/* Main Content */} | |
<main className={`transition-all duration-200 ${ | |
sidebarOpen ? 'md:ml-64' : 'ml-0' | |
} pt-16`}> | |
{chatStarted ? ( | |
<ChatInterface | |
conversationId={activeConversationId} | |
conversations={conversations} | |
setConversations={updateConversations} | |
darkMode={darkMode} | |
/> | |
) : ( | |
<WelcomeScreen | |
onStartChat={handleFirstMessage} | |
onNewChat={startNewChat} | |
darkMode={darkMode} | |
/> | |
)} | |
</main> | |
{/* Toast notifications */} | |
<Toaster | |
position="top-right" | |
toastOptions={{ | |
duration: 4000, | |
style: { | |
background: darkMode ? '#374151' : '#ffffff', | |
color: darkMode ? '#f9fafb' : '#111827', | |
border: darkMode ? '1px solid #4b5563' : '1px solid #e5e7eb', | |
}, | |
}} | |
/> | |
</div> | |
); | |
} | |
export default App; |