Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 additions & 11 deletions frontend/src/components/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Plus } from "lucide-react";
import { useState } from "react";
import { Plus, Search } from "lucide-react";
import { useConversationsStore } from "@/store/conversations";
import { createConversation, deleteConversation } from "@/api/conversations";
import { ConversationItem } from "./ConversationItem";
Expand All @@ -16,19 +17,29 @@ export function Sidebar() {
removeConversation,
} = useConversationsStore();

const [filter, setFilter] = useState("");

const defaultEngine = engines[0]?.name ?? "";

const handleNew = async () => {
const conv = await createConversation(defaultEngine);
addConversation(conv);
setActiveId(conv.id);
setFilter("");
};

const handleDelete = async (id: string) => {
await deleteConversation(id);
removeConversation(id);
};

const normalizedFilter = filter.trim().toLowerCase();
const visibleConversations = normalizedFilter
? conversations.filter((c) =>
c.title.toLowerCase().includes(normalizedFilter)
)
: conversations;

return (
<aside className="w-64 flex flex-col border-r border-border h-full bg-muted/40">
{/* Brand header */}
Expand All @@ -49,22 +60,41 @@ export function Sidebar() {
</button>
</div>

{/* Filter */}
<div className="px-3 py-2 border-b border-border">
<div className="relative">
<Search className="absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground pointer-events-none" />
<input
type="text"
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter conversations…"
className="w-full pl-7 pr-2 py-1.5 text-xs bg-background border border-border rounded-md focus:outline-none focus:ring-1 focus:ring-primary/50 placeholder:text-muted-foreground/40"
/>
</div>
</div>

{/* Conversations */}
<div className="flex-1 overflow-y-auto p-2 space-y-0.5">
{conversations.length === 0 && (
{conversations.length === 0 ? (
<p className="text-xs text-muted-foreground px-2 py-4 text-center">
No conversations yet
</p>
) : visibleConversations.length === 0 ? (
<p className="text-xs text-muted-foreground px-2 py-4 text-center">
No matching conversations
</p>
) : (
visibleConversations.map((conv) => (
<ConversationItem
key={conv.id}
conversation={conv}
isActive={conv.id === activeId}
onSelect={() => setActiveId(conv.id)}
onDelete={() => handleDelete(conv.id)}
/>
))
)}
{conversations.map((conv) => (
<ConversationItem
key={conv.id}
conversation={conv}
isActive={conv.id === activeId}
onSelect={() => setActiveId(conv.id)}
onDelete={() => handleDelete(conv.id)}
/>
))}
</div>

{/* Footer: theme switcher + DataHub badge */}
Expand Down
Loading