2025-06-21 14:30:11 -03:00
"use client" ;
import React , { useState , useRef , useEffect } from 'react' ;
import {
2025-06-28 22:36:36 -03:00
Send , Plus , Menu , Search ,
2025-06-21 14:30:11 -03:00
MessageSquare , User , Bot , Copy , ThumbsUp , ThumbsDown ,
2025-06-28 22:36:36 -03:00
Share , Image , Video ,
Brain ,
Globe
} from 'lucide-react' ;
2025-06-21 14:30:11 -03:00
const ChatPage = ( ) = > {
const [ messages , setMessages ] = useState ( [
{
id : 1 ,
type : 'assistant' ,
content : "Hello! I'm General Bots, a large language model by Pragmatismo. How can I help you today?" ,
timestamp : new Date ( ) . toISOString ( )
}
] ) ;
const [ input , setInput ] = useState ( '' ) ;
const [ isTyping , setIsTyping ] = useState ( false ) ;
const [ sidebarOpen , setSidebarOpen ] = useState ( true ) ;
const [ conversations , setConversations ] = useState ( [
{ id : 1 , title : 'Current Chat' , timestamp : new Date ( ) , active : true } ,
{ id : 2 , title : 'Previous Conversation' , timestamp : new Date ( Date . now ( ) - 86400000 ) , active : false } ,
{ id : 3 , title : 'Code Review Discussion' , timestamp : new Date ( Date . now ( ) - 172800000 ) , active : false } ,
{ id : 4 , title : 'Project Planning' , timestamp : new Date ( Date . now ( ) - 259200000 ) , active : false } ,
] ) ;
const [ activeMode , setActiveMode ] = useState ( 'assistant' ) ;
const messagesEndRef = useRef ( null ) ;
const textareaRef = useRef ( null ) ;
// Mode buttons
const modeButtons = [
{ id : 'deep-think' , icon : < Brain size = { 16 } / > , label : 'Deep Think' } ,
{ id : 'web' , icon : < Globe size = { 16 } / > , label : 'Web' } ,
{ id : 'image' , icon : < Image size = { 16 } / > , label : 'Image' } ,
{ id : 'video' , icon : < Video size = { 16 } / > , label : 'Video' } ,
] ;
const scrollToBottom = ( ) = > {
messagesEndRef . current ? . scrollIntoView ( { behavior : 'smooth' } ) ;
} ;
useEffect ( ( ) = > {
scrollToBottom ( ) ;
} , [ messages ] ) ;
const handleSubmit = async ( ) = > {
if ( ! input . trim ( ) ) return ;
const userMessage = {
id : Date.now ( ) ,
type : 'user' ,
content : input.trim ( ) ,
timestamp : new Date ( ) . toISOString ( )
} ;
setMessages ( prev = > [ . . . prev , userMessage ] ) ;
setInput ( '' ) ;
setIsTyping ( true ) ;
// Simulate assistant response
setTimeout ( ( ) = > {
const assistantMessage = {
id : Date.now ( ) + 1 ,
type : 'assistant' ,
content : ` I understand you're asking about " ${ input . trim ( ) } ". This is a simulated response to demonstrate the chat interface. The actual implementation would connect to your chat provider and send real responses. ` ,
timestamp : new Date ( ) . toISOString ( )
} ;
setMessages ( prev = > [ . . . prev , assistantMessage ] ) ;
setIsTyping ( false ) ;
} , 1500 ) ;
} ;
const handleKeyDown = ( e ) = > {
if ( e . key === 'Enter' && ! e . shiftKey ) {
e . preventDefault ( ) ;
handleSubmit ( ) ;
}
} ;
const formatTimestamp = ( timestamp ) = > {
const date = new Date ( timestamp ) ;
const now = new Date ( ) ;
2025-06-28 22:36:36 -03:00
const diffInHours = ( now . getTime ( ) - date . getTime ( ) ) / ( 1000 * 60 * 60 ) ;
2025-06-21 14:30:11 -03:00
if ( diffInHours < 24 ) {
return date . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) ;
} else if ( diffInHours < 48 ) {
return 'Yesterday' ;
} else {
return date . toLocaleDateString ( ) ;
}
} ;
const newChat = ( ) = > {
const newConv = {
id : Date.now ( ) ,
title : 'New Chat' ,
timestamp : new Date ( ) ,
active : true
} ;
setConversations ( prev = > [ newConv , . . . prev . map ( c = > ( { . . . c , active : false } ) ) ] ) ;
setMessages ( [ {
id : Date.now ( ) ,
type : 'assistant' ,
content : "Hello! I'm General Bots, a large language model by Pragmatismo. How can I help you today?" ,
timestamp : new Date ( ) . toISOString ( )
} ] ) ;
} ;
2025-06-28 22:36:36 -03:00
// @ts-ignore
2025-06-21 14:30:11 -03:00
const MessageActions = ( { message } ) = > (
< div className = "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200" >
2025-06-21 19:45:21 -03:00
< button className = "p-1.5 hover:bg-secondary rounded-md transition-colors" >
< Copy className = "w-3.5 h-3.5 text-muted-foreground" / >
2025-06-21 14:30:11 -03:00
< / button >
2025-06-21 19:45:21 -03:00
< button className = "p-1.5 hover:bg-secondary rounded-md transition-colors" >
< ThumbsUp className = "w-3.5 h-3.5 text-muted-foreground" / >
2025-06-21 14:30:11 -03:00
< / button >
2025-06-21 19:45:21 -03:00
< button className = "p-1.5 hover:bg-secondary rounded-md transition-colors" >
< ThumbsDown className = "w-3.5 h-3.5 text-muted-foreground" / >
2025-06-21 14:30:11 -03:00
< / button >
2025-06-21 19:45:21 -03:00
< button className = "p-1.5 hover:bg-secondary rounded-md transition-colors" >
< Share className = "w-3.5 h-3.5 text-muted-foreground" / >
2025-06-21 14:30:11 -03:00
< / button >
< / div >
) ;
// Auto-resize textarea
useEffect ( ( ) = > {
const textarea = textareaRef . current ;
if ( textarea ) {
textarea . style . height = 'auto' ;
textarea . style . height = ` ${ Math . min ( textarea . scrollHeight , 120 ) } px ` ;
}
} , [ input ] ) ;
2025-03-30 16:42:51 -03:00
return (
2025-06-21 19:45:21 -03:00
< div className = "flex min-h-[calc(100vh-43px)] bg-background text-foreground" >
2025-06-21 14:30:11 -03:00
{ /* Sidebar */ }
< div
className = { ` ${
sidebarOpen ? 'w-80' : 'w-0'
2025-06-21 19:45:21 -03:00
} transition - all duration - 300 ease - in - out bg - card border - r border - border flex flex - col overflow - hidden ` }
2025-06-21 14:30:11 -03:00
>
{ sidebarOpen && (
< >
{ /* Sidebar Header */ }
2025-06-21 19:45:21 -03:00
< div className = "p-4 border-b border-border flex-shrink-0" >
2025-06-21 14:30:11 -03:00
< button
onClick = { newChat }
2025-06-21 19:45:21 -03:00
className = "flex items-center gap-3 w-full p-3 rounded-xl border-2 border-dashed border-muted hover:border-accent hover:bg-accent/10 transition-all duration-200 group"
2025-06-21 14:30:11 -03:00
>
2025-06-21 19:45:21 -03:00
< Plus className = "w-5 h-5 text-muted-foreground group-hover:text-accent" / >
< span className = "font-medium text-foreground group-hover:text-accent" >
2025-06-21 14:30:11 -03:00
New Chat
< / span >
< / button >
< / div >
{ /* Conversations List */ }
< div className = "flex-1 overflow-hidden" >
< div className = "h-full overflow-y-auto px-3 py-2 space-y-1" >
{ conversations . map ( conv = > (
< div
key = { conv . id }
className = { ` group relative flex items-center gap-3 p-3 rounded-xl cursor-pointer transition-all duration-200 ${
conv . active
2025-06-21 19:45:21 -03:00
? 'bg-primary/10 border border-primary'
: 'hover:bg-secondary'
2025-06-21 14:30:11 -03:00
} ` }
onClick = { ( ) = > {
setConversations ( prev = > prev . map ( c = > ( { . . . c , active : c.id === conv . id } ) ) ) ;
} }
>
< MessageSquare className = { ` w-4 h-4 flex-shrink-0 ${
2025-06-21 19:45:21 -03:00
conv . active ? 'text-primary' : 'text-muted-foreground'
2025-06-21 14:30:11 -03:00
} ` } />
< div className = "flex-1 min-w-0" >
< div className = { ` font-medium truncate ${
2025-06-21 19:45:21 -03:00
conv . active ? 'text-primary' : 'text-foreground'
2025-06-21 14:30:11 -03:00
} ` }>
{ conv . title }
< / div >
2025-06-21 19:45:21 -03:00
< div className = "text-xs text-muted-foreground truncate" >
2025-06-21 14:30:11 -03:00
{ formatTimestamp ( conv . timestamp ) }
< / div >
< / div >
{ conv . active && (
2025-06-21 19:45:21 -03:00
< div className = "w-2 h-2 rounded-full bg-primary flex-shrink-0" > < / div >
2025-06-21 14:30:11 -03:00
) }
< / div >
) ) }
< / div >
< / div >
{ /* Sidebar Footer */ }
2025-06-21 19:45:21 -03:00
< div className = "p-4 border-t border-border flex-shrink-0" >
< div className = "flex items-center gap-3 p-3 rounded-xl hover:bg-secondary cursor-pointer transition-colors duration-200" >
< User className = "w-5 h-5 text-muted-foreground" / >
< User className = "w-5 h-5 text-muted-foreground" / >
< User className = "w-5 h-5 text-muted-foreground" / >
< User className = "w-5 h-5 text-muted-foreground" / >
< User className = "w-5 h-5 text-muted-foreground" / >
2025-06-21 14:30:11 -03:00
< / div >
< / div >
< / >
) }
< / div >
{ /* Main Chat Area */ }
2025-06-21 19:45:21 -03:00
< div className = "flex-1 flex flex-col min-w-0 bg-background" >
2025-06-21 14:30:11 -03:00
{ /* Header */ }
2025-06-21 19:45:21 -03:00
< div className = "flex-shrink-0 p-4 border-b border-border bg-card" >
2025-06-21 14:30:11 -03:00
< div className = "flex items-center justify-between" >
< div className = "flex items-center gap-4" >
< button
onClick = { ( ) = > setSidebarOpen ( ! sidebarOpen ) }
2025-06-21 19:45:21 -03:00
className = "p-2 hover:bg-secondary rounded-lg transition-colors duration-200"
2025-06-21 14:30:11 -03:00
>
< Menu className = "w-5 h-5" / >
< / button >
2025-06-21 19:45:21 -03:00
< h1 className = "text-xl font-semibold text-foreground" >
2025-06-21 14:30:11 -03:00
{ conversations . find ( c = > c . active ) ? . title || 'New Chat' }
< / h1 >
< / div >
2025-06-21 19:45:21 -03:00
< button className = "p-2 hover:bg-secondary rounded-lg transition-colors duration-200" >
2025-06-21 14:30:11 -03:00
< Search className = "w-5 h-5" / >
< / button >
< / div >
< / div >
2025-06-21 19:45:21 -03:00
{ /* Messages Container */ }
2025-06-21 14:30:11 -03:00
< div className = "flex-1 overflow-hidden flex flex-col" >
< div className = "flex-1 overflow-y-auto px-4 py-6 space-y-6" >
{ messages . map ( message = > (
< div
key = { message . id }
className = { ` group flex ${ message . type === 'user' ? 'justify-end' : 'justify-start' } ` }
>
< div className = { ` max-w-[85%] md:max-w-[75%] ${
message . type === 'user'
2025-06-21 19:45:21 -03:00
? 'bg-primary text-primary-foreground'
: 'bg-secondary text-secondary-foreground'
2025-06-21 14:30:11 -03:00
} rounded - 2 xl px - 4 py - 3 shadow - sm ` }>
< div className = "flex items-start gap-3" >
< div className = "flex-shrink-0 mt-0.5" >
{ message . type === 'user' ? (
< User className = "w-4 h-4" / >
) : (
< Bot className = "w-4 h-4" / >
) }
< / div >
< div className = "flex-1 min-w-0" >
< div className = "whitespace-pre-wrap break-words leading-relaxed" >
{ message . content }
< / div >
< div className = { ` mt-3 flex items-center justify-between text-xs ${
message . type === 'user'
2025-06-21 19:45:21 -03:00
? 'text-primary-foreground/80'
: 'text-muted-foreground'
2025-06-21 14:30:11 -03:00
} ` }>
< span > { formatTimestamp ( message . timestamp ) } < / span >
{ message . type === 'assistant' && < MessageActions message = { message } / > }
< / div >
< / div >
< / div >
< / div >
< / div >
) ) }
{ isTyping && (
< div className = "flex justify-start" >
2025-06-21 19:45:21 -03:00
< div className = "max-w-[85%] md:max-w-[75%] bg-secondary rounded-2xl px-4 py-3 shadow-sm" >
2025-06-21 14:30:11 -03:00
< div className = "flex items-center gap-3" >
2025-06-21 19:45:21 -03:00
< Bot className = "w-4 h-4 text-muted-foreground" / >
2025-06-21 14:30:11 -03:00
< div className = "flex gap-1" >
2025-06-21 19:45:21 -03:00
< div className = "w-2 h-2 rounded-full bg-muted-foreground animate-bounce" > < / div >
< div className = "w-2 h-2 rounded-full bg-muted-foreground animate-bounce" style = { { animationDelay : '0.2s' } } > < / div >
< div className = "w-2 h-2 rounded-full bg-muted-foreground animate-bounce" style = { { animationDelay : '0.4s' } } > < / div >
2025-06-21 14:30:11 -03:00
< / div >
< / div >
< / div >
< / div >
) }
< div ref = { messagesEndRef } / >
< / div >
< / div >
{ /* Mode Carousel */ }
2025-06-21 19:45:21 -03:00
< div className = "flex-shrink-0 border-t border-border bg-card" >
2025-06-21 14:30:11 -03:00
< div className = "overflow-x-auto px-4 py-3" >
< div className = "flex gap-2 min-w-max" >
{ modeButtons . map ( button = > (
< button
key = { button . id }
onClick = { ( ) = > setActiveMode ( button . id ) }
className = { ` flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 whitespace-nowrap ${
activeMode === button . id
2025-06-21 19:45:21 -03:00
? 'bg-primary/10 text-primary border border-primary'
: 'bg-secondary text-secondary-foreground hover:bg-secondary/80'
2025-06-21 14:30:11 -03:00
} ` }
>
{ button . icon }
< span > { button . label } < / span >
< / button >
) ) }
< / div >
< / div >
< / div >
{ /* Input Area */ }
2025-06-21 19:45:21 -03:00
< div className = "flex-shrink-0 p-4 border-t border-border bg-card" >
2025-06-21 14:30:11 -03:00
< div className = "relative max-w-4xl mx-auto" >
< textarea
ref = { textareaRef }
value = { input }
onChange = { ( e ) = > setInput ( e . target . value ) }
onKeyDown = { handleKeyDown }
placeholder = "Type your message..."
2025-06-21 19:45:21 -03:00
className = "w-full p-4 pr-14 rounded-2xl border border-input bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent resize-none transition-all duration-200"
2025-06-21 14:30:11 -03:00
rows = { 1 }
style = { { minHeight : '56px' , maxHeight : '120px' } }
/ >
< button
onClick = { handleSubmit }
disabled = { ! input . trim ( ) }
2025-06-21 19:45:21 -03:00
className = { ` absolute right-6 bottom-3 p-2.5 rounded-xl transition-all duration-200 ${
2025-06-21 14:30:11 -03:00
input . trim ( )
2025-06-21 19:45:21 -03:00
? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-lg hover:shadow-xl transform hover:scale-105'
: 'bg-muted text-muted-foreground cursor-not-allowed'
2025-06-21 14:30:11 -03:00
} ` }
>
2025-06-21 19:45:21 -03:00
< Send className = "w-5 h-5 " / >
2025-06-21 14:30:11 -03:00
< / button >
< / div >
< / div >
< / div >
< / div >
2025-03-30 16:42:51 -03:00
) ;
2025-06-21 14:30:11 -03:00
} ;
export default ChatPage ;