mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-04-17 18:18:11 +00:00
feat: implement questionnaire during onboarding (optional) (#429)
fix: PFP url check
This commit is contained in:
parent
ce9233c258
commit
a84333901a
8 changed files with 269 additions and 9 deletions
|
@ -379,3 +379,9 @@ dialog::backdrop {
|
|||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.radio-container:has(input:checked) {
|
||||
@apply border-blue-500 bg-blue-400/10 text-blue-800;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom";
|
|||
import paths from "@/utils/paths";
|
||||
import Workspace from "@/models/workspace";
|
||||
|
||||
function CreateFirstWorkspace() {
|
||||
function CreateFirstWorkspace({ prevStep }) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleCreate = async (e) => {
|
||||
|
@ -47,6 +47,13 @@ function CreateFirstWorkspace() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex w-full justify-end items-center px-6 py-4 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
|
||||
type="submit"
|
||||
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"
|
||||
|
|
|
@ -231,7 +231,7 @@ function DataHandling({ nextStep, prevStep, currentStep }) {
|
|||
Back
|
||||
</button>
|
||||
<button
|
||||
onClick={() => nextStep("create_workspace")}
|
||||
onClick={() => nextStep("user_questionnaire")}
|
||||
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
|
||||
|
|
|
@ -28,7 +28,7 @@ function LLMSelection({ nextStep, prevStep, currentStep }) {
|
|||
async function fetchKeys() {
|
||||
const _settings = await System.keys();
|
||||
setSettings(_settings);
|
||||
setLLMChoice(_settings?.LLMProvider);
|
||||
setLLMChoice(_settings?.LLMProvider || "openai");
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
import { COMPLETE_QUESTIONNAIRE } from "@/utils/constants";
|
||||
import paths from "@/utils/paths";
|
||||
import { CheckCircle, Circle } from "@phosphor-icons/react";
|
||||
import React, { memo } from "react";
|
||||
|
||||
async function sendQuestionnaire({ email, useCase, comment }) {
|
||||
if (import.meta.env.DEV) return;
|
||||
return fetch(`https://onboarding-wxich7363q-uc.a.run.app`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
useCase,
|
||||
comment,
|
||||
sourceId: "0VRjqHh6Vukqi0x0Vd0n/m8JuT7k8nOz",
|
||||
}),
|
||||
})
|
||||
.then(() => {
|
||||
window.localStorage.setItem(COMPLETE_QUESTIONNAIRE, true);
|
||||
console.log(`✅ Questionnaire responses sent.`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`sendQuestionnaire`, error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function UserQuestionnaire({ nextStep, prevStep }) {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
const formData = new FormData(form);
|
||||
await sendQuestionnaire({
|
||||
email: formData.get("email"),
|
||||
useCase: formData.get("use_case") || "other",
|
||||
comment: formData.get("comment") || null,
|
||||
});
|
||||
nextStep("create_workspace");
|
||||
return;
|
||||
};
|
||||
|
||||
const handleSkip = () => {
|
||||
nextStep("create_workspace");
|
||||
};
|
||||
|
||||
if (!!window?.localStorage?.getItem(COMPLETE_QUESTIONNAIRE)) {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="w-full flex items-center justify-center px-1 md:px-8 py-4">
|
||||
<div className="w-auto flex flex-col gap-y-1 items-center">
|
||||
<CheckCircle size={60} className="text-green-500" />
|
||||
<p className="text-zinc-300">Thank you for your feedback!</p>
|
||||
<a
|
||||
href={paths.mailToMintplex()}
|
||||
className="text-blue-400 underline text-xs"
|
||||
>
|
||||
team@mintplexlabs.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full justify-between items-center px-6 py-4 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>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={handleSkip}
|
||||
type="button"
|
||||
className="px-4 py-2 rounded-lg text-white hover:bg-sidebar"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="border px-4 py-2 rounded-lg text-sm items-center flex gap-x-2
|
||||
border-slate-200 text-slate-800 bg-slate-200 hover:text-white hover:bg-transparent focus:ring-gray-800 font-semibold shadow
|
||||
disabled:border-gray-400 disabled:text-slate-800 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<form className="flex flex-col w-full" onSubmit={handleSubmit}>
|
||||
<div className="flex flex-col w-full px-1 md:px-8 py-4">
|
||||
<div className="w-full flex flex-col gap-y-2 my-5">
|
||||
<div className="w-80">
|
||||
<div className="flex flex-col mb-3 ">
|
||||
<label htmlFor="email" className="block font-medium text-white">
|
||||
What is your email?
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
name="email"
|
||||
type="email"
|
||||
className="bg-zinc-900 text-white text-sm rounded-lg focus:border-blue-500 block w-full p-2.5 placeholder-white placeholder-opacity-60 focus:ring-blue-500"
|
||||
placeholder="you@gmail.com"
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full flex flex-col gap-y-2 my-5">
|
||||
<div className="w-full">
|
||||
<div className="flex flex-col mb-3 ">
|
||||
<label
|
||||
htmlFor="use_case"
|
||||
className="block font-medium text-white"
|
||||
>
|
||||
How are you planning to use AnythingLLM?
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<div class="flex items-center ps-4 border border-zinc-400 rounded group radio-container hover:bg-blue-400/10">
|
||||
<input
|
||||
id="bordered-radio-1"
|
||||
type="radio"
|
||||
value="business"
|
||||
name="use_case"
|
||||
class="sr-only peer"
|
||||
/>
|
||||
<Circle
|
||||
weight="fill"
|
||||
className="fill-transparent border border-gray-300 rounded-full peer-checked:fill-blue-500 peer-checked:border-none"
|
||||
/>
|
||||
<label
|
||||
for="bordered-radio-1"
|
||||
class="w-full py-4 ms-2 text-sm font-medium text-gray-300"
|
||||
>
|
||||
For my business
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center ps-4 border border-zinc-400 rounded group radio-container hover:bg-blue-400/10">
|
||||
<input
|
||||
id="bordered-radio-2"
|
||||
type="radio"
|
||||
value="personal"
|
||||
name="use_case"
|
||||
class="sr-only peer"
|
||||
/>
|
||||
<Circle
|
||||
weight="fill"
|
||||
className="fill-transparent border border-gray-300 rounded-full peer-checked:fill-blue-500 peer-checked:border-none"
|
||||
/>
|
||||
<label
|
||||
for="bordered-radio-2"
|
||||
class="w-full py-4 ms-2 text-sm font-medium text-gray-300"
|
||||
>
|
||||
For personal use
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center ps-4 border border-zinc-400 rounded group radio-container hover:bg-blue-400/10">
|
||||
<input
|
||||
id="bordered-radio-3"
|
||||
type="radio"
|
||||
value="other"
|
||||
name="use_case"
|
||||
class="sr-only peer"
|
||||
/>
|
||||
<Circle
|
||||
weight="fill"
|
||||
className="fill-transparent border border-gray-300 rounded-full peer-checked:fill-blue-500 peer-checked:border-none"
|
||||
/>
|
||||
<label
|
||||
for="bordered-radio-3"
|
||||
class="w-full py-4 ms-2 text-sm font-medium text-gray-300"
|
||||
>
|
||||
I'm not sure yet
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full flex flex-col gap-y-2 my-5">
|
||||
<div className="w-full">
|
||||
<div className="flex flex-col mb-3 ">
|
||||
<label
|
||||
htmlFor="comments"
|
||||
className="block font-medium text-white"
|
||||
>
|
||||
Any comments for the team?
|
||||
</label>
|
||||
</div>
|
||||
<textarea
|
||||
name="comment"
|
||||
rows={5}
|
||||
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="If you have any questions or comments right now, you can leave them here and we will get back to you. You can also email team@mintplexlabs.com"
|
||||
wrap="soft"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full justify-between items-center px-6 py-4 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>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={handleSkip}
|
||||
type="button"
|
||||
className="px-4 py-2 rounded-lg text-white hover:bg-sidebar"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="border px-4 py-2 rounded-lg text-sm items-center flex gap-x-2
|
||||
border-slate-200 text-slate-800 bg-slate-200 hover:text-white hover:bg-transparent focus:ring-gray-800 font-semibold shadow
|
||||
disabled:border-gray-400 disabled:text-slate-800 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default memo(UserQuestionnaire);
|
|
@ -9,6 +9,7 @@ import MultiUserSetup from "./Steps/MultiUserSetup";
|
|||
import CreateFirstWorkspace from "./Steps/CreateFirstWorkspace";
|
||||
import EmbeddingSelection from "./Steps/EmbeddingSelection";
|
||||
import DataHandling from "./Steps/DataHandling";
|
||||
import UserQuestionnaire from "./Steps/UserQuestionnaire";
|
||||
|
||||
const DIALOG_ID = "onboarding-modal";
|
||||
|
||||
|
@ -19,6 +20,11 @@ const STEPS = {
|
|||
"These are the credentials and settings for your preferred LLM chat & embedding provider.",
|
||||
component: LLMSelection,
|
||||
},
|
||||
embedding_preferences: {
|
||||
title: "Embedding Preference",
|
||||
description: "Choose a provider for embedding files and text.",
|
||||
component: EmbeddingSelection,
|
||||
},
|
||||
vector_database: {
|
||||
title: "Vector Database",
|
||||
description:
|
||||
|
@ -54,16 +60,17 @@ const STEPS = {
|
|||
"We are committed to transparency and control when it comes to your personal data.",
|
||||
component: DataHandling,
|
||||
},
|
||||
user_questionnaire: {
|
||||
title: "A little about yourself",
|
||||
description:
|
||||
"We use information about how you use AnythingLLM to make our product better.",
|
||||
component: UserQuestionnaire,
|
||||
},
|
||||
create_workspace: {
|
||||
title: "Create Workspace",
|
||||
description: "To get started, create a new workspace.",
|
||||
component: CreateFirstWorkspace,
|
||||
},
|
||||
embedding_preferences: {
|
||||
title: "Embedding Preference",
|
||||
description: "Choose a provider for embedding files and text.",
|
||||
component: EmbeddingSelection,
|
||||
},
|
||||
};
|
||||
|
||||
export const OnboardingModalId = DIALOG_ID;
|
||||
|
|
|
@ -3,6 +3,7 @@ export const API_BASE = import.meta.env.VITE_API_BASE || "/api";
|
|||
export const AUTH_USER = "anythingllm_user";
|
||||
export const AUTH_TOKEN = "anythingllm_authToken";
|
||||
export const AUTH_TIMESTAMP = "anythingllm_authTimestamp";
|
||||
export const COMPLETE_QUESTIONNAIRE = "anythingllm_completed_questionnaire";
|
||||
|
||||
export const USER_BACKGROUND_COLOR = "bg-historical-msg-user";
|
||||
export const AI_BACKGROUND_COLOR = "bg-historical-msg-system";
|
||||
|
|
|
@ -26,7 +26,7 @@ function fetchPfp(pfpPath) {
|
|||
async function determinePfpFilepath(id) {
|
||||
const numberId = Number(id);
|
||||
const user = await User.get({ id: numberId });
|
||||
const pfpFilename = user.pfpFilename;
|
||||
const pfpFilename = user?.pfpFilename || null;
|
||||
if (!pfpFilename) return null;
|
||||
|
||||
const basePath = process.env.STORAGE_DIR
|
||||
|
|
Loading…
Add table
Reference in a new issue