Components / AI Chat Interfaces
AI Chat Sidebar
A conversation history sidebar listing previous AI chat sessions with search, date grouping, and active state indicators.
Components / AI Chat Interfaces
A conversation history sidebar listing previous AI chat sessions with search, date grouping, and active state indicators.
"use client";
import React, { useState } from "react";
import { MessageSquare, Plus, Search, Trash2, MoreHorizontal } from "lucide-react";
interface Conversation {
id: string;
title: string;
lastMessage: string;
date: string;
isActive?: boolean;
}
const mockConversations: Conversation[] = [
{ id: "1", title: "React component architecture", lastMessage: "Here's a clean approach to...", date: "Today", isActive: true },
{ id: "2", title: "Python data pipeline", lastMessage: "You can use pandas to...", date: "Today" },
{ id: "3", title: "Database optimization tips", lastMessage: "Consider adding an index...", date: "Yesterday" },
{ id: "4", title: "API design patterns", lastMessage: "REST vs GraphQL depends...", date: "Yesterday" },
{ id: "5", title: "CSS Grid layouts", lastMessage: "Grid is perfect for...", date: "This Week" },
];
export default function AIChatSidebar() {
const [search, setSearch] = useState("");
const filtered = mockConversations.filter((c) =>
c.title.toLowerCase().includes(search.toLowerCase())
);
const grouped = filtered.reduce((acc, conv) => {
if (!acc[conv.date]) acc[conv.date] = [];
acc[conv.date].push(conv);
return acc;
}, {} as Record<string, Conversation[]>);
return (
<div className="w-[280px] h-[500px] flex flex-col border border-zinc-200 dark:border-zinc-800 rounded-xl bg-zinc-50 dark:bg-zinc-950 overflow-hidden">
<div className="p-3 border-b border-zinc-200 dark:border-zinc-800">
<button className="w-full flex items-center justify-center gap-2 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 px-3 py-2 text-sm font-medium hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors">
<Plus className="h-4 w-4" />
New Chat
</button>
</div>
<div className="px-3 py-2">
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-3.5 w-3.5 text-zinc-400" />
<input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search chats..."
className="w-full rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 pl-8 pr-3 py-2 text-xs focus:outline-none focus:ring-2 focus:ring-blue-500/20"
/>
</div>
</div>
<div className="flex-1 overflow-y-auto px-2">
{Object.entries(grouped).map(([date, convs]) => (
<div key={date} className="mb-3">
<p className="px-2 py-1 text-[10px] font-semibold text-zinc-400 uppercase tracking-wider">{date}</p>
{convs.map((conv) => (
<button
key={conv.id}
className={`w-full flex items-center gap-2 rounded-lg px-2.5 py-2 text-left text-sm transition-colors ${
conv.isActive
? "bg-zinc-200/70 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100"
: "text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800/50"
}`}
>
<MessageSquare className="h-3.5 w-3.5 shrink-0" />
<span className="truncate text-xs">{conv.title}</span>
</button>
))}
</div>
))}
</div>
</div>
);
}react lucide-react npm install react lucide-react