-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
737e0eb
commit 1a7cdcc
Showing
11 changed files
with
383 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { ChatInterface } from '@/components/plan-advisor/chat-interface'; | ||
import { ChatLayout } from '@/components/plan-advisor/chat-layout'; | ||
|
||
export default function PlanAdvisorPage() { | ||
return ( | ||
<div className="h-screen w-full overflow-hidden bg-black"> | ||
<ChatLayout /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
'use client'; | ||
|
||
import { Plus, Search, LightbulbIcon } from 'lucide-react'; | ||
import { Button } from '@/components/ui/button'; | ||
|
||
export function ChatHeader() { | ||
return ( | ||
<div className="flex items-center gap-2 border-b border-gray-800 p-4"> | ||
<Button | ||
variant="outline" | ||
className="flex gap-2 rounded-full bg-[#262626] text-gray-300 hover:bg-[#363636] hover:text-white" | ||
> | ||
<Plus className="size-4" /> | ||
<span>New chat</span> | ||
</Button> | ||
<Button | ||
variant="outline" | ||
className="flex gap-2 rounded-full bg-[#262626] text-gray-300 hover:bg-[#363636] hover:text-white" | ||
> | ||
<Search className="size-4" /> | ||
<span>Search</span> | ||
</Button> | ||
<Button | ||
variant="outline" | ||
className="flex gap-2 rounded-full bg-[#262626] text-gray-300 hover:bg-[#363636] hover:text-white" | ||
> | ||
<LightbulbIcon className="size-4" /> | ||
<span>Reason</span> | ||
</Button> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
'use client'; | ||
|
||
import * as React from 'react'; | ||
import { Send, Image, FileText, Mic, MoreHorizontal } from 'lucide-react'; | ||
import { Button } from '@/components/ui/button'; | ||
import { Textarea } from '@/components/ui/textarea'; | ||
import { | ||
Tooltip, | ||
TooltipContent, | ||
TooltipProvider, | ||
TooltipTrigger, | ||
} from '@/components/ui/tooltip'; | ||
|
||
interface ChatInputProps { | ||
input: string; | ||
setInput: (value: string) => void; | ||
onSubmit: (e: React.FormEvent) => void; | ||
} | ||
|
||
export function ChatInput({ input, setInput, onSubmit }: ChatInputProps) { | ||
const textareaRef = React.useRef<HTMLTextAreaElement>(null); | ||
|
||
const adjustTextareaHeight = () => { | ||
const textarea = textareaRef.current; | ||
if (textarea) { | ||
textarea.style.height = 'auto'; | ||
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`; | ||
} | ||
}; | ||
|
||
React.useEffect(() => { | ||
adjustTextareaHeight(); | ||
}, [input]); | ||
|
||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => { | ||
if (e.key === 'Enter' && !e.shiftKey) { | ||
e.preventDefault(); | ||
onSubmit(e); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="border-t border-gray-800 p-4"> | ||
<form onSubmit={onSubmit} className="relative space-y-4"> | ||
<ChatInputActions /> | ||
<div className="relative"> | ||
<Textarea | ||
ref={textareaRef} | ||
value={input} | ||
onChange={(e) => { | ||
setInput(e.target.value); | ||
adjustTextareaHeight(); | ||
}} | ||
onKeyDown={handleKeyDown} | ||
placeholder="Message ChatGPT..." | ||
rows={1} | ||
className="min-h-[52px] w-full resize-none rounded-2xl border-gray-700 bg-[#262626] pr-12 text-white placeholder:text-gray-400 focus:border-gray-600" | ||
style={{ | ||
maxHeight: '200px', | ||
overflow: 'auto', | ||
}} | ||
/> | ||
<Button | ||
type="submit" | ||
size="icon" | ||
className="absolute bottom-1.5 right-1.5 size-8 rounded-xl bg-orange-600 p-2 hover:bg-orange-700 disabled:opacity-50" | ||
disabled={!input.trim()} | ||
> | ||
<Send className="size-4" /> | ||
</Button> | ||
</div> | ||
</form> | ||
<div className="mt-2 text-center text-xs text-gray-500"> | ||
Press Enter to send, Shift + Enter for new line | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
function ChatInputActions() { | ||
return ( | ||
<div className="flex items-center gap-2 px-2"> | ||
<TooltipProvider> | ||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
type="button" | ||
variant="ghost" | ||
size="icon" | ||
className="text-gray-400 hover:text-gray-300" | ||
> | ||
<Image className="size-4" /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent>Upload image</TooltipContent> | ||
</Tooltip> | ||
</TooltipProvider> | ||
<TooltipProvider> | ||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
type="button" | ||
variant="ghost" | ||
size="icon" | ||
className="text-gray-400 hover:text-gray-300" | ||
> | ||
<FileText className="size-4" /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent>Upload file</TooltipContent> | ||
</Tooltip> | ||
</TooltipProvider> | ||
<TooltipProvider> | ||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
type="button" | ||
variant="ghost" | ||
size="icon" | ||
className="text-gray-400 hover:text-gray-300" | ||
> | ||
<Mic className="size-4" /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent>Voice message</TooltipContent> | ||
</Tooltip> | ||
</TooltipProvider> | ||
<Button | ||
type="button" | ||
variant="ghost" | ||
size="icon" | ||
className="ml-auto text-gray-400 hover:text-gray-300" | ||
> | ||
<MoreHorizontal className="size-4" /> | ||
</Button> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use client'; | ||
|
||
import * as React from 'react'; | ||
import { ChatHeader } from './chat-header'; | ||
import { ChatMessages } from './chat-messages'; | ||
import { ChatInput } from './chat-input'; | ||
import { Message } from './types'; | ||
|
||
export function ChatInterface() { | ||
const [messages, setMessages] = React.useState<Message[]>([ | ||
{ | ||
role: 'assistant', | ||
content: | ||
"Hello! I'm here to help you choose the perfect plan for your needs. What type of features are you looking for?", | ||
}, | ||
]); | ||
const [input, setInput] = React.useState(''); | ||
|
||
const handleSubmit = (e: React.FormEvent) => { | ||
e.preventDefault(); | ||
if (!input.trim()) return; | ||
|
||
const newMessages = [ | ||
...messages, | ||
{ role: 'user', content: input }, | ||
{ | ||
role: 'assistant', | ||
content: | ||
'Thank you for sharing. Let me analyze your needs and suggest the most suitable plan...', | ||
}, | ||
]; | ||
|
||
setMessages(newMessages as Message[]); | ||
setInput(''); | ||
}; | ||
|
||
return ( | ||
<div className="flex h-full flex-col"> | ||
<ChatHeader /> | ||
<ChatMessages messages={messages} /> | ||
<ChatInput input={input} setInput={setInput} onSubmit={handleSubmit} /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
'use client'; | ||
|
||
import * as React from 'react'; | ||
import { Menu } from 'lucide-react'; | ||
import { Button } from '@/components/ui/button'; | ||
import { ChatSidebar } from './chat-sidebar'; | ||
import { ChatInterface } from './chat-interface'; | ||
|
||
export function ChatLayout() { | ||
const [isSidebarOpen, setIsSidebarOpen] = React.useState(true); | ||
|
||
return ( | ||
<div className="relative flex h-screen bg-black"> | ||
<ChatSidebar | ||
isOpen={isSidebarOpen} | ||
onClose={() => setIsSidebarOpen(false)} | ||
/> | ||
|
||
<main className="flex flex-1 flex-col bg-[#1A1A1A]"> | ||
<div className="flex h-14 items-center border-b border-gray-800 px-4 md:hidden"> | ||
<Button | ||
variant="ghost" | ||
size="sm" | ||
className="text-white" | ||
onClick={() => setIsSidebarOpen(!isSidebarOpen)} | ||
> | ||
<Menu className="size-5" /> | ||
</Button> | ||
</div> | ||
<div className="flex-1 overflow-hidden"> | ||
<ChatInterface /> | ||
</div> | ||
</main> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { ScrollArea } from '@/components/ui/scroll-area'; | ||
import { cn } from '@/lib/utils'; | ||
import { Message } from './types'; | ||
|
||
interface ChatMessagesProps { | ||
messages: Message[]; | ||
} | ||
|
||
export function ChatMessages({ messages }: ChatMessagesProps) { | ||
return ( | ||
<ScrollArea className="flex-1 p-4"> | ||
<div className="space-y-6"> | ||
{messages.map((message, index) => ( | ||
<div | ||
key={index} | ||
className={`flex items-start gap-3 ${ | ||
message.role === 'user' ? 'justify-end' : 'justify-start' | ||
}`} | ||
> | ||
{message.role === 'assistant' && ( | ||
<div className="flex size-8 items-center justify-center rounded-full bg-gray-800 text-white"> | ||
AI | ||
</div> | ||
)} | ||
<div | ||
className={cn( | ||
'max-w-[80%] rounded-lg px-4 py-2', | ||
message.role === 'user' | ||
? 'bg-orange-600 text-white' | ||
: 'bg-gray-800 text-gray-100', | ||
)} | ||
> | ||
{message.content} | ||
</div> | ||
{message.role === 'user' && ( | ||
<div className="flex size-8 items-center justify-center rounded-full bg-gray-700 text-white"> | ||
You | ||
</div> | ||
)} | ||
</div> | ||
))} | ||
</div> | ||
</ScrollArea> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
'use client'; | ||
|
||
import * as React from 'react'; | ||
import { Plus } from 'lucide-react'; | ||
import { Button } from '@/components/ui/button'; | ||
import { ScrollArea } from '@/components/ui/scroll-area'; | ||
import { cn } from '@/lib/utils'; | ||
|
||
interface ChatSidebarProps { | ||
isOpen: boolean; | ||
onClose: () => void; | ||
} | ||
|
||
export function ChatSidebar({ isOpen, onClose }: ChatSidebarProps) { | ||
const [conversations] = React.useState([ | ||
{ id: 1, title: 'Previous consultation 1', date: '2024-02-20' }, | ||
{ id: 2, title: 'Previous consultation 2', date: '2024-02-19' }, | ||
]); | ||
|
||
return ( | ||
<div | ||
className={cn( | ||
'fixed inset-y-0 left-0 z-40 flex w-[260px] flex-col bg-black transition-transform duration-300 ease-in-out md:relative md:translate-x-0', | ||
isOpen ? 'translate-x-0' : '-translate-x-full', | ||
)} | ||
> | ||
<div className="flex h-14 items-center border-b border-gray-800 px-2"> | ||
<Button | ||
variant="outline" | ||
className="w-full justify-start space-x-2 border-gray-700 bg-transparent text-white hover:bg-gray-800" | ||
> | ||
<Plus className="size-4" /> | ||
<span>New Consultation</span> | ||
</Button> | ||
</div> | ||
<ScrollArea className="flex-1 px-2 py-4"> | ||
<div className="space-y-1"> | ||
{conversations.map((chat) => ( | ||
<Button | ||
key={chat.id} | ||
variant="ghost" | ||
className="w-full justify-start px-3 py-2 text-gray-300 hover:bg-gray-800" | ||
> | ||
<div className="flex flex-col items-start text-sm"> | ||
<span className="font-medium">{chat.title}</span> | ||
<span className="text-xs text-gray-500">{chat.date}</span> | ||
</div> | ||
</Button> | ||
))} | ||
</div> | ||
</ScrollArea> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface Message { | ||
role: 'user' | 'assistant'; | ||
content: string; | ||
} |
Oops, something went wrong.