summaryrefslogtreecommitdiff
path: root/examples/server/webui/src/components/Sidebar.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'examples/server/webui/src/components/Sidebar.tsx')
-rw-r--r--examples/server/webui/src/components/Sidebar.tsx96
1 files changed, 96 insertions, 0 deletions
diff --git a/examples/server/webui/src/components/Sidebar.tsx b/examples/server/webui/src/components/Sidebar.tsx
new file mode 100644
index 00000000..34727c62
--- /dev/null
+++ b/examples/server/webui/src/components/Sidebar.tsx
@@ -0,0 +1,96 @@
+import { useEffect, useState } from 'react';
+import { classNames } from '../utils/misc';
+import { Conversation } from '../utils/types';
+import StorageUtils from '../utils/storage';
+import { useNavigate, useParams } from 'react-router';
+
+export default function Sidebar() {
+ const params = useParams();
+ const navigate = useNavigate();
+
+ const [conversations, setConversations] = useState<Conversation[]>([]);
+ const [currConv, setCurrConv] = useState<Conversation | null>(null);
+
+ useEffect(() => {
+ StorageUtils.getOneConversation(params.convId ?? '').then(setCurrConv);
+ }, [params.convId]);
+
+ useEffect(() => {
+ const handleConversationChange = async () => {
+ setConversations(await StorageUtils.getAllConversations());
+ };
+ StorageUtils.onConversationChanged(handleConversationChange);
+ handleConversationChange();
+ return () => {
+ StorageUtils.offConversationChanged(handleConversationChange);
+ };
+ }, []);
+
+ return (
+ <>
+ <input
+ id="toggle-drawer"
+ type="checkbox"
+ className="drawer-toggle"
+ defaultChecked
+ />
+
+ <div className="drawer-side h-screen lg:h-screen z-50 lg:max-w-64">
+ <label
+ htmlFor="toggle-drawer"
+ aria-label="close sidebar"
+ className="drawer-overlay"
+ ></label>
+ <div className="flex flex-col bg-base-200 min-h-full max-w-64 py-4 px-4">
+ <div className="flex flex-row items-center justify-between mb-4 mt-4">
+ <h2 className="font-bold ml-4">Conversations</h2>
+
+ {/* close sidebar button */}
+ <label htmlFor="toggle-drawer" className="btn btn-ghost lg:hidden">
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="16"
+ height="16"
+ fill="currentColor"
+ className="bi bi-arrow-bar-left"
+ viewBox="0 0 16 16"
+ >
+ <path
+ fillRule="evenodd"
+ d="M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5M10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5"
+ />
+ </svg>
+ </label>
+ </div>
+
+ {/* list of conversations */}
+ <div
+ className={classNames({
+ 'btn btn-ghost justify-start': true,
+ 'btn-active': !currConv,
+ })}
+ onClick={() => navigate('/')}
+ >
+ + New conversation
+ </div>
+ {conversations.map((conv) => (
+ <div
+ key={conv.id}
+ className={classNames({
+ 'btn btn-ghost justify-start font-normal': true,
+ 'btn-active': conv.id === currConv?.id,
+ })}
+ onClick={() => navigate(`/chat/${conv.id}`)}
+ dir="auto"
+ >
+ <span className="truncate">{conv.name}</span>
+ </div>
+ ))}
+ <div className="text-center text-xs opacity-40 mt-auto mx-4">
+ Conversations are saved to browser's IndexedDB
+ </div>
+ </div>
+ </div>
+ </>
+ );
+}