open-chatbot / src /components /chat /sidebar.tsx
romizone's picture
Upload folder using huggingface_hub
c730f0b verified
"use client";
import { Bot, Plus, Settings, Trash2, MessageSquare, PanelLeftClose, PanelLeft, Zap, ZapOff } from "lucide-react";
import type { ChatSession, AppSettings } from "@/lib/types";
import { ANTHROPIC_MODELS, OPENAI_MODELS, DEEPSEEK_MODELS } from "@/lib/constants";
const PROVIDER_LABELS: Record<string, string> = {
deepseek: "DeepSeek",
openai: "OpenAI",
anthropic: "Claude",
};
interface Props {
sessions: ChatSession[];
currentSessionId: string | null;
onSelectSession: (id: string) => void;
onNewChat: () => void;
onDeleteSession: (id: string) => void;
onOpenSettings: () => void;
isOpen: boolean;
onToggle: () => void;
settings: AppSettings;
}
export function Sidebar({
sessions,
currentSessionId,
onSelectSession,
onNewChat,
onDeleteSession,
onOpenSettings,
isOpen,
onToggle,
settings,
}: Props) {
const hasKey = !!(settings.apiKeys[settings.provider]);
const providerLabel = PROVIDER_LABELS[settings.provider] || settings.provider;
const allModels = settings.provider === "anthropic"
? ANTHROPIC_MODELS
: settings.provider === "deepseek"
? DEEPSEEK_MODELS
: OPENAI_MODELS;
const modelLabel = allModels.find((m) => m.id === settings.model)?.name || settings.model;
return (
<>
{/* Toggle button when sidebar is closed */}
{!isOpen && (
<button
onClick={onToggle}
className="fixed top-4 left-4 z-50 p-2 rounded-xl bg-white border border-gray-200 shadow-sm hover:bg-gray-50 transition-colors"
>
<PanelLeft className="w-5 h-5 text-gray-600" />
</button>
)}
{/* Sidebar */}
<div
className={`fixed inset-y-0 left-0 z-40 w-[280px] bg-gray-50 border-r border-gray-200 flex flex-col transition-transform duration-300 ${
isOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
{/* Header */}
<div className="flex items-center justify-between px-4 py-4 border-b border-gray-200">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-500 flex items-center justify-center">
<Bot className="w-5 h-5 text-white" />
</div>
<span className="font-semibold text-gray-900 text-sm">
Open Chatbot
</span>
</div>
<button
onClick={onToggle}
className="p-1.5 rounded-lg hover:bg-gray-200 text-gray-500 transition-colors"
>
<PanelLeftClose className="w-5 h-5" />
</button>
</div>
{/* API connection indicator */}
<button
onClick={onOpenSettings}
className={`mx-3 mt-3 flex items-center gap-2.5 px-3 py-2 rounded-xl text-xs transition-colors ${
hasKey
? "bg-emerald-50 border border-emerald-200 text-emerald-700 hover:bg-emerald-100"
: "bg-red-50 border border-red-200 text-red-600 hover:bg-red-100"
}`}
>
{hasKey ? (
<Zap className="w-3.5 h-3.5 flex-shrink-0" />
) : (
<ZapOff className="w-3.5 h-3.5 flex-shrink-0" />
)}
<div className="flex flex-col items-start min-w-0">
<span className="font-medium">
{hasKey ? `Terhubung — ${providerLabel}` : "Tidak terhubung"}
</span>
<span className={`truncate w-full ${hasKey ? "text-emerald-500" : "text-red-400"}`}>
{hasKey ? modelLabel : "API key belum diisi"}
</span>
</div>
</button>
{/* New chat button */}
<div className="px-3 py-3">
<button
onClick={onNewChat}
className="w-full flex items-center gap-2 px-3 py-2.5 rounded-xl border border-gray-200 bg-white hover:bg-gray-100 text-gray-700 text-sm font-medium transition-colors"
>
<Plus className="w-4 h-4" />
Chat Baru
</button>
</div>
{/* Chat history */}
<div className="flex-1 overflow-y-auto px-3 space-y-1">
{sessions.length === 0 && (
<p className="text-xs text-gray-400 text-center mt-8">
Belum ada riwayat chat
</p>
)}
{sessions.map((session) => (
<div
key={session.id}
className={`group flex items-center gap-2 px-3 py-2.5 rounded-xl cursor-pointer transition-colors ${
currentSessionId === session.id
? "bg-gray-200 text-gray-900"
: "hover:bg-gray-100 text-gray-600"
}`}
onClick={() => onSelectSession(session.id)}
>
<MessageSquare className="w-4 h-4 flex-shrink-0" />
<span className="text-sm truncate flex-1">{session.title}</span>
<button
onClick={(e) => {
e.stopPropagation();
onDeleteSession(session.id);
}}
className="p-1 rounded-lg hover:bg-gray-300 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>
))}
</div>
{/* Settings button */}
<div className="px-3 py-3 border-t border-gray-200">
<button
onClick={onOpenSettings}
className="w-full flex items-center gap-2 px-3 py-2.5 rounded-xl hover:bg-gray-100 text-gray-600 text-sm transition-colors"
>
<Settings className="w-4 h-4" />
Pengaturan
</button>
</div>
</div>
</>
);
}