feat: add responsive mobile-friendly sidebar for chat sessions
This commit is contained in:
@@ -42,6 +42,7 @@ export default function AIChatPage() {
|
|||||||
const [sessions, setSessions] = useState<ChatSession[]>([]);
|
const [sessions, setSessions] = useState<ChatSession[]>([]);
|
||||||
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
|
const [isMobileSessionsOpen, setIsMobileSessionsOpen] = useState(false);
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { messages, sendMessage, setMessages, status, stop } = useChat({
|
const { messages, sendMessage, setMessages, status, stop } = useChat({
|
||||||
@@ -158,15 +159,17 @@ export default function AIChatPage() {
|
|||||||
await sendMessage({ text: currentInput }, { body: { sessionId } });
|
await sendMessage({ text: currentInput }, { body: { sessionId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const SessionsSidebarContent = (
|
||||||
<div className="flex h-[calc(100dvh-6rem)] w-full overflow-hidden rounded-sm border border-border bg-background">
|
<>
|
||||||
<aside className="hidden w-80 flex-col border-r border-border bg-muted/20 md:flex">
|
<div className="flex items-center justify-between border-b border-border p-4 shrink-0">
|
||||||
<div className="flex items-center justify-between border-b border-border p-4">
|
|
||||||
<h2 className="flex items-center gap-2 font-semibold text-foreground">
|
<h2 className="flex items-center gap-2 font-semibold text-foreground">
|
||||||
<MessageSquare className="h-4 w-4" />
|
<MessageSquare className="h-4 w-4" />
|
||||||
Sohbetler
|
Sohbetler
|
||||||
</h2>
|
</h2>
|
||||||
<Button variant="outline" size="icon" className="h-8 w-8" onClick={handleNewChat}>
|
<Button variant="outline" size="icon" className="h-8 w-8" onClick={() => {
|
||||||
|
handleNewChat();
|
||||||
|
setIsMobileSessionsOpen(false);
|
||||||
|
}}>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -181,7 +184,10 @@ export default function AIChatPage() {
|
|||||||
<button
|
<button
|
||||||
key={session.id}
|
key={session.id}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setActiveSessionId(session.id)}
|
onClick={() => {
|
||||||
|
setActiveSessionId(session.id);
|
||||||
|
setIsMobileSessionsOpen(false);
|
||||||
|
}}
|
||||||
className={`group flex w-full items-center justify-between rounded-sm p-3 text-left transition-colors ${
|
className={`group flex w-full items-center justify-between rounded-sm p-3 text-left transition-colors ${
|
||||||
activeSessionId === session.id
|
activeSessionId === session.id
|
||||||
? "bg-primary/10 text-primary"
|
? "bg-primary/10 text-primary"
|
||||||
@@ -195,7 +201,7 @@ export default function AIChatPage() {
|
|||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={(event) => void handleDeleteSession(session.id, event)}
|
onClick={(event) => void handleDeleteSession(session.id, event)}
|
||||||
className="rounded-sm p-1 opacity-0 transition-all hover:bg-rose-50 hover:text-rose-600 group-hover:opacity-100"
|
className="rounded-sm p-1 opacity-0 transition-all hover:bg-rose-50 hover:text-rose-600 lg:group-hover:opacity-100"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
</span>
|
</span>
|
||||||
@@ -203,10 +209,35 @@ export default function AIChatPage() {
|
|||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col md:flex-row h-[calc(100dvh-3.5rem)] md:h-[calc(100dvh-6rem)] w-[calc(100%+2rem)] md:w-full -mx-4 -my-4 md:mx-0 md:my-0 overflow-hidden md:rounded-sm border-0 md:border md:border-border bg-background">
|
||||||
|
|
||||||
|
{/* Desktop Sidebar */}
|
||||||
|
<aside className="hidden w-80 flex-col border-r border-border bg-muted/20 md:flex">
|
||||||
|
{SessionsSidebarContent}
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<section className="flex min-w-0 flex-1 flex-col">
|
{/* Mobile Sidebar Overlay */}
|
||||||
<header className="flex h-14 items-center justify-between border-b border-border px-6">
|
{isMobileSessionsOpen && (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-40 bg-black/50 backdrop-blur-sm md:hidden transition-opacity"
|
||||||
|
onClick={() => setIsMobileSessionsOpen(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Mobile Sidebar Drawer */}
|
||||||
|
<aside
|
||||||
|
className={`fixed inset-y-0 left-0 z-50 w-72 transform border-r border-border bg-background transition-transform duration-300 ease-in-out md:hidden flex flex-col ${
|
||||||
|
isMobileSessionsOpen ? "translate-x-0" : "-translate-x-full"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{SessionsSidebarContent}
|
||||||
|
</aside>
|
||||||
|
<section className="flex min-w-0 flex-1 flex-col h-full">
|
||||||
|
<header className="flex h-14 items-center justify-between border-b border-border px-4 md:px-6 shrink-0">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex h-8 w-8 items-center justify-center rounded-sm bg-primary/10 text-primary">
|
<div className="flex h-8 w-8 items-center justify-center rounded-sm bg-primary/10 text-primary">
|
||||||
<Brain className="h-4 w-4" />
|
<Brain className="h-4 w-4" />
|
||||||
@@ -216,8 +247,8 @@ export default function AIChatPage() {
|
|||||||
<p className="text-xs text-muted-foreground">Kayıtlı verilerin hakkında soru sor.</p>
|
<p className="text-xs text-muted-foreground">Kayıtlı verilerin hakkında soru sor.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="outline" size="icon" className="h-8 w-8 md:hidden" onClick={handleNewChat}>
|
<Button variant="outline" size="sm" className="h-8 md:hidden text-xs px-3" onClick={() => setIsMobileSessionsOpen(true)}>
|
||||||
<Plus className="h-4 w-4" />
|
<MessageSquare className="h-3.5 w-3.5 mr-1.5" /> Sohbetler
|
||||||
</Button>
|
</Button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -264,8 +295,8 @@ export default function AIChatPage() {
|
|||||||
<div ref={messagesEndRef} />
|
<div ref={messagesEndRef} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="border-t border-border p-4 shrink-0">
|
<form onSubmit={handleSubmit} className="border-t border-border p-3 md:p-4 shrink-0 bg-background">
|
||||||
<div className="mx-auto flex max-w-4xl items-end gap-2 rounded-sm border border-border bg-background p-2 focus-within:border-primary">
|
<div className="mx-auto flex max-w-4xl items-end gap-2 rounded-sm border border-border bg-background p-1.5 focus-within:border-primary">
|
||||||
<textarea
|
<textarea
|
||||||
value={input}
|
value={input}
|
||||||
onChange={(event) => setInput(event.target.value)}
|
onChange={(event) => setInput(event.target.value)}
|
||||||
@@ -275,17 +306,17 @@ export default function AIChatPage() {
|
|||||||
void handleSubmit(event);
|
void handleSubmit(event);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
placeholder="Örn. Bu hafta geciken işlerim neler?"
|
placeholder="Mesaj gönder..."
|
||||||
className="min-h-10 max-h-40 flex-1 resize-none bg-transparent px-2 py-2 text-sm outline-none placeholder:text-muted-foreground"
|
className="min-h-9 max-h-40 flex-1 resize-none bg-transparent px-2 py-2 text-sm outline-none placeholder:text-muted-foreground placeholder:truncate"
|
||||||
rows={1}
|
rows={1}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Button type="button" variant="outline" size="icon" onClick={() => void stop()}>
|
<Button type="button" variant="outline" size="icon" className="shrink-0 h-9 w-9" onClick={() => void stop()}>
|
||||||
<span className="h-3 w-3 bg-current" />
|
<span className="h-3 w-3 bg-current" />
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button type="submit" size="icon" disabled={!input.trim()}>
|
<Button type="submit" size="icon" className="shrink-0 h-9 w-9" disabled={!input.trim()}>
|
||||||
<Send className="h-4 w-4" />
|
<Send className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user