“vinit5112”
Upgrade UI
6f1f94e
raw
history blame
11.8 kB
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, Bars3Icon } 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 - Mobile optimized */}
<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 shadow-sm`}>
<div className="flex items-center justify-between px-3 md:px-4 py-3 md:py-3">
{/* Left side - Menu and Title */}
<div className="flex items-center space-x-2 md:space-x-4 flex-1 min-w-0">
{/* Hamburger Menu Button - Larger touch target */}
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className={`p-2 md:p-2 rounded-lg md:rounded-xl transition-all duration-200 touch-manipulation ${
darkMode
? 'hover:bg-gray-700 active:bg-gray-600 text-gray-300 hover:text-white'
: 'hover:bg-gray-100 active:bg-gray-200 text-gray-600 hover:text-gray-900'
} min-w-[44px] min-h-[44px] md:min-w-[40px] md:min-h-[40px] flex items-center justify-center`}
title="Toggle menu"
>
<Bars3Icon className="w-6 h-6 md:w-5 md:h-5" />
</button>
{/* App Title - Mobile optimized */}
<button
onClick={goBackToHome}
className="flex-1 min-w-0 text-left group"
>
<h1 className="text-lg md:text-xl font-bold gradient-text hover:opacity-80 transition-opacity truncate">
CA Study Assistant
</h1>
{/* Subtitle for larger screens */}
<p className={`text-xs hidden md:block ${
darkMode ? 'text-gray-400' : 'text-gray-500'
}`}>
AI-powered study companion
</p>
</button>
</div>
{/* Right side - Action buttons */}
<div className="flex items-center space-x-1 md:space-x-2 flex-shrink-0">
{/* Home Button - Only show in chat mode */}
{chatStarted && (
<button
onClick={goBackToHome}
className={`p-2 md:p-2 rounded-lg md:rounded-xl transition-all duration-200 touch-manipulation ${
darkMode
? 'hover:bg-gray-700 active:bg-gray-600 text-gray-300 hover:text-white'
: 'hover:bg-gray-100 active:bg-gray-200 text-gray-600 hover:text-gray-900'
} min-w-[44px] min-h-[44px] md:min-w-[40px] md:min-h-[40px] flex items-center justify-center`}
title="Back to Home"
>
<HomeIcon className="w-6 h-6 md:w-5 md:h-5" />
</button>
)}
{/* Dark Mode Toggle - Larger touch target */}
<button
onClick={toggleDarkMode}
className={`p-2 md:p-2 rounded-lg md:rounded-xl transition-all duration-200 touch-manipulation ${
darkMode
? 'hover:bg-gray-700 active:bg-gray-600 text-gray-300 hover:text-yellow-400'
: 'hover:bg-gray-100 active:bg-gray-200 text-gray-600 hover:text-yellow-600'
} min-w-[44px] min-h-[44px] md:min-w-[40px] md:min-h-[40px] flex items-center justify-center`}
title={darkMode ? 'Switch to light mode' : 'Switch to dark mode'}
>
{darkMode ? (
<SunIcon className="w-6 h-6 md:w-5 md:h-5" />
) : (
<MoonIcon className="w-6 h-6 md:w-5 md:h-5" />
)}
</button>
{/* Conversation count badge - Mobile optimized */}
{conversations.length > 0 && (
<div className={`hidden sm:flex items-center px-2 md:px-3 py-1 md:py-1.5 rounded-full text-xs md:text-sm font-medium ${
darkMode
? 'bg-primary-900/30 text-primary-300 border border-primary-700/50'
: 'bg-primary-50 text-primary-700 border border-primary-200'
}`}>
{conversations.length} chat{conversations.length !== 1 ? 's' : ''}
</div>
)}
</div>
</div>
{/* Mobile conversation indicator - Shows only on small screens */}
{conversations.length > 0 && (
<div className="sm:hidden px-3 pb-2">
<div className={`flex items-center justify-center px-3 py-1 rounded-full text-xs font-medium ${
darkMode
? 'bg-primary-900/30 text-primary-300 border border-primary-700/50'
: 'bg-primary-50 text-primary-700 border border-primary-200'
}`}>
📚 {conversations.length} conversation{conversations.length !== 1 ? 's' : ''} saved
</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 - Mobile optimized */}
<main className={`transition-all duration-200 ${
sidebarOpen ? 'md:ml-80' : 'ml-0'
} pt-16 md:pt-20 min-h-screen`}>
{chatStarted ? (
<ChatInterface
conversationId={activeConversationId}
conversations={conversations}
setConversations={updateConversations}
darkMode={darkMode}
/>
) : (
<WelcomeScreen
onStartChat={handleFirstMessage}
onNewChat={startNewChat}
darkMode={darkMode}
/>
)}
</main>
{/* Toast notifications - Mobile optimized */}
<Toaster
position="top-center"
containerStyle={{
top: '80px', // Account for header height
}}
toastOptions={{
duration: 4000,
style: {
background: darkMode ? '#374151' : '#ffffff',
color: darkMode ? '#f9fafb' : '#111827',
border: darkMode ? '1px solid #4b5563' : '1px solid #e5e7eb',
borderRadius: '12px',
padding: '12px 16px',
fontSize: '14px',
maxWidth: '90vw',
wordBreak: 'break-word',
},
success: {
iconTheme: {
primary: '#10b981',
secondary: '#ffffff',
},
},
error: {
iconTheme: {
primary: '#ef4444',
secondary: '#ffffff',
},
},
}}
/>
{/* Global loading overlay for mobile (if needed) */}
{/* This can be used for app-wide loading states */}
{/* Safe area spacing for mobile devices */}
<style jsx global>{`
@supports (padding: max(0px)) {
.pb-safe {
padding-bottom: max(env(safe-area-inset-bottom), 1rem);
}
}
/* Prevent zoom on double-tap for iOS */
button, input, select, textarea {
touch-action: manipulation;
}
/* Improve scrolling on mobile */
* {
-webkit-overflow-scrolling: touch;
}
/* Custom gradient text */
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Mobile-specific touch improvements */
@media (max-width: 768px) {
/* Increase minimum touch target size */
button, [role="button"] {
min-height: 44px;
min-width: 44px;
}
/* Reduce motion for users who prefer it */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
}
`}</style>
</div>
);
}
export default App;