From 858b2fcedb93f6d8e7acf438532a6209279d8f80 Mon Sep 17 00:00:00 2001
From: Sean Hatfield <seanhatfield5@gmail.com>
Date: Wed, 14 Feb 2024 15:29:49 -0800
Subject: [PATCH] [FEAT] New Workspace Settings Layout (#718)

* WIP new settings layout

* add suggested messages to general & appearance and clean up/make more user friendly

* lazy load workspace settings pages

* css fix on X button for document picker where button is barely clickable

* remove additional workspace settings page

* fix thread selection action when on thread

* refactor inputs into sub-components
remove unused paths

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
---
 frontend/src/App.jsx                          |   8 +-
 .../Modals/MangeWorkspace/Documents/index.jsx |   2 +-
 .../Settings/ChatModelPreference/index.jsx    | 120 ------
 .../Modals/MangeWorkspace/Settings/index.jsx  | 387 ------------------
 .../Modals/MangeWorkspace/index.jsx           |  52 +--
 .../ThreadContainer/ThreadItem/index.jsx      |   8 +-
 .../Sidebar/ActiveWorkspaces/index.jsx        |  95 +++--
 .../useGetProvidersModels.js}                 |   0
 .../WorkspaceSettings/ChatSettings/index.jsx  |  76 ++++
 .../GeneralAppearance/index.jsx               |  74 ++++
 .../VectorDatabase/index.jsx                  |  56 +++
 .../src/pages/WorkspaceSettings/index.jsx     | 279 +++++--------
 frontend/src/utils/paths.js                   |  12 +-
 frontend/src/utils/types.js                   |  19 +
 14 files changed, 413 insertions(+), 775 deletions(-)
 delete mode 100644 frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelPreference/index.jsx
 delete mode 100644 frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx
 rename frontend/src/{components/Modals/MangeWorkspace/Settings/ChatModelPreference/useGetProviderModels.js => hooks/useGetProvidersModels.js} (100%)
 create mode 100644 frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
 create mode 100644 frontend/src/pages/WorkspaceSettings/GeneralAppearance/index.jsx
 create mode 100644 frontend/src/pages/WorkspaceSettings/VectorDatabase/index.jsx
 create mode 100644 frontend/src/utils/types.js

diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 7a1395f17..7633af2c4 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -57,6 +57,10 @@ export default function App() {
             <Routes>
               <Route path="/" element={<PrivateRoute Component={Main} />} />
               <Route path="/login" element={<Login />} />
+              <Route
+                path="/workspace/:slug/settings/:tab"
+                element={<PrivateRoute Component={WorkspaceSettings} />}
+              />
               <Route
                 path="/workspace/:slug"
                 element={<PrivateRoute Component={WorkspaceChat} />}
@@ -68,10 +72,6 @@ export default function App() {
               <Route path="/accept-invite/:code" element={<InvitePage />} />
 
               {/* Admin */}
-              <Route
-                path="/workspace/:slug/settings"
-                element={<PrivateRoute Component={WorkspaceSettings} />}
-              />
               <Route
                 path="/settings/llm-preference"
                 element={<AdminRoute Component={GeneralLLMPreference} />}
diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx
index c5a66748c..705e78faa 100644
--- a/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx
@@ -195,7 +195,7 @@ export default function DocumentSettings({
   };
 
   return (
-    <div className="flex gap-x-6 justify-center">
+    <div className="flex gap-x-6 justify-center -mt-6 z-10 relative">
       <Directory
         files={availableDocs}
         loading={loading}
diff --git a/frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelPreference/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelPreference/index.jsx
deleted file mode 100644
index ea03c09a9..000000000
--- a/frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelPreference/index.jsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import useGetProviderModels, {
-  DISABLED_PROVIDERS,
-} from "./useGetProviderModels";
-
-export default function ChatModelSelection({
-  settings,
-  workspace,
-  setHasChanges,
-}) {
-  const { defaultModels, customModels, loading } = useGetProviderModels(
-    settings?.LLMProvider
-  );
-  if (DISABLED_PROVIDERS.includes(settings?.LLMProvider)) return null;
-
-  if (loading) {
-    return (
-      <div>
-        <div className="flex flex-col">
-          <label
-            htmlFor="name"
-            className="block text-sm font-medium text-white"
-          >
-            Chat model
-          </label>
-          <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
-            The specific chat model that will be used for this workspace. If
-            empty, will use the system LLM preference.
-          </p>
-        </div>
-        <select
-          name="chatModel"
-          required={true}
-          disabled={true}
-          className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
-        >
-          <option disabled={true} selected={true}>
-            -- waiting for models --
-          </option>
-        </select>
-      </div>
-    );
-  }
-
-  return (
-    <div>
-      <div className="flex flex-col">
-        <label htmlFor="name" className="block text-sm font-medium text-white">
-          Chat model{" "}
-          <span className="font-normal">({settings?.LLMProvider})</span>
-        </label>
-        <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
-          The specific chat model that will be used for this workspace. If
-          empty, will use the system LLM preference.
-        </p>
-      </div>
-
-      <select
-        name="chatModel"
-        required={true}
-        onChange={() => {
-          setHasChanges(true);
-        }}
-        className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
-      >
-        <option disabled={true} selected={workspace?.chatModel === null}>
-          System default
-        </option>
-        {defaultModels.length > 0 && (
-          <optgroup label="General models">
-            {defaultModels.map((model) => {
-              return (
-                <option
-                  key={model}
-                  value={model}
-                  selected={workspace?.chatModel === model}
-                >
-                  {model}
-                </option>
-              );
-            })}
-          </optgroup>
-        )}
-        {Array.isArray(customModels) && customModels.length > 0 && (
-          <optgroup label="Custom models">
-            {customModels.map((model) => {
-              return (
-                <option
-                  key={model.id}
-                  value={model.id}
-                  selected={workspace?.chatModel === model.id}
-                >
-                  {model.id}
-                </option>
-              );
-            })}
-          </optgroup>
-        )}
-        {/* For providers like TogetherAi where we partition model by creator entity. */}
-        {!Array.isArray(customModels) &&
-          Object.keys(customModels).length > 0 && (
-            <>
-              {Object.entries(customModels).map(([organization, models]) => (
-                <optgroup key={organization} label={organization}>
-                  {models.map((model) => (
-                    <option
-                      key={model.id}
-                      value={model.id}
-                      selected={workspace?.chatModel === model.id}
-                    >
-                      {model.name}
-                    </option>
-                  ))}
-                </optgroup>
-              ))}
-            </>
-          )}
-      </select>
-    </div>
-  );
-}
diff --git a/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx
deleted file mode 100644
index 48a3ff5d7..000000000
--- a/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx
+++ /dev/null
@@ -1,387 +0,0 @@
-import React, { useState, useRef, useEffect } from "react";
-import Workspace from "../../../../models/workspace";
-import paths from "../../../../utils/paths";
-import { chatPrompt } from "../../../../utils/chat";
-import System from "../../../../models/system";
-import PreLoader from "../../../Preloader";
-import { useParams } from "react-router-dom";
-import showToast from "../../../../utils/toast";
-import ChatModelPreference from "./ChatModelPreference";
-import { Link } from "react-router-dom";
-
-// Ensure that a type is correct before sending the body
-// to the backend.
-function castToType(key, value) {
-  const definitions = {
-    openAiTemp: {
-      cast: (value) => Number(value),
-    },
-    openAiHistory: {
-      cast: (value) => Number(value),
-    },
-    similarityThreshold: {
-      cast: (value) => parseFloat(value),
-    },
-    topN: {
-      cast: (value) => Number(value),
-    },
-  };
-
-  if (!definitions.hasOwnProperty(key)) return value;
-  return definitions[key].cast(value);
-}
-
-function recommendedSettings(provider = null) {
-  switch (provider) {
-    case "mistral":
-      return { temp: 0 };
-    default:
-      return { temp: 0.7 };
-  }
-}
-
-export default function WorkspaceSettings({ active, workspace, settings }) {
-  const { slug } = useParams();
-  const formEl = useRef(null);
-  const [saving, setSaving] = useState(false);
-  const [hasChanges, setHasChanges] = useState(false);
-  const [deleting, setDeleting] = useState(false);
-  const defaults = recommendedSettings(settings?.LLMProvider);
-
-  const handleUpdate = async (e) => {
-    setSaving(true);
-    e.preventDefault();
-    const data = {};
-    const form = new FormData(formEl.current);
-    for (var [key, value] of form.entries()) data[key] = castToType(key, value);
-    const { workspace: updatedWorkspace, message } = await Workspace.update(
-      workspace.slug,
-      data
-    );
-    if (!!updatedWorkspace) {
-      showToast("Workspace updated!", "success", { clear: true });
-    } else {
-      showToast(`Error: ${message}`, "error", { clear: true });
-    }
-    setSaving(false);
-    setHasChanges(false);
-  };
-
-  const deleteWorkspace = async () => {
-    if (
-      !window.confirm(
-        `You are about to delete your entire ${workspace.name} workspace. This will remove all vector embeddings on your vector database.\n\nThe original source files will remain untouched. This action is irreversible.`
-      )
-    )
-      return false;
-
-    setDeleting(true);
-    const success = await Workspace.delete(workspace.slug);
-    if (!success) {
-      showToast("Workspace could not be deleted!", "error", { clear: true });
-      setDeleting(false);
-      return;
-    }
-
-    workspace.slug === slug
-      ? (window.location = paths.home())
-      : window.location.reload();
-  };
-
-  return (
-    <form ref={formEl} onSubmit={handleUpdate}>
-      <div className="-mt-12 px-12 pb-6 flex flex-col h-full w-full max-h-[80vh] overflow-y-scroll">
-        <div className="flex flex-col gap-y-1 min-w-[900px]">
-          <div className="text-white text-opacity-60 text-sm font-bold uppercase py-6 border-b-2 border-white/10">
-            Workspace Settings
-          </div>
-          <div className="flex flex-row w-full py-6 border-b-2 border-white/10">
-            <div className="w-1/2">
-              <h3 className="text-white text-sm font-semibold">
-                Vector database identifier
-              </h3>
-              <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
-                {" "}
-              </p>
-              <p className="text-white text-opacity-60 text-sm font-medium">
-                {workspace?.slug}
-              </p>
-            </div>
-
-            <div className="w-1/2">
-              <h3 className="text-white text-sm font-semibold">
-                Number of vectors
-              </h3>
-              <p className="text-white text-opacity-60 text-xs font-medium my-[2px]">
-                Total number of vectors in your vector database.
-              </p>
-              <VectorCount reload={active} workspace={workspace} />
-            </div>
-          </div>
-        </div>
-        <div className="flex flex-col gap-y-1 w-full mt-7">
-          <div className="flex">
-            <div className="flex flex-col gap-y-4 w-1/2">
-              <div className="w-3/4 flex flex-col gap-y-4">
-                <ChatModelPreference
-                  settings={settings}
-                  workspace={workspace}
-                  setHasChanges={setHasChanges}
-                />
-                <div>
-                  <div className="flex flex-col">
-                    <label
-                      htmlFor="name"
-                      className="block text-sm font-medium text-white"
-                    >
-                      Workspace Name
-                    </label>
-                    <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
-                      This will only change the display name of your workspace.
-                    </p>
-                  </div>
-                  <input
-                    name="name"
-                    type="text"
-                    minLength={2}
-                    maxLength={80}
-                    defaultValue={workspace?.name}
-                    className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
-                    placeholder="My Workspace"
-                    required={true}
-                    autoComplete="off"
-                    onChange={() => setHasChanges(true)}
-                  />
-                </div>
-
-                <div>
-                  <div className="flex flex-col">
-                    <label
-                      htmlFor="name"
-                      className="block text-sm font-medium text-white"
-                    >
-                      LLM Temperature
-                    </label>
-                    <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
-                      This setting controls how "random" or dynamic your chat
-                      responses will be.
-                      <br />
-                      The higher the number (1.0 maximum) the more random and
-                      incoherent.
-                      <br />
-                      <i>Recommended: {defaults.temp}</i>
-                    </p>
-                  </div>
-                  <input
-                    name="openAiTemp"
-                    type="number"
-                    min={0.0}
-                    max={1.0}
-                    step={0.1}
-                    onWheel={(e) => e.target.blur()}
-                    defaultValue={workspace?.openAiTemp ?? defaults.temp}
-                    className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
-                    placeholder="0.7"
-                    required={true}
-                    autoComplete="off"
-                    onChange={() => setHasChanges(true)}
-                  />
-                </div>
-
-                <div>
-                  <div className="flex flex-col gap-y-1 mb-4">
-                    <label
-                      htmlFor="name"
-                      className="block mb-2 text-sm font-medium text-white"
-                    >
-                      Chat History
-                    </label>
-                    <p className="text-white text-opacity-60 text-xs font-medium">
-                      The number of previous chats that will be included in the
-                      response's short-term memory.
-                      <i>Recommend 20. </i>
-                      Anything more than 45 is likely to lead to continuous chat
-                      failures depending on message size.
-                    </p>
-                  </div>
-                  <input
-                    name="openAiHistory"
-                    type="number"
-                    min={1}
-                    max={45}
-                    step={1}
-                    onWheel={(e) => e.target.blur()}
-                    defaultValue={workspace?.openAiHistory ?? 20}
-                    className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
-                    placeholder="20"
-                    required={true}
-                    autoComplete="off"
-                    onChange={() => setHasChanges(true)}
-                  />
-                </div>
-              </div>
-            </div>
-
-            <div className="w-1/2">
-              <div className="w-3/4">
-                <div className="flex flex-col">
-                  <label
-                    htmlFor="name"
-                    className="block text-sm font-medium text-white"
-                  >
-                    Prompt
-                  </label>
-                  <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
-                    The prompt that will be used on this workspace. Define the
-                    context and instructions for the AI to generate a response.
-                    You should to provide a carefully crafted prompt so the AI
-                    can generate a relevant and accurate response.
-                  </p>
-                </div>
-                <textarea
-                  name="openAiPrompt"
-                  rows={5}
-                  defaultValue={chatPrompt(workspace)}
-                  className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
-                  placeholder="Given the following conversation, relevant context, and a follow up question, reply with an answer to the current question the user is asking. Return only your response to the question given the above information following the users instructions as needed."
-                  required={true}
-                  wrap="soft"
-                  autoComplete="off"
-                  onChange={() => setHasChanges(true)}
-                />
-
-                <div className="mt-4">
-                  <div className="flex flex-col">
-                    <label
-                      htmlFor="name"
-                      className="block text-sm font-medium text-white"
-                    >
-                      Max Context Snippets
-                    </label>
-                    <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
-                      This setting controls the maximum amount of context
-                      snippets the will be sent to the LLM for per chat or
-                      query.
-                      <br />
-                      <i>Recommended: 4</i>
-                    </p>
-                  </div>
-                  <input
-                    name="topN"
-                    type="number"
-                    min={1}
-                    max={12}
-                    step={1}
-                    onWheel={(e) => e.target.blur()}
-                    defaultValue={workspace?.topN ?? 4}
-                    className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
-                    placeholder="4"
-                    required={true}
-                    autoComplete="off"
-                    onChange={() => setHasChanges(true)}
-                  />
-                </div>
-                <div className="mt-4">
-                  <div className="flex flex-col">
-                    <label
-                      htmlFor="name"
-                      className="block text-sm font-medium text-white"
-                    >
-                      Document similarity threshold
-                    </label>
-                    <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
-                      The minimum similarity score required for a source to be
-                      considered related to the chat. The higher the number, the
-                      more similar the source must be to the chat.
-                    </p>
-                  </div>
-                  <select
-                    name="similarityThreshold"
-                    defaultValue={workspace?.similarityThreshold ?? 0.25}
-                    className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
-                    onChange={() => setHasChanges(true)}
-                    required={true}
-                  >
-                    <option value={0.0}>No restriction</option>
-                    <option value={0.25}>
-                      Low (similarity score &ge; .25)
-                    </option>
-                    <option value={0.5}>
-                      Medium (similarity score &ge; .50)
-                    </option>
-                    <option value={0.75}>
-                      High (similarity score &ge; .75)
-                    </option>
-                  </select>
-                </div>
-                <div className="mt-4 w-full flex justify-start">
-                  <Link to={paths.workspace.additionalSettings(workspace.slug)}>
-                    <a className="underline text-white/60 text-sm font-medium hover:text-sky-600">
-                      View additional settings
-                    </a>
-                  </Link>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-      <div className="flex items-center justify-between p-2 md:p-6 space-x-2 border-t rounded-b border-gray-600">
-        <DeleteWorkspace
-          deleting={deleting}
-          workspace={workspace}
-          onClick={deleteWorkspace}
-        />
-        {hasChanges && (
-          <button
-            type="submit"
-            className="transition-all duration-300 border border-slate-200 px-5 py-2.5 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
-          >
-            {saving ? "Updating..." : "Update workspace"}
-          </button>
-        )}
-      </div>
-    </form>
-  );
-}
-
-function DeleteWorkspace({ deleting, workspace, onClick }) {
-  const [canDelete, setCanDelete] = useState(false);
-  useEffect(() => {
-    async function fetchKeys() {
-      const canDelete = await System.getCanDeleteWorkspaces();
-      setCanDelete(canDelete);
-    }
-    fetchKeys();
-  }, [workspace?.slug]);
-
-  if (!canDelete) return null;
-  return (
-    <button
-      disabled={deleting}
-      onClick={onClick}
-      type="button"
-      className="transition-all duration-300 border border-transparent rounded-lg whitespace-nowrap text-sm px-5 py-2.5 focus:z-10 bg-transparent text-white hover:text-white hover:bg-red-600 disabled:bg-red-600 disabled:text-red-200 disabled:animate-pulse"
-    >
-      {deleting ? "Deleting Workspace..." : "Delete Workspace"}
-    </button>
-  );
-}
-
-function VectorCount({ reload, workspace }) {
-  const [totalVectors, setTotalVectors] = useState(null);
-  useEffect(() => {
-    async function fetchVectorCount() {
-      const totalVectors = await System.totalIndexes(workspace.slug);
-      setTotalVectors(totalVectors);
-    }
-    fetchVectorCount();
-  }, [workspace?.slug, reload]);
-
-  if (totalVectors === null) return <PreLoader size="4" />;
-  return (
-    <p className="text-white text-opacity-60 text-sm font-medium">
-      {totalVectors}
-    </p>
-  );
-}
diff --git a/frontend/src/components/Modals/MangeWorkspace/index.jsx b/frontend/src/components/Modals/MangeWorkspace/index.jsx
index b788eec74..6696a8756 100644
--- a/frontend/src/components/Modals/MangeWorkspace/index.jsx
+++ b/frontend/src/components/Modals/MangeWorkspace/index.jsx
@@ -1,18 +1,15 @@
-import React, { useState, useEffect, lazy, memo } from "react";
+import React, { useState, useEffect, memo } from "react";
 import { X } from "@phosphor-icons/react";
 import { useParams } from "react-router-dom";
 import Workspace from "../../../models/workspace";
 import System from "../../../models/system";
 import { isMobile } from "react-device-detect";
 import useUser from "../../../hooks/useUser";
-
-const DocumentSettings = lazy(() => import("./Documents"));
-const WorkspaceSettings = lazy(() => import("./Settings"));
+import DocumentSettings from "./Documents";
 
 const noop = () => {};
 const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => {
   const { slug } = useParams();
-  const [selectedTab, setSelectedTab] = useState("documents");
   const [workspace, setWorkspace] = useState(null);
   const [fileTypes, setFileTypes] = useState(null);
   const [settings, setSettings] = useState({});
@@ -72,31 +69,7 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => {
       <div className="backdrop h-full w-full absolute top-0 z-10" />
       <div className={`absolute max-h-full w-fit transition duration-300 z-20`}>
         <div className="relative bg-main-gradient rounded-[12px] shadow border-2 border-slate-300/10">
-          <div className="absolute top-[-18px] left-1/2 transform -translate-x-1/2 bg-sidebar-button p-1 rounded-xl shadow border-2 border-slate-300/10">
-            <div className="flex gap-x-1">
-              <button
-                onClick={() => setSelectedTab("documents")}
-                className={`px-4 py-2 rounded-[8px] font-semibold text-white hover:bg-switch-selected hover:bg-opacity-60 ${
-                  selectedTab === "documents"
-                    ? "bg-switch-selected shadow-md"
-                    : "bg-sidebar-button"
-                }`}
-              >
-                Documents
-              </button>
-              <button
-                onClick={() => setSelectedTab("settings")}
-                className={`px-4 py-2 rounded-[8px] font-semibold text-white hover:bg-switch-selected hover:bg-opacity-60 ${
-                  selectedTab === "settings"
-                    ? "bg-switch-selected shadow-md"
-                    : "bg-sidebar-button"
-                }`}
-              >
-                Settings
-              </button>
-            </div>
-          </div>
-          <div className="flex items-start justify-between p-2 rounded-t border-gray-500/50">
+          <div className="flex items-start justify-between p-2 rounded-t border-gray-500/50 z-40 relative">
             <button
               onClick={hideModal}
               type="button"
@@ -105,20 +78,11 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => {
               <X className="text-gray-300 text-lg" />
             </button>
           </div>
-          <div className={selectedTab === "documents" ? "" : "hidden"}>
-            <DocumentSettings
-              workspace={workspace}
-              fileTypes={fileTypes}
-              systemSettings={settings}
-            />
-          </div>
-          <div className={selectedTab === "settings" ? "" : "hidden"}>
-            <WorkspaceSettings
-              active={selectedTab === "settings"} // To force reload live sub-components like VectorCount
-              workspace={workspace}
-              settings={settings}
-            />
-          </div>
+          <DocumentSettings
+            workspace={workspace}
+            fileTypes={fileTypes}
+            systemSettings={settings}
+          />
         </div>
       </div>
     </div>
diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx
index 5b7cb5e0b..cea32dda4 100644
--- a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx
+++ b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx
@@ -16,11 +16,10 @@ export default function ThreadItem({
   onRemove,
   hasNext,
 }) {
-  const optionsContainer = useRef(null);
   const { slug } = useParams();
+  const optionsContainer = useRef(null);
   const [showOptions, setShowOptions] = useState(false);
   const [name, setName] = useState(thread.name);
-
   const linkTo = !thread.slug
     ? paths.workspace.chat(slug)
     : paths.workspace.thread(slug, thread.slug);
@@ -61,7 +60,10 @@ export default function ThreadItem({
             </p>
           </a>
         ) : (
-          <a href={isActive ? "#" : linkTo} className="w-full">
+          <a
+            href={window.location.pathname === linkTo ? "#" : linkTo}
+            className="w-full"
+          >
             <p
               className={`text-left text-sm ${
                 isActive ? "font-medium text-white" : "text-slate-400"
diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx
index 54580a2af..9ee9c40cd 100644
--- a/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx
+++ b/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx
@@ -7,18 +7,20 @@ import ManageWorkspace, {
 } from "../../Modals/MangeWorkspace";
 import paths from "@/utils/paths";
 import { useParams } from "react-router-dom";
-import { GearSix, SquaresFour } from "@phosphor-icons/react";
+import { GearSix, SquaresFour, UploadSimple } from "@phosphor-icons/react";
 import truncate from "truncate";
 import useUser from "@/hooks/useUser";
 import ThreadContainer from "./ThreadContainer";
+import { Link } from "react-router-dom";
 
 export default function ActiveWorkspaces() {
   const { slug } = useParams();
   const [loading, setLoading] = useState(true);
-  const [settingHover, setSettingHover] = useState({});
   const [workspaces, setWorkspaces] = useState([]);
   const [selectedWs, setSelectedWs] = useState(null);
   const [hoverStates, setHoverStates] = useState({});
+  const [gearHover, setGearHover] = useState({});
+  const [uploadHover, setUploadHover] = useState({});
   const { showing, showModal, hideModal } = useManageWorkspaceModal();
   const { user } = useUser();
 
@@ -30,7 +32,6 @@ export default function ActiveWorkspaces() {
     }
     getWorkspaces();
   }, []);
-
   const handleMouseEnter = useCallback((workspaceId) => {
     setHoverStates((prev) => ({ ...prev, [workspaceId]: true }));
   }, []);
@@ -38,13 +39,20 @@ export default function ActiveWorkspaces() {
   const handleMouseLeave = useCallback((workspaceId) => {
     setHoverStates((prev) => ({ ...prev, [workspaceId]: false }));
   }, []);
-
   const handleGearMouseEnter = useCallback((workspaceId) => {
-    setSettingHover((prev) => ({ ...prev, [workspaceId]: true }));
+    setGearHover((prev) => ({ ...prev, [workspaceId]: true }));
   }, []);
 
   const handleGearMouseLeave = useCallback((workspaceId) => {
-    setSettingHover((prev) => ({ ...prev, [workspaceId]: false }));
+    setGearHover((prev) => ({ ...prev, [workspaceId]: false }));
+  }, []);
+
+  const handleUploadMouseEnter = useCallback((workspaceId) => {
+    setUploadHover((prev) => ({ ...prev, [workspaceId]: true }));
+  }, []);
+
+  const handleUploadMouseLeave = useCallback((workspaceId) => {
+    setUploadHover((prev) => ({ ...prev, [workspaceId]: false }));
   }, []);
 
   if (loading) {
@@ -67,14 +75,16 @@ export default function ActiveWorkspaces() {
       {workspaces.map((workspace) => {
         const isActive = workspace.slug === slug;
         const isHovered = hoverStates[workspace.id];
-        const isGearHovered = settingHover[workspace.id];
         return (
-          <div className="flex flex-col w-full">
+          <div
+            className="flex flex-col w-full"
+            onMouseEnter={() => handleMouseEnter(workspace.id)}
+            onMouseLeave={() => handleMouseLeave(workspace.id)}
+            key={workspace.id}
+          >
             <div
               key={workspace.id}
               className="flex gap-x-2 items-center justify-between"
-              onMouseEnter={() => handleMouseEnter(workspace.id)}
-              onMouseLeave={() => handleMouseLeave(workspace.id)}
             >
               <a
                 href={isActive ? null : paths.workspace.chat(workspace.slug)}
@@ -99,30 +109,55 @@ export default function ActiveWorkspaces() {
                         isActive ? "" : "text-opacity-80"
                       }`}
                     >
-                      {isActive
+                      {isActive || isHovered
                         ? truncate(workspace.name, 17)
                         : truncate(workspace.name, 20)}
                     </p>
                   </div>
-                  <button
-                    type="button"
-                    onClick={(e) => {
-                      e.preventDefault();
-                      setSelectedWs(workspace);
-                      showModal();
-                    }}
-                    onMouseEnter={() => handleGearMouseEnter(workspace.id)}
-                    onMouseLeave={() => handleGearMouseLeave(workspace.id)}
-                    className="rounded-md flex items-center justify-center text-white ml-auto"
-                  >
-                    <GearSix
-                      weight={isGearHovered ? "fill" : "regular"}
-                      hidden={
-                        (!isActive && !isHovered) || user?.role === "default"
-                      }
-                      className="h-[20px] w-[20px] transition-all duration-300"
-                    />
-                  </button>
+                  {isActive ||
+                  isHovered ||
+                  gearHover[workspace.id] ||
+                  user?.role === "default" ? (
+                    <div className="flex items-center gap-x-2">
+                      <button
+                        type="button"
+                        onClick={(e) => {
+                          e.preventDefault();
+                          setSelectedWs(workspace);
+                          showModal();
+                        }}
+                        onMouseEnter={() =>
+                          handleUploadMouseEnter(workspace.id)
+                        }
+                        onMouseLeave={() =>
+                          handleUploadMouseLeave(workspace.id)
+                        }
+                        className="rounded-md flex items-center justify-center text-white ml-auto"
+                      >
+                        <UploadSimple
+                          weight={
+                            uploadHover[workspace.id] ? "fill" : "regular"
+                          }
+                          className="h-[20px] w-[20px] transition-all duration-300"
+                        />
+                      </button>
+
+                      <Link
+                        type="button"
+                        to={paths.workspace.settings.generalAppearance(
+                          workspace.slug
+                        )}
+                        onMouseEnter={() => handleGearMouseEnter(workspace.id)}
+                        onMouseLeave={() => handleGearMouseLeave(workspace.id)}
+                        className="rounded-md flex items-center justify-center text-white ml-auto"
+                      >
+                        <GearSix
+                          weight={gearHover[workspace.id] ? "fill" : "regular"}
+                          className="h-[20px] w-[20px] transition-all duration-300"
+                        />
+                      </Link>
+                    </div>
+                  ) : null}
                 </div>
               </a>
             </div>
diff --git a/frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelPreference/useGetProviderModels.js b/frontend/src/hooks/useGetProvidersModels.js
similarity index 100%
rename from frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelPreference/useGetProviderModels.js
rename to frontend/src/hooks/useGetProvidersModels.js
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
new file mode 100644
index 000000000..d1f5a5014
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
@@ -0,0 +1,76 @@
+import System from "@/models/system";
+import Workspace from "@/models/workspace";
+import showToast from "@/utils/toast";
+import { castToType } from "@/utils/types";
+import { useEffect, useRef, useState } from "react";
+import ChatModelSelection from "./ChatModelSelection";
+import ChatHistorySettings from "./ChatHistorySettings";
+import ChatPromptSettings from "./ChatPromptSettings";
+import ChatTemperatureSettings from "./ChatTemperatureSettings";
+
+export default function ChatSettings({ workspace }) {
+  const [settings, setSettings] = useState({});
+  const [hasChanges, setHasChanges] = useState(false);
+  const [saving, setSaving] = useState(false);
+
+  const formEl = useRef(null);
+  useEffect(() => {
+    async function fetchSettings() {
+      const _settings = await System.keys();
+      setSettings(_settings ?? {});
+    }
+    fetchSettings();
+  }, []);
+
+  const handleUpdate = async (e) => {
+    setSaving(true);
+    e.preventDefault();
+    const data = {};
+    const form = new FormData(formEl.current);
+    for (var [key, value] of form.entries()) data[key] = castToType(key, value);
+    const { workspace: updatedWorkspace, message } = await Workspace.update(
+      workspace.slug,
+      data
+    );
+    if (!!updatedWorkspace) {
+      showToast("Workspace updated!", "success", { clear: true });
+    } else {
+      showToast(`Error: ${message}`, "error", { clear: true });
+    }
+    setSaving(false);
+    setHasChanges(false);
+  };
+
+  if (!workspace) return null;
+  return (
+    <form
+      ref={formEl}
+      onSubmit={handleUpdate}
+      className="w-1/2 flex flex-col gap-y-6"
+    >
+      <ChatModelSelection
+        settings={settings}
+        workspace={workspace}
+        setHasChanges={setHasChanges}
+      />
+      <ChatHistorySettings
+        workspace={workspace}
+        setHasChanges={setHasChanges}
+      />
+      <ChatPromptSettings workspace={workspace} setHasChanges={setHasChanges} />
+      <ChatTemperatureSettings
+        settings={settings}
+        workspace={workspace}
+        setHasChanges={setHasChanges}
+      />
+      {hasChanges && (
+        <button
+          type="submit"
+          className="w-fit transition-all duration-300 border border-slate-200 px-5 py-2.5 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
+        >
+          {saving ? "Updating..." : "Update workspace"}
+        </button>
+      )}
+    </form>
+  );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/GeneralAppearance/index.jsx b/frontend/src/pages/WorkspaceSettings/GeneralAppearance/index.jsx
new file mode 100644
index 000000000..aec9e2e64
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/GeneralAppearance/index.jsx
@@ -0,0 +1,74 @@
+import Workspace from "@/models/workspace";
+import { castToType } from "@/utils/types";
+import showToast from "@/utils/toast";
+import { useEffect, useRef, useState } from "react";
+import VectorCount from "./VectorCount";
+import WorkspaceName from "./WorkspaceName";
+import SuggestedChatMessages from "./SuggestedChatMessages";
+
+export default function GeneralInfo({ slug }) {
+  const [workspace, setWorkspace] = useState(null);
+  const [hasChanges, setHasChanges] = useState(false);
+  const [saving, setSaving] = useState(false);
+  const [loading, setLoading] = useState(true);
+  const formEl = useRef(null);
+
+  useEffect(() => {
+    async function fetchWorkspace() {
+      const workspace = await Workspace.bySlug(slug);
+      setWorkspace(workspace);
+      setLoading(false);
+    }
+    fetchWorkspace();
+  }, [slug]);
+
+  const handleUpdate = async (e) => {
+    setSaving(true);
+    e.preventDefault();
+    const data = {};
+    const form = new FormData(formEl.current);
+    for (var [key, value] of form.entries()) data[key] = castToType(key, value);
+    const { workspace: updatedWorkspace, message } = await Workspace.update(
+      workspace.slug,
+      data
+    );
+    if (!!updatedWorkspace) {
+      showToast("Workspace updated!", "success", { clear: true });
+      setTimeout(() => window.location.reload(), 1_500);
+    } else {
+      showToast(`Error: ${message}`, "error", { clear: true });
+    }
+    setSaving(false);
+    setHasChanges(false);
+  };
+
+  if (!workspace || loading) return null;
+  return (
+    <>
+      <form
+        ref={formEl}
+        onSubmit={handleUpdate}
+        className="w-1/2 flex flex-col gap-y-6"
+      >
+        <VectorCount reload={true} workspace={workspace} />
+        <WorkspaceName
+          key={workspace.slug}
+          workspace={workspace}
+          setHasChanges={setHasChanges}
+        />
+
+        {hasChanges && (
+          <button
+            type="submit"
+            className="transition-all w-fit duration-300 border border-slate-200 px-5 py-2.5 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
+          >
+            {saving ? "Updating..." : "Update workspace"}
+          </button>
+        )}
+      </form>
+      <div className="mt-6">
+        <SuggestedChatMessages slug={workspace.slug} />
+      </div>
+    </>
+  );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/VectorDatabase/index.jsx b/frontend/src/pages/WorkspaceSettings/VectorDatabase/index.jsx
new file mode 100644
index 000000000..ca8de006b
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/VectorDatabase/index.jsx
@@ -0,0 +1,56 @@
+import Workspace from "@/models/workspace";
+import showToast from "@/utils/toast";
+import { castToType } from "@/utils/types";
+import { useRef, useState } from "react";
+import VectorDBIdentifier from "./VectorDBIdentifier";
+import MaxContextSnippets from "./MaxContextSnippets";
+import DocumentSimilarityThreshold from "./DocumentSimilarityThreshold";
+
+export default function VectorDatabase({ workspace }) {
+  const [hasChanges, setHasChanges] = useState(false);
+  const [saving, setSaving] = useState(false);
+  const formEl = useRef(null);
+
+  const handleUpdate = async (e) => {
+    setSaving(true);
+    e.preventDefault();
+    const data = {};
+    const form = new FormData(formEl.current);
+    for (var [key, value] of form.entries()) data[key] = castToType(key, value);
+    const { workspace: updatedWorkspace, message } = await Workspace.update(
+      workspace.slug,
+      data
+    );
+    if (!!updatedWorkspace) {
+      showToast("Workspace updated!", "success", { clear: true });
+    } else {
+      showToast(`Error: ${message}`, "error", { clear: true });
+    }
+    setSaving(false);
+    setHasChanges(false);
+  };
+
+  if (!workspace) return null;
+  return (
+    <form
+      ref={formEl}
+      onSubmit={handleUpdate}
+      className="w-1/2 flex flex-col gap-y-6"
+    >
+      <VectorDBIdentifier workspace={workspace} />
+      <MaxContextSnippets workspace={workspace} setHasChanges={setHasChanges} />
+      <DocumentSimilarityThreshold
+        workspace={workspace}
+        setHasChanges={setHasChanges}
+      />
+      {hasChanges && (
+        <button
+          type="submit"
+          className="w-fit transition-all duration-300 border border-slate-200 px-5 py-2.5 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
+        >
+          {saving ? "Updating..." : "Update workspace"}
+        </button>
+      )}
+    </form>
+  );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/index.jsx b/frontend/src/pages/WorkspaceSettings/index.jsx
index 35743d137..2050232bc 100644
--- a/frontend/src/pages/WorkspaceSettings/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/index.jsx
@@ -1,208 +1,119 @@
-import React, { useState, useEffect } from "react";
+import React, { useEffect, useState } from "react";
 import { useParams } from "react-router-dom";
-import { isMobile } from "react-device-detect";
-import showToast from "@/utils/toast";
-import { ArrowUUpLeft, Plus, X } from "@phosphor-icons/react";
+import Sidebar from "@/components/Sidebar";
 import Workspace from "@/models/workspace";
+import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
+import { isMobile } from "react-device-detect";
+import { FullScreenLoader } from "@/components/Preloader";
+import {
+  ArrowUUpLeft,
+  ChatText,
+  Database,
+  Wrench,
+} from "@phosphor-icons/react";
 import paths from "@/utils/paths";
+import { Link } from "react-router-dom";
+import { NavLink } from "react-router-dom";
+import GeneralAppearance from "./GeneralAppearance";
+import ChatSettings from "./ChatSettings";
+import VectorDatabase from "./VectorDatabase";
+
+const TABS = {
+  "general-appearance": GeneralAppearance,
+  "chat-settings": ChatSettings,
+  "vector-database": VectorDatabase,
+};
 
 export default function WorkspaceSettings() {
-  const [hasChanges, setHasChanges] = useState(false);
+  const { loading, requiresAuth, mode } = usePasswordModal();
+
+  if (loading) return <FullScreenLoader />;
+  if (requiresAuth !== false) {
+    return <>{requiresAuth !== null && <PasswordModal mode={mode} />}</>;
+  }
+
+  return <ShowWorkspaceChat />;
+}
+
+function ShowWorkspaceChat() {
+  const { slug, tab } = useParams();
   const [workspace, setWorkspace] = useState(null);
-  const [suggestedMessages, setSuggestedMessages] = useState([]);
-  const [editingIndex, setEditingIndex] = useState(-1);
-  const [newMessage, setNewMessage] = useState({ heading: "", message: "" });
-  const { slug } = useParams();
+  const [loading, setLoading] = useState(true);
 
   useEffect(() => {
-    async function fetchWorkspace() {
+    async function getWorkspace() {
       if (!slug) return;
-      const workspace = await Workspace.bySlug(slug);
+      const _workspace = await Workspace.bySlug(slug);
+      if (!_workspace) {
+        setLoading(false);
+        return;
+      }
+
       const suggestedMessages = await Workspace.getSuggestedMessages(slug);
-      setWorkspace(workspace);
-      setSuggestedMessages(suggestedMessages);
+      setWorkspace({
+        ..._workspace,
+        suggestedMessages,
+      });
+      setLoading(false);
     }
-    fetchWorkspace();
+    getWorkspace();
   }, [slug]);
 
-  const handleSaveSuggestedMessages = async () => {
-    const validMessages = suggestedMessages.filter(
-      (msg) =>
-        msg?.heading?.trim()?.length > 0 || msg?.message?.trim()?.length > 0
-    );
-    const { success, error } = await Workspace.setSuggestedMessages(
-      slug,
-      validMessages
-    );
-    if (!success) {
-      showToast(`Failed to update welcome messages: ${error}`, "error");
-      return;
-    }
-    showToast("Successfully updated welcome messages.", "success");
-    setHasChanges(false);
-  };
-
-  const addMessage = () => {
-    setEditingIndex(-1);
-    if (suggestedMessages.length >= 4) {
-      showToast("Maximum of 4 messages allowed.", "warning");
-      return;
-    }
-    const defaultMessage = {
-      heading: "Explain to me",
-      message: "the benefits of AnythingLLM",
-    };
-    setNewMessage(defaultMessage);
-    setSuggestedMessages([...suggestedMessages, { ...defaultMessage }]);
-    setHasChanges(true);
-  };
-
-  const removeMessage = (index) => {
-    const messages = [...suggestedMessages];
-    messages.splice(index, 1);
-    setSuggestedMessages(messages);
-    setHasChanges(true);
-  };
-
-  const startEditing = (index) => {
-    setEditingIndex(index);
-    setNewMessage({ ...suggestedMessages[index] });
-  };
-
-  const handleRemoveMessage = (index) => {
-    removeMessage(index);
-    setEditingIndex(-1);
-  };
-
-  const onEditChange = (e) => {
-    const updatedNewMessage = {
-      ...newMessage,
-      [e.target.name]: e.target.value,
-    };
-    setNewMessage(updatedNewMessage);
-    const updatedMessages = suggestedMessages.map((message, index) => {
-      if (index === editingIndex) {
-        return { ...message, [e.target.name]: e.target.value };
-      }
-      return message;
-    });
-
-    setSuggestedMessages(updatedMessages);
-    setHasChanges(true);
-  };
+  if (loading) return <FullScreenLoader />;
 
+  const TabContent = TABS[tab];
   return (
     <div className="w-screen h-screen overflow-hidden bg-sidebar flex">
-      <a
-        href={paths.workspace.chat(slug)}
-        className="absolute top-2 left-2 md:top-16 md:left-10 transition-all duration-300 p-2 rounded-full text-white bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border-transparent border z-10"
-      >
-        <ArrowUUpLeft className="h-4 w-4" />
-      </a>
+      {!isMobile && <Sidebar />}
       <div
         style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
-        className="transition-all duration-500 relative md:ml-[16px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
+        className="transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
       >
-        <div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
-          <div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
-            <div className="items-center flex gap-x-4">
-              <p className="text-2xl font-semibold text-white">
-                Workspace Settings ({workspace?.name})
-              </p>
-            </div>
-            <p className="text-sm font-base text-white text-opacity-60">
-              Customize your workspace.
-            </p>
-          </div>
-          <div className="my-6">
-            <div className="flex flex-col gap-y-2">
-              <h2 className="leading-tight font-medium text-white">
-                Suggested Chat Messages
-              </h2>
-              <p className="text-sm font-base text-white/60">
-                Customize the messages that will be suggested to your workspace
-                users.
-              </p>
-            </div>
-
-            <div className="grid grid-cols-1 md:grid-cols-2 gap-6 text-white/60 text-xs mt-6 w-full justify-center max-w-[600px]">
-              {suggestedMessages.map((suggestion, index) => (
-                <div key={index} className="relative w-full">
-                  <button
-                    className="transition-all duration-300 absolute z-10 text-neutral-700 bg-white rounded-full hover:bg-zinc-600 hover:border-zinc-600 hover:text-white border-transparent border shadow-lg ml-2"
-                    style={{
-                      top: -8,
-                      left: 265,
-                    }}
-                    onClick={() => handleRemoveMessage(index)}
-                  >
-                    <X className="m-[1px]" size={20} />
-                  </button>
-                  <button
-                    key={index}
-                    onClick={() => startEditing(index)}
-                    className={`text-left p-2.5 border rounded-xl w-full border-white/20 bg-sidebar hover:bg-workspace-item-selected-gradient ${
-                      editingIndex === index ? "border-sky-400" : ""
-                    }`}
-                  >
-                    <p className="font-semibold">{suggestion.heading}</p>
-                    <p>{suggestion.message}</p>
-                  </button>
-                </div>
-              ))}
-            </div>
-            {editingIndex >= 0 && (
-              <div className="flex flex-col gap-y-4 mr-2 mt-8">
-                <div className="w-1/2">
-                  <label className="text-white text-sm font-semibold block mb-2">
-                    Heading
-                  </label>
-                  <input
-                    placeholder="Message heading"
-                    className=" bg-sidebar text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
-                    value={newMessage.heading}
-                    name="heading"
-                    onChange={onEditChange}
-                  />
-                </div>
-                <div className="w-1/2">
-                  <label className="text-white text-sm font-semibold block mb-2">
-                    Message
-                  </label>
-                  <input
-                    placeholder="Message"
-                    className="bg-sidebar text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
-                    value={newMessage.message}
-                    name="message"
-                    onChange={onEditChange}
-                  />
-                </div>
-              </div>
-            )}
-            {suggestedMessages.length < 4 && (
-              <button
-                type="button"
-                onClick={addMessage}
-                className="flex gap-x-2 items-center justify-center mt-6 text-white text-sm hover:text-sky-400 transition-all duration-300"
-              >
-                Add new message <Plus className="" size={24} weight="fill" />
-              </button>
-            )}
-
-            {hasChanges && (
-              <div className="flex justify-center py-6">
-                <button
-                  type="button"
-                  className="transition-all duration-300 border border-slate-200 px-4 py-2 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
-                  onClick={handleSaveSuggestedMessages}
-                >
-                  Save Messages
-                </button>
-              </div>
-            )}
-          </div>
+        <div className="flex gap-x-10 pt-6 pb-4 ml-16 mr-8 border-b-2 border-white border-opacity-10">
+          <Link
+            to={paths.workspace.chat(slug)}
+            className="absolute top-2 left-2 md:top-4 md:left-4 transition-all duration-300 p-2 rounded-full text-white bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border-transparent border z-10"
+          >
+            <ArrowUUpLeft className="h-4 w-4" />
+          </Link>
+          <TabItem
+            title="General Settings"
+            icon={<Wrench className="h-6 w-6" />}
+            to={paths.workspace.settings.generalAppearance(slug)}
+          />
+          <TabItem
+            title="Chat Settings"
+            icon={<ChatText className="h-6 w-6" />}
+            to={paths.workspace.settings.chatSettings(slug)}
+          />
+          <TabItem
+            title="Vector Database"
+            icon={<Database className="h-6 w-6" />}
+            to={paths.workspace.settings.vectorDatabase(slug)}
+          />
+        </div>
+        <div className="px-16 py-6">
+          <TabContent slug={slug} workspace={workspace} />
         </div>
       </div>
     </div>
   );
 }
+
+function TabItem({ title, icon, to }) {
+  return (
+    <NavLink
+      to={to}
+      className={({ isActive }) =>
+        `${
+          isActive
+            ? "text-sky-400 pb-4 border-b-[4px] -mb-[19px] border-sky-400"
+            : "text-white/60 hover:text-sky-400"
+        } ` + " flex gap-x-2 items-center font-medium"
+      }
+    >
+      {icon}
+      <div>{title}</div>
+    </NavLink>
+  );
+}
diff --git a/frontend/src/utils/paths.js b/frontend/src/utils/paths.js
index e57a2641f..da10aa23c 100644
--- a/frontend/src/utils/paths.js
+++ b/frontend/src/utils/paths.js
@@ -55,8 +55,16 @@ export default {
     chat: (slug) => {
       return `/workspace/${slug}`;
     },
-    additionalSettings: (slug) => {
-      return `/workspace/${slug}/settings`;
+    settings: {
+      generalAppearance: (slug) => {
+        return `/workspace/${slug}/settings/general-appearance`;
+      },
+      chatSettings: (slug) => {
+        return `/workspace/${slug}/settings/chat-settings`;
+      },
+      vectorDatabase: (slug) => {
+        return `/workspace/${slug}/settings/vector-database`;
+      },
     },
     thread: (wsSlug, threadSlug) => {
       return `/workspace/${wsSlug}/t/${threadSlug}`;
diff --git a/frontend/src/utils/types.js b/frontend/src/utils/types.js
new file mode 100644
index 000000000..b63d49f6a
--- /dev/null
+++ b/frontend/src/utils/types.js
@@ -0,0 +1,19 @@
+export function castToType(key, value) {
+  const definitions = {
+    openAiTemp: {
+      cast: (value) => Number(value),
+    },
+    openAiHistory: {
+      cast: (value) => Number(value),
+    },
+    similarityThreshold: {
+      cast: (value) => parseFloat(value),
+    },
+    topN: {
+      cast: (value) => Number(value),
+    },
+  };
+
+  if (!definitions.hasOwnProperty(key)) return value;
+  return definitions[key].cast(value);
+}