From 458ffed0c77b9417b56134492b845d1176e5fa28 Mon Sep 17 00:00:00 2001 From: Sean Hatfield <seanhatfield5@gmail.com> Date: Wed, 8 Nov 2023 19:00:12 -0800 Subject: [PATCH] added onboarding data handling modal (#342) * added onboarding data handling modal * adding data handling modal component * update element to list Update copy * remove useEffect dep * refactor onboarding navigation using history --------- Co-authored-by: timothycarambat <rambat1010@gmail.com> --- .../Steps/AppearanceSetup/index.jsx | 19 +- .../Steps/DataHandling/index.jsx | 171 ++++++++++++++++++ .../Steps/EmbeddingSelection/index.jsx | 6 +- .../Steps/LLMSelection/index.jsx | 11 +- .../Steps/MultiUserSetup/index.jsx | 2 +- .../Steps/PasswordProtection/index.jsx | 6 +- .../Steps/UserModeSelection/index.jsx | 6 +- .../Steps/VectorDatabaseConnection/index.jsx | 4 +- .../OnboardingFlow/OnboardingModal/index.jsx | 52 ++++-- 9 files changed, 232 insertions(+), 45 deletions(-) create mode 100644 frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/DataHandling/index.jsx diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/AppearanceSetup/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/AppearanceSetup/index.jsx index 7fcd46c13..3ccfa8c58 100644 --- a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/AppearanceSetup/index.jsx +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/AppearanceSetup/index.jsx @@ -5,7 +5,7 @@ import useLogo from "../../../../../hooks/useLogo"; import { Plus } from "@phosphor-icons/react"; import showToast from "../../../../../utils/toast"; -function AppearanceSetup({ nextStep }) { +function AppearanceSetup({ prevStep, nextStep }) { const { logo: _initLogo } = useLogo(); const [logo, setLogo] = useState(""); const [isDefaultLogo, setIsDefaultLogo] = useState(true); @@ -57,7 +57,7 @@ function AppearanceSetup({ nextStep }) { }; return ( - <div> + <div className="w-full"> <div className="flex flex-col w-full px-10 py-12"> <div className="flex flex-col gap-y-2"> <h2 className="text-white text-sm font-medium">Custom Logo</h2> @@ -109,20 +109,23 @@ function AppearanceSetup({ nextStep }) { </div> </div> <div className="flex w-full justify-between items-center p-6 space-x-6 border-t rounded-b border-gray-500/50"> - <div className="w-96 text-white text-opacity-80 text-xs font-base"> - Want to customize the automatic messages in your chat? Find more - customization options on the appearance settings page. - </div> + <button + onClick={prevStep} + type="button" + className="px-4 py-2 rounded-lg text-white hover:bg-sidebar" + > + Back + </button> <div className="flex gap-2"> <button - onClick={nextStep} + onClick={() => nextStep("user_mode_setup")} type="button" className="px-4 py-2 rounded-lg text-white hover:bg-sidebar" > Skip </button> <button - onClick={nextStep} + onClick={() => nextStep("user_mode_setup")} type="button" className="border border-slate-200 px-4 py-2 rounded-lg text-slate-800 bg-slate-200 text-sm items-center flex gap-x-2 hover:text-white hover:bg-transparent focus:ring-gray-800 font-semibold shadow" > diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/DataHandling/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/DataHandling/index.jsx new file mode 100644 index 000000000..ce0aa91b4 --- /dev/null +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/DataHandling/index.jsx @@ -0,0 +1,171 @@ +import React, { memo, useEffect, useState } from "react"; +import System from "../../../../../models/system"; +import OpenAiLogo from "../../../../../media/llmprovider/openai.png"; +import AzureOpenAiLogo from "../../../../../media/llmprovider/azure.png"; +import AnthropicLogo from "../../../../../media/llmprovider/anthropic.png"; +import ChromaLogo from "../../../../../media/vectordbs/chroma.png"; +import PineconeLogo from "../../../../../media/vectordbs/pinecone.png"; +import LanceDbLogo from "../../../../../media/vectordbs/lancedb.png"; +import WeaviateLogo from "../../../../../media/vectordbs/weaviate.png"; +import QDrantLogo from "../../../../../media/vectordbs/qdrant.png"; +import PreLoader from "../../../../../components/Preloader"; + +const LLM_SELECTION_PRIVACY = { + openai: { + name: "OpenAI", + description: [ + "Your chats will not be used for training", + "Your prompts and document text used in responses are visible to OpenAI", + ], + logo: OpenAiLogo, + }, + azure: { + name: "Azure OpenAI", + description: [ + "Your chats will not be used for training", + "Your text and embedding text are not visible to OpenAI or Microsoft", + ], + logo: AzureOpenAiLogo, + }, + anthropic: { + name: "Anthropic", + description: [ + "Your chats will not be used for training", + "Your prompts and document text used in responses are visible to Anthropic", + ], + logo: AnthropicLogo, + }, +}; + +const VECTOR_DB_PRIVACY = { + chroma: { + name: "Chroma", + description: [ + "Your embedded text not visible outside of your Chroma instance", + "Access to your instance is managed by you", + ], + logo: ChromaLogo, + }, + pinecone: { + name: "Pinecone", + description: [ + "Your embedded text and vectors are visible to Pinecone, but is not accessed", + "They manage your data and access to their servers", + ], + logo: PineconeLogo, + }, + qdrant: { + name: "Qdrant", + description: [ + "Your embedded text is visible to Qdrant if using a hosted instance", + "Your embedded text is not visible to Qdrant if using a self-hosted instance", + "Your data is stored on your Qdrant instance", + ], + logo: QDrantLogo, + }, + weaviate: { + name: "Weaviate", + description: [ + "Your embedded text is visible to Weaviate, if using a hosted instance", + "Your embedded text is not visible to Weaviate, if using a self-hosted instance", + "Your data is stored on your Weaviate instance", + ], + logo: WeaviateLogo, + }, + lancedb: { + name: "LanceDB", + description: [ + "Your embedded text and vectors are only accessible by this AnythingLLM instance", + ], + logo: LanceDbLogo, + }, +}; + +function DataHandling({ nextStep, prevStep, currentStep }) { + const [llmChoice, setLLMChoice] = useState("openai"); + const [loading, setLoading] = useState(true); + const [vectorDb, setVectorDb] = useState("pinecone"); + + useEffect(() => { + async function fetchKeys() { + const _settings = await System.keys(); + setLLMChoice(_settings?.LLMProvider); + setVectorDb(_settings?.VectorDB); + + setLoading(false); + } + if (currentStep === "data_handling") { + fetchKeys(); + } + }, []); + + if (loading) + return ( + <div className="w-full h-full flex justify-center items-center p-20"> + <PreLoader /> + </div> + ); + + return ( + <div className="max-w-[750px]"> + <div className="p-8 flex gap-x-16"> + <div className="w-1/2 flex flex-col gap-y-3.5"> + <div className="text-white text-base font-bold">LLM Selection</div> + <div className="flex items-center gap-2.5"> + <img + src={LLM_SELECTION_PRIVACY[llmChoice].logo} + alt="LLM Logo" + className="w-8 h-8 rounded" + /> + <p className="text-white text-sm font-bold"> + {LLM_SELECTION_PRIVACY[llmChoice].name} + </p> + </div> + <div className="flex flex-col"> + {LLM_SELECTION_PRIVACY[llmChoice].description.map((desc) => ( + <p className="text-white/90 text-sm"> + <b>•</b> {desc} + </p> + ))} + </div> + </div> + + <div className="w-1/2 flex flex-col gap-y-3.5"> + <div className="text-white text-base font-bold">Vector Database</div> + <div className="flex items-center gap-2.5"> + <img + src={VECTOR_DB_PRIVACY[vectorDb].logo} + alt="Vector DB Logo" + className="w-8 h-8 rounded" + /> + <p className="text-white text-sm font-bold"> + {VECTOR_DB_PRIVACY[vectorDb].name} + </p> + </div> + <ul className="flex flex-col list-disc"> + {VECTOR_DB_PRIVACY[vectorDb].description.map((desc) => ( + <li className="text-white/90 text-sm">{desc}</li> + ))} + </ul> + </div> + </div> + <div className="flex w-full justify-between items-center p-6 space-x-2 border-t rounded-b border-gray-500/50"> + <button + onClick={prevStep} + type="button" + className="px-4 py-2 rounded-lg text-white hover:bg-sidebar" + > + Back + </button> + <button + onClick={() => nextStep("create_workspace")} + className="border border-slate-200 px-4 py-2 rounded-lg text-slate-800 bg-slate-200 text-sm items-center flex gap-x-2 hover:text-white hover:bg-transparent focus:ring-gray-800 font-semibold shadow" + > + Continue + </button> + </div> + </div> + ); +} + +export default memo(DataHandling); diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/EmbeddingSelection/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/EmbeddingSelection/index.jsx index 27fe27525..4edde9be0 100644 --- a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/EmbeddingSelection/index.jsx +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/EmbeddingSelection/index.jsx @@ -5,7 +5,7 @@ import System from "../../../../../models/system"; import PreLoader from "../../../../../components/Preloader"; import LLMProviderOption from "../../../../../components/LLMSelection/LLMProviderOption"; -function EmbeddingSelection({ nextStep, prevStep, currentStep, goToStep }) { +function EmbeddingSelection({ nextStep, prevStep, currentStep }) { const [embeddingChoice, setEmbeddingChoice] = useState("openai"); const [settings, setSettings] = useState(null); const [loading, setLoading] = useState(true); @@ -35,7 +35,7 @@ function EmbeddingSelection({ nextStep, prevStep, currentStep, goToStep }) { alert(`Failed to save LLM settings: ${error}`, "error"); return; } - goToStep(2); + nextStep("vector_database"); return; }; @@ -156,7 +156,7 @@ function EmbeddingSelection({ nextStep, prevStep, currentStep, goToStep }) { </div> <div className="flex w-full justify-between items-center p-6 space-x-2 border-t rounded-b border-gray-500/50"> <button - onClick={() => goToStep(1)} + onClick={prevStep} type="button" className="px-4 py-2 rounded-lg text-white hover:bg-sidebar" > diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/LLMSelection/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/LLMSelection/index.jsx index a813dbd9e..3a19d3874 100644 --- a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/LLMSelection/index.jsx +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/LLMSelection/index.jsx @@ -9,7 +9,7 @@ import OpenAiOptions from "../../../../../components/LLMSelection/OpenAiOptions" import AzureAiOptions from "../../../../../components/LLMSelection/AzureAiOptions"; import AnthropicAiOptions from "../../../../../components/LLMSelection/AnthropicAiOptions"; -function LLMSelection({ nextStep, prevStep, currentStep, goToStep }) { +function LLMSelection({ nextStep, prevStep, currentStep }) { const [llmChoice, setLLMChoice] = useState("openai"); const [settings, setSettings] = useState(null); const [loading, setLoading] = useState(true); @@ -26,10 +26,10 @@ function LLMSelection({ nextStep, prevStep, currentStep, goToStep }) { setLoading(false); } - if (currentStep === 1) { + if (currentStep === "llm_preference") { fetchKeys(); } - }, [currentStep]); + }, []); const handleSubmit = async (e) => { e.preventDefault(); @@ -45,11 +45,10 @@ function LLMSelection({ nextStep, prevStep, currentStep, goToStep }) { switch (data.LLMProvider) { case "anthropic": - goToStep(7); + return nextStep("embedding_preferences"); default: - nextStep(); + return nextStep("vector_database"); } - return; }; if (loading) diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/MultiUserSetup/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/MultiUserSetup/index.jsx index 726a70276..3b34f2ae6 100644 --- a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/MultiUserSetup/index.jsx +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/MultiUserSetup/index.jsx @@ -33,7 +33,7 @@ function MultiUserSetup({ nextStep, prevStep }) { window.localStorage.setItem(AUTH_TOKEN, token); window.localStorage.removeItem(AUTH_TIMESTAMP); - nextStep(); + nextStep("data_handling"); }; const setNewUsername = (e) => setUsername(e.target.value); diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/PasswordProtection/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/PasswordProtection/index.jsx index 1baca6d23..51ae732c1 100644 --- a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/PasswordProtection/index.jsx +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/PasswordProtection/index.jsx @@ -7,7 +7,7 @@ import { } from "../../../../../utils/constants"; import debounce from "lodash.debounce"; -function PasswordProtection({ goToStep, prevStep }) { +function PasswordProtection({ nextStep, prevStep }) { const [password, setPassword] = useState(""); const handleSubmit = async (e) => { e.preventDefault(); @@ -32,12 +32,12 @@ function PasswordProtection({ goToStep, prevStep }) { window.localStorage.removeItem(AUTH_TIMESTAMP); window.localStorage.setItem(AUTH_TOKEN, token); - goToStep(7); + nextStep("data_handling"); return; }; const handleSkip = () => { - goToStep(7); + nextStep("data_handling"); }; const setNewPassword = (e) => setPassword(e.target.value); diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/UserModeSelection/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/UserModeSelection/index.jsx index 1d981b35f..6625257f5 100644 --- a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/UserModeSelection/index.jsx +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/UserModeSelection/index.jsx @@ -1,13 +1,13 @@ import React, { memo } from "react"; // How many people will be using your instance step -function UserModeSelection({ goToStep, prevStep }) { +function UserModeSelection({ nextStep, prevStep }) { const justMeClicked = () => { - goToStep(5); + nextStep("password_protection"); }; const myTeamClicked = () => { - goToStep(6); + nextStep("multi_user_mode"); }; return ( diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/VectorDatabaseConnection/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/VectorDatabaseConnection/index.jsx index 47fbb870c..d8766c816 100644 --- a/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/VectorDatabaseConnection/index.jsx +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/VectorDatabaseConnection/index.jsx @@ -21,7 +21,7 @@ function VectorDatabaseConnection({ nextStep, prevStep, currentStep }) { setVectorDB(_settings?.VectorDB || "lancedb"); setLoading(false); } - if (currentStep === 2) { + if (currentStep === "vector_database") { fetchKeys(); } }, [currentStep]); @@ -41,7 +41,7 @@ function VectorDatabaseConnection({ nextStep, prevStep, currentStep }) { alert(`Failed to save settings: ${error}`, "error"); return; } - nextStep(); + nextStep("appearance"); return; }; diff --git a/frontend/src/pages/OnboardingFlow/OnboardingModal/index.jsx b/frontend/src/pages/OnboardingFlow/OnboardingModal/index.jsx index a412ecc52..c53ba2c1a 100644 --- a/frontend/src/pages/OnboardingFlow/OnboardingModal/index.jsx +++ b/frontend/src/pages/OnboardingFlow/OnboardingModal/index.jsx @@ -8,6 +8,7 @@ import PasswordProtection from "./Steps/PasswordProtection"; import MultiUserSetup from "./Steps/MultiUserSetup"; import CreateFirstWorkspace from "./Steps/CreateFirstWorkspace"; import EmbeddingSelection from "./Steps/EmbeddingSelection"; +import DataHandling from "./Steps/DataHandling"; const DIALOG_ID = "onboarding-modal"; @@ -16,46 +17,53 @@ function hideModal() { } const STEPS = { - 1: { + llm_preference: { title: "LLM Preference", description: "These are the credentials and settings for your preferred LLM chat & embedding provider.", component: LLMSelection, }, - 2: { + vector_database: { title: "Vector Database", description: "These are the credentials and settings for how your AnythingLLM instance will function.", component: VectorDatabaseConnection, }, - 3: { + appearance: { title: "Appearance", - description: "Customize the appearance of your AnythingLLM instance.", + description: + "Customize the appearance of your AnythingLLM instance.\nFind more customization options on the appearance settings page.", component: AppearanceSetup, }, - 4: { + user_mode_setup: { title: "User Mode Setup", description: "Choose how many people will be using your instance.", component: UserModeSelection, }, - 5: { + password_protection: { title: "Password Protect", description: "Protect your instance with a password. It is important to save this password as it cannot be recovered.", component: PasswordProtection, }, - 6: { + multi_user_mode: { title: "Multi-User Mode", description: "Setup your instance to support your team by activating multi-user mode.", component: MultiUserSetup, }, - 7: { + data_handling: { + title: "Data Handling", + description: + "We are committed to transparency and control when it comes to your personal data.", + component: DataHandling, + }, + create_workspace: { title: "Create Workspace", description: "To get started, create a new workspace.", component: CreateFirstWorkspace, }, - 8: { + embedding_preferences: { title: "Embedding Preference", description: "Due to your LLM selection you need to set up a provider for embedding files and text.", @@ -65,19 +73,26 @@ const STEPS = { export const OnboardingModalId = DIALOG_ID; export default function OnboardingModal() { - const [currentStep, setCurrentStep] = useState(1); + const [currentStep, setCurrentStep] = useState("llm_preference"); + const [history, setHistory] = useState(["llm_preference"]); - const nextStep = () => { - setCurrentStep((prevStep) => prevStep + 1); + const nextStep = (stepKey) => { + setCurrentStep(stepKey); + setHistory([...history, stepKey]); }; const prevStep = () => { - if (currentStep === 1) return hideModal(); - setCurrentStep((prevStep) => prevStep - 1); - }; + const currentStepIdx = history.indexOf(currentStep); + if (currentStepIdx === -1 || currentStepIdx === 0) { + setCurrentStep("llm_preference"); + setHistory(["llm_preference"]); + return hideModal(); + } - const goToStep = (step) => { - setCurrentStep(step); + const prevStep = history[currentStepIdx - 1]; + const _history = [...history].slice(0, currentStepIdx); + setCurrentStep(prevStep); + setHistory(_history); }; const { component: StepComponent, ...step } = STEPS[currentStep]; @@ -88,7 +103,7 @@ export default function OnboardingModal() { <div className="flex items-start justify-between p-8 border-b rounded-t border-gray-500/50"> <div className="flex flex-col gap-2"> <h3 className="text-xl font-semibold text-white">{step.title}</h3> - <p className="text-sm font-base text-white text-opacity-60"> + <p className="text-sm font-base text-white text-opacity-60 whitespace-pre"> {step.description || ""} </p> </div> @@ -106,7 +121,6 @@ export default function OnboardingModal() { currentStep={currentStep} nextStep={nextStep} prevStep={prevStep} - goToStep={goToStep} /> </div> </div>