diff --git a/src/App.tsx b/src/App.tsx index 3d42fb670cf35e1ed14c9546e66c27274a1c2a8a..db3527fe35b0bfb7fedffce8440c6eb15c358688 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ -import { ChatInterface } from "./components/ChatInterface"; - -export default function App() { - return ; +import { ChatInterface } from "./components/ChatInterface"; + +export default function App() { + return ; } \ No newline at end of file diff --git a/src/Attributions.md b/src/Attributions.md index 9b7cd4e13487db2e20f4f3844255f0b967db8448..a6da069f4562f5d3fe6132e22e9731162ed55190 100644 --- a/src/Attributions.md +++ b/src/Attributions.md @@ -1,3 +1,3 @@ -This Figma Make file includes components from [shadcn/ui](https://ui.shadcn.com/) used under [MIT license](https://github.com/shadcn-ui/ui/blob/main/LICENSE.md). - +This Figma Make file includes components from [shadcn/ui](https://ui.shadcn.com/) used under [MIT license](https://github.com/shadcn-ui/ui/blob/main/LICENSE.md). + This Figma Make file includes photos from [Unsplash](https://unsplash.com) used under [license](https://unsplash.com/license). \ No newline at end of file diff --git a/src/components/ChatInterface.tsx b/src/components/ChatInterface.tsx index 208eb0f3a91b5d68cd81218a43a258e1d4521871..190fdfb925a58da048349759bea1c72fd7233a1f 100644 --- a/src/components/ChatInterface.tsx +++ b/src/components/ChatInterface.tsx @@ -1,772 +1,772 @@ -import { useState, useRef, useEffect, type ReactNode } from 'react'; -import { Send, MessageCircle, Settings, Search, BarChart3, User, Bot, Sparkles, HelpCircle, Moon, Sun, Globe, Database, BookOpen, Crown, Zap, Menu, ChevronLeft, ChevronRight, ThumbsUp, ThumbsDown, MessageSquare } from 'lucide-react'; -import { Button } from './ui/button'; -import { Input } from './ui/input'; -import { Message } from './Message'; -import { TypingIndicator } from './TypingIndicator'; -import { Sheet, SheetContent, SheetTrigger, SheetTitle, SheetDescription } from './ui/sheet'; -import { Textarea } from './ui/textarea'; -import { motion, AnimatePresence } from 'motion/react'; - -interface ChatMessage { - id: string; - content: string; - isUser: boolean; - timestamp: Date; - isPlusResponse?: boolean; - sources?: Array<{ name: string; icon: ReactNode; url?: string }>; -} - -export function ChatInterface() { - const [messages, setMessages] = useState([]); - const [inputValue, setInputValue] = useState(''); - const [isTyping, setIsTyping] = useState(false); - const [showComingSoon, setShowComingSoon] = useState(false); - const [isDarkMode, setIsDarkMode] = useState(false); - const [isUnivAiPlusMode, setIsUnivAiPlusMode] = useState(false); - const [hasUsedPlusResponse, setHasUsedPlusResponse] = useState(false); - const [currentSources, setCurrentSources] = useState>([]); - const [showFeedback, setShowFeedback] = useState(false); - const [feedbackType, setFeedbackType] = useState<'positive' | 'negative' | null>(null); - const [feedbackComment, setFeedbackComment] = useState(''); - const [lastBotMessageId, setLastBotMessageId] = useState(null); - const [leftSheetOpen, setLeftSheetOpen] = useState(false); - const [rightSheetOpen, setRightSheetOpen] = useState(false); - const messagesEndRef = useRef(null); - - // Initialize messages based on current mode - useEffect(() => { - const initialMessage: ChatMessage = { - id: '1', - content: isUnivAiPlusMode - ? "Feel free to explore UnivAi+++ for a richer, more responsive conversation experience. Please note: due to limited computational resources, the AI can provide only one response per session. Make it count!" - : "Hello! I'm your AI assistant. How can I help you today? Feel free to ask me anything!", - isUser: false, - timestamp: new Date(), - isPlusResponse: isUnivAiPlusMode - }; - setMessages([initialMessage]); - setHasUsedPlusResponse(false); - setCurrentSources([]); // Reset sources when switching modes - setShowFeedback(false); - setFeedbackType(null); - setFeedbackComment(''); - }, [isUnivAiPlusMode]); - - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }; - - useEffect(() => { - scrollToBottom(); - }, [messages, isTyping]); - - useEffect(() => { - // Apply dark mode class to document - if (isDarkMode) { - document.documentElement.classList.add('dark'); - } else { - document.documentElement.classList.remove('dark'); - } - }, [isDarkMode]); - - const handleSendMessage = async (e: React.FormEvent) => { - e.preventDefault(); - if (!inputValue.trim()) return; - if (isUnivAiPlusMode && hasUsedPlusResponse) return; // Prevent sending if already used Plus response - - const userMessage: ChatMessage = { - id: Date.now().toString(), - content: inputValue, - isUser: true, - timestamp: new Date(), - }; - - setMessages(prev => [...prev, userMessage]); - setInputValue(''); - setIsTyping(true); - - try { - const res = await fetch('/api/chat', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - query: inputValue, - dev_mode: isUnivAiPlusMode, - }), - }); - const data = await res.json(); - // Optionally, you can extract sources from data if provided - const botResponse: ChatMessage = { - id: (Date.now() + 1).toString(), - content: data.response, - isUser: false, - timestamp: new Date(), - isPlusResponse: isUnivAiPlusMode, - sources: [], // Optionally fill from data.source - }; - - setMessages(prev => [...prev, botResponse]); - setCurrentSources([]); // Optionally update if sources are available - setIsTyping(false); - setLastBotMessageId(botResponse.id); - - // Show feedback form after bot response - setShowFeedback(true); - setFeedbackType(null); - setFeedbackComment(''); - - // Mark Plus response as used - if (isUnivAiPlusMode) { - setHasUsedPlusResponse(true); - } - } catch (err) { - setIsTyping(false); - } - }; - - const handleFeedbackSubmit = async () => { - console.log('Feedback submitted:', { messageId: lastBotMessageId, feedbackType, feedbackComment }); - if (!lastBotMessageId) return; - const lastBotMsg = messages.find(m => m.id === lastBotMessageId); - if (!lastBotMsg) return; - try { - await fetch('/api/feedback', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - query: lastBotMsg.content, - response: lastBotMsg.content, - feedback: feedbackType, - }), - }); - setFeedbackType(null); - setFeedbackComment(''); - } catch (err) { - // Optionally handle error - } - }; - - - - const handleComingSoonClick = () => { - setShowComingSoon(true); - setTimeout(() => { - setShowComingSoon(false); - }, 2000); - }; - - const toggleDarkMode = () => { - setIsDarkMode(!isDarkMode); - }; - - const toggleUnivAiMode = () => { - setIsUnivAiPlusMode(!isUnivAiPlusMode); - }; - - const sidebarItems = [ - { icon: , active: true }, - { icon: , active: false }, - { icon: , active: false }, - { icon: , active: false }, - { icon: , active: false }, - ]; - - const isInputDisabled = isTyping || (isUnivAiPlusMode && hasUsedPlusResponse); - - // Sidebar content components - const LeftSidebarContent = () => ( - <> - {/* Logo */} -
- {isUnivAiPlusMode ? : } -
- - {/* Navigation */} -
- {sidebarItems.map((item, index) => ( - - {item.active ? item.icon : } - - ))} -
- - ); - - const RightSidebarContent = () => ( - <> - {/* AI Status Card - More compact */} -
-
-
- {isUnivAiPlusMode ? : } -
-
-
- {/* Fixed height container for title */} -
- - {isUnivAiPlusMode ? 'UnivAi+++' : 'UnivAi'} - {isUnivAiPlusMode && } - -
- {/* Fixed height container for description */} -
- - {isUnivAiPlusMode - ? 'Smarter with human-like responses' - : 'Any PUP-Related Queries?' - } - -
- {/* Fixed button */} -
- -
-
-
- - {/* Chat Stats - Equalized for both modes */} -
-

Chat Statistics

-
-
- Response Time - {isUnivAiPlusMode ? '2.5s' : '1.2s'} -
-
- Status - {isUnivAiPlusMode ? 'Premium' : 'Online'} -
-
- {isUnivAiPlusMode ? 'Responses Left' : 'Queries Processed'} - {isUnivAiPlusMode ? (hasUsedPlusResponse ? '0' : '1') : '∞'} -
-
-
- - {/* Sources - More compact */} -
-

Sources

-
- {currentSources.length === 0 ? ( -
-

- Sources will appear here after AI responses -

-
- ) : ( - currentSources.map((source, index) => ( - -
-
- {source.icon} -
- {source.name} -
- {source.url && ( - - )} -
- )) - )} -
-
- - ); - - return ( -
- {/* Coming Soon Toast */} - - {showComingSoon && ( - -

Coming soon!

-
- )} -
- - {/* Usage Limit Warning */} - - {isUnivAiPlusMode && hasUsedPlusResponse && ( - -

UnivAi+++ limit reached. Switch to UnivAi to continue.

-
- )} -
- - {/* Dark Mode Toggle - Top Right */} - - - {isDarkMode ? : } - - - - {/* Mobile Left Sheet Trigger */} - - - - - - - - Navigation Menu - - Access chat navigation, statistics, search, profile, and settings - -
- -
-
-
- - {/* Sidebar - Desktop only */} - - - - - {/* Main Container */} -
- {/* Header */} - -
-
-
-
- {isUnivAiPlusMode ? : } -
-
-
- - {isUnivAiPlusMode ? 'UnivAi+++' : 'UnivAi'} - {isUnivAiPlusMode && } - -
-

- {isUnivAiPlusMode ? 'Premium AI Experience' : 'Always ready to help'} -

-
-
-
-
-
-
-
-
- - {/* Chat Container */} -
- {/* Messages Area */} -
- {/* Messages */} -
-
- {messages.map((message) => ( - - ))} - {isTyping && } -
-
-
- - {/* Feedback Section - Appears after AI responses */} - - {showFeedback && messages.length > 1 && ( - -
-
- -

How was this response?

-
- -
- - -
- -