diff options
Diffstat (limited to 'examples/server/webui/src/components/Sidebar.tsx')
-rw-r--r-- | examples/server/webui/src/components/Sidebar.tsx | 96 |
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> + </> + ); +} |