2024-01-04 15:54:31 -08:00
|
|
|
import System from "@/models/system";
|
|
|
|
import showToast from "@/utils/toast";
|
|
|
|
import React, { useState, useEffect, useRef } from "react";
|
|
|
|
import debounce from "lodash.debounce";
|
|
|
|
import paths from "@/utils/paths";
|
|
|
|
import { useNavigate } from "react-router-dom";
|
|
|
|
import { AUTH_TIMESTAMP, AUTH_TOKEN, AUTH_USER } from "@/utils/constants";
|
2025-02-11 15:46:08 -08:00
|
|
|
import { useTranslation } from "react-i18next";
|
2024-01-04 15:54:31 -08:00
|
|
|
|
|
|
|
export default function UserSetup({ setHeader, setForwardBtn, setBackBtn }) {
|
2025-02-11 15:46:08 -08:00
|
|
|
const { t } = useTranslation();
|
2024-01-04 15:54:31 -08:00
|
|
|
const [selectedOption, setSelectedOption] = useState("");
|
|
|
|
const [singleUserPasswordValid, setSingleUserPasswordValid] = useState(false);
|
|
|
|
const [multiUserLoginValid, setMultiUserLoginValid] = useState(false);
|
|
|
|
const [enablePassword, setEnablePassword] = useState(false);
|
|
|
|
const myTeamSubmitRef = useRef(null);
|
|
|
|
const justMeSubmitRef = useRef(null);
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
2025-02-11 15:46:08 -08:00
|
|
|
const TITLE = t("onboarding.userSetup.title");
|
|
|
|
const DESCRIPTION = t("onboarding.userSetup.description");
|
|
|
|
|
2024-01-04 15:54:31 -08:00
|
|
|
function handleForward() {
|
|
|
|
if (selectedOption === "just_me" && enablePassword) {
|
|
|
|
justMeSubmitRef.current?.click();
|
|
|
|
} else if (selectedOption === "just_me" && !enablePassword) {
|
|
|
|
navigate(paths.onboarding.dataHandling());
|
|
|
|
} else if (selectedOption === "my_team") {
|
|
|
|
myTeamSubmitRef.current?.click();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleBack() {
|
2024-05-01 16:26:14 -07:00
|
|
|
navigate(paths.onboarding.llmPreference());
|
2024-01-04 15:54:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
let isDisabled = true;
|
|
|
|
if (selectedOption === "just_me") {
|
|
|
|
isDisabled = !singleUserPasswordValid;
|
|
|
|
} else if (selectedOption === "my_team") {
|
|
|
|
isDisabled = !multiUserLoginValid;
|
|
|
|
}
|
|
|
|
|
|
|
|
setForwardBtn({
|
|
|
|
showing: true,
|
|
|
|
disabled: isDisabled,
|
|
|
|
onClick: handleForward,
|
|
|
|
});
|
|
|
|
}, [selectedOption, singleUserPasswordValid, multiUserLoginValid]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setHeader({ title: TITLE, description: DESCRIPTION });
|
|
|
|
setBackBtn({ showing: true, disabled: false, onClick: handleBack });
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="w-full flex items-center justify-center flex-col gap-y-6">
|
2024-11-18 15:40:18 -08:00
|
|
|
<div className="flex flex-col border rounded-lg border-white/20 light:border-theme-sidebar-border p-8 items-center gap-y-4 w-full max-w-[600px]">
|
2024-01-04 15:54:31 -08:00
|
|
|
<div className=" text-white text-sm font-semibold md:-ml-44">
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("onboarding.userSetup.howManyUsers")}
|
2024-01-04 15:54:31 -08:00
|
|
|
</div>
|
|
|
|
<div className="flex flex-col md:flex-row gap-6 w-full justify-center">
|
|
|
|
<button
|
|
|
|
onClick={() => setSelectedOption("just_me")}
|
|
|
|
className={`${
|
|
|
|
selectedOption === "just_me"
|
|
|
|
? "text-sky-400 border-sky-400/70"
|
2024-11-18 15:40:18 -08:00
|
|
|
: "text-theme-text-primary border-theme-sidebar-border"
|
2024-01-04 15:54:31 -08:00
|
|
|
} min-w-[230px] h-11 p-4 rounded-[10px] border-2 justify-center items-center gap-[100px] inline-flex hover:border-sky-400/70 hover:text-sky-400 transition-all duration-300`}
|
|
|
|
>
|
2025-02-11 15:46:08 -08:00
|
|
|
<div className="text-center text-sm font-bold">
|
|
|
|
{t("onboarding.userSetup.justMe")}
|
|
|
|
</div>
|
2024-01-04 15:54:31 -08:00
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
onClick={() => setSelectedOption("my_team")}
|
|
|
|
className={`${
|
|
|
|
selectedOption === "my_team"
|
|
|
|
? "text-sky-400 border-sky-400/70"
|
2024-11-18 15:40:18 -08:00
|
|
|
: "text-theme-text-primary border-theme-sidebar-border"
|
2024-01-04 15:54:31 -08:00
|
|
|
} min-w-[230px] h-11 p-4 rounded-[10px] border-2 justify-center items-center gap-[100px] inline-flex hover:border-sky-400/70 hover:text-sky-400 transition-all duration-300`}
|
|
|
|
>
|
2025-02-11 15:46:08 -08:00
|
|
|
<div className="text-center text-sm font-bold">
|
|
|
|
{t("onboarding.userSetup.myTeam")}
|
|
|
|
</div>
|
2024-01-04 15:54:31 -08:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{selectedOption === "just_me" && (
|
|
|
|
<JustMe
|
|
|
|
setSingleUserPasswordValid={setSingleUserPasswordValid}
|
|
|
|
enablePassword={enablePassword}
|
|
|
|
setEnablePassword={setEnablePassword}
|
|
|
|
justMeSubmitRef={justMeSubmitRef}
|
|
|
|
navigate={navigate}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{selectedOption === "my_team" && (
|
|
|
|
<MyTeam
|
|
|
|
setMultiUserLoginValid={setMultiUserLoginValid}
|
|
|
|
myTeamSubmitRef={myTeamSubmitRef}
|
|
|
|
navigate={navigate}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const JustMe = ({
|
|
|
|
setSingleUserPasswordValid,
|
|
|
|
enablePassword,
|
|
|
|
setEnablePassword,
|
|
|
|
justMeSubmitRef,
|
|
|
|
navigate,
|
|
|
|
}) => {
|
2025-02-11 15:46:08 -08:00
|
|
|
const { t } = useTranslation();
|
2024-01-04 15:54:31 -08:00
|
|
|
const [itemSelected, setItemSelected] = useState(false);
|
|
|
|
const [password, setPassword] = useState("");
|
|
|
|
const handleSubmit = async (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
const form = e.target;
|
|
|
|
const formData = new FormData(form);
|
|
|
|
const { error } = await System.updateSystemPassword({
|
|
|
|
usePassword: true,
|
|
|
|
newPassword: formData.get("password"),
|
|
|
|
});
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
showToast(`Failed to set password: ${error}`, "error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Auto-request token with password that was just set so they
|
|
|
|
// are not redirected to login after completion.
|
|
|
|
const { token } = await System.requestToken({
|
|
|
|
password: formData.get("password"),
|
|
|
|
});
|
|
|
|
window.localStorage.removeItem(AUTH_USER);
|
|
|
|
window.localStorage.removeItem(AUTH_TIMESTAMP);
|
|
|
|
window.localStorage.setItem(AUTH_TOKEN, token);
|
|
|
|
|
|
|
|
navigate(paths.onboarding.dataHandling());
|
|
|
|
};
|
|
|
|
|
|
|
|
const setNewPassword = (e) => setPassword(e.target.value);
|
|
|
|
const handlePasswordChange = debounce(setNewPassword, 500);
|
|
|
|
|
|
|
|
function handleYes() {
|
|
|
|
setItemSelected(true);
|
|
|
|
setEnablePassword(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleNo() {
|
|
|
|
setItemSelected(true);
|
|
|
|
setEnablePassword(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (enablePassword && itemSelected && password.length >= 8) {
|
|
|
|
setSingleUserPasswordValid(true);
|
|
|
|
} else if (!enablePassword && itemSelected) {
|
|
|
|
setSingleUserPasswordValid(true);
|
|
|
|
} else {
|
|
|
|
setSingleUserPasswordValid(false);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return (
|
|
|
|
<div className="w-full flex items-center justify-center flex-col gap-y-6">
|
2024-11-18 15:40:18 -08:00
|
|
|
<div className="flex flex-col border rounded-lg border-white/20 light:border-theme-sidebar-border p-8 items-center gap-y-4 w-full max-w-[600px]">
|
2024-01-04 15:54:31 -08:00
|
|
|
<div className=" text-white text-sm font-semibold md:-ml-56">
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("onboarding.userSetup.setPassword")}
|
2024-01-04 15:54:31 -08:00
|
|
|
</div>
|
|
|
|
<div className="flex flex-col md:flex-row gap-6 w-full justify-center">
|
|
|
|
<button
|
|
|
|
onClick={handleYes}
|
|
|
|
className={`${
|
|
|
|
enablePassword && itemSelected
|
|
|
|
? "text-sky-400 border-sky-400/70"
|
2024-11-18 15:40:18 -08:00
|
|
|
: "text-theme-text-primary border-theme-sidebar-border"
|
2024-01-04 15:54:31 -08:00
|
|
|
} min-w-[230px] h-11 p-4 rounded-[10px] border-2 justify-center items-center gap-[100px] inline-flex hover:border-sky-400/70 hover:text-sky-400 transition-all duration-300`}
|
|
|
|
>
|
2025-02-11 15:46:08 -08:00
|
|
|
<div className="text-center text-sm font-bold">
|
|
|
|
{t("common.yes")}
|
|
|
|
</div>
|
2024-01-04 15:54:31 -08:00
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
onClick={handleNo}
|
|
|
|
className={`${
|
|
|
|
!enablePassword && itemSelected
|
|
|
|
? "text-sky-400 border-sky-400/70"
|
2024-11-18 15:40:18 -08:00
|
|
|
: "text-theme-text-primary border-theme-sidebar-border"
|
2024-01-04 15:54:31 -08:00
|
|
|
} min-w-[230px] h-11 p-4 rounded-[10px] border-2 justify-center items-center gap-[100px] inline-flex hover:border-sky-400/70 hover:text-sky-400 transition-all duration-300`}
|
|
|
|
>
|
2025-02-11 15:46:08 -08:00
|
|
|
<div className="text-center text-sm font-bold">
|
|
|
|
{t("common.no")}
|
|
|
|
</div>
|
2024-01-04 15:54:31 -08:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
{enablePassword && (
|
|
|
|
<form className="w-full mt-4" onSubmit={handleSubmit}>
|
|
|
|
<label
|
|
|
|
htmlFor="name"
|
|
|
|
className="block mb-3 text-sm font-medium text-white"
|
|
|
|
>
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("onboarding.userSetup.instancePassword")}
|
2024-01-04 15:54:31 -08:00
|
|
|
</label>
|
|
|
|
<input
|
|
|
|
name="password"
|
|
|
|
type="password"
|
2024-11-25 21:07:23 -08:00
|
|
|
className="border-none bg-theme-settings-input-bg text-white text-sm rounded-lg block w-full p-2.5 focus:outline-primary-button active:outline-primary-button outline-none placeholder:text-theme-text-secondary"
|
2024-01-04 15:54:31 -08:00
|
|
|
placeholder="Your admin password"
|
|
|
|
minLength={6}
|
|
|
|
required={true}
|
|
|
|
autoComplete="off"
|
|
|
|
onChange={handlePasswordChange}
|
|
|
|
/>
|
|
|
|
<div className="mt-4 text-white text-opacity-80 text-xs font-base -mb-2">
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("onboarding.userSetup.passwordReq")}
|
2024-01-04 15:54:31 -08:00
|
|
|
<br />
|
2025-02-11 15:46:08 -08:00
|
|
|
<i>{t("onboarding.userSetup.passwordWarn")}</i>{" "}
|
2024-01-04 15:54:31 -08:00
|
|
|
</div>
|
|
|
|
<button
|
|
|
|
type="submit"
|
|
|
|
ref={justMeSubmitRef}
|
|
|
|
hidden
|
|
|
|
aria-hidden="true"
|
|
|
|
></button>
|
|
|
|
</form>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const MyTeam = ({ setMultiUserLoginValid, myTeamSubmitRef, navigate }) => {
|
2025-02-11 15:46:08 -08:00
|
|
|
const { t } = useTranslation();
|
2024-01-04 15:54:31 -08:00
|
|
|
const [username, setUsername] = useState("");
|
|
|
|
const [password, setPassword] = useState("");
|
|
|
|
|
|
|
|
const handleSubmit = async (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
const form = e.target;
|
|
|
|
const formData = new FormData(form);
|
|
|
|
const data = {
|
|
|
|
username: formData.get("username"),
|
|
|
|
password: formData.get("password"),
|
|
|
|
};
|
|
|
|
const { success, error } = await System.setupMultiUser(data);
|
|
|
|
if (!success) {
|
|
|
|
showToast(`Error: ${error}`, "error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
navigate(paths.onboarding.dataHandling());
|
|
|
|
// Auto-request token with credentials that was just set so they
|
|
|
|
// are not redirected to login after completion.
|
|
|
|
const { user, token } = await System.requestToken(data);
|
|
|
|
window.localStorage.setItem(AUTH_USER, JSON.stringify(user));
|
|
|
|
window.localStorage.setItem(AUTH_TOKEN, token);
|
|
|
|
window.localStorage.removeItem(AUTH_TIMESTAMP);
|
|
|
|
};
|
|
|
|
|
|
|
|
const setNewUsername = (e) => setUsername(e.target.value);
|
|
|
|
const setNewPassword = (e) => setPassword(e.target.value);
|
|
|
|
const handleUsernameChange = debounce(setNewUsername, 500);
|
|
|
|
const handlePasswordChange = debounce(setNewPassword, 500);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (username.length >= 6 && password.length >= 8) {
|
|
|
|
setMultiUserLoginValid(true);
|
|
|
|
} else {
|
|
|
|
setMultiUserLoginValid(false);
|
|
|
|
}
|
|
|
|
}, [username, password]);
|
|
|
|
return (
|
2024-11-18 15:40:18 -08:00
|
|
|
<div className="w-full flex items-center justify-center border max-w-[600px] rounded-lg border-white/20 light:border-theme-sidebar-border">
|
2024-01-04 15:54:31 -08:00
|
|
|
<form onSubmit={handleSubmit}>
|
|
|
|
<div className="flex flex-col w-full md:px-8 px-2 py-4">
|
|
|
|
<div className="space-y-6 flex h-full w-full">
|
|
|
|
<div className="w-full flex flex-col gap-y-4">
|
|
|
|
<div>
|
|
|
|
<label
|
|
|
|
htmlFor="name"
|
|
|
|
className="block mb-3 text-sm font-medium text-white"
|
|
|
|
>
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("common.adminUsername")}
|
2024-01-04 15:54:31 -08:00
|
|
|
</label>
|
|
|
|
<input
|
|
|
|
name="username"
|
|
|
|
type="text"
|
2024-11-25 21:07:23 -08:00
|
|
|
className="border-none bg-theme-settings-input-bg text-white text-sm rounded-lg block w-full p-2.5 focus:outline-primary-button active:outline-primary-button placeholder:text-theme-text-secondary outline-none"
|
2024-01-04 15:54:31 -08:00
|
|
|
placeholder="Your admin username"
|
|
|
|
minLength={6}
|
|
|
|
required={true}
|
|
|
|
autoComplete="off"
|
|
|
|
onChange={handleUsernameChange}
|
|
|
|
/>
|
|
|
|
</div>
|
2024-08-16 12:55:14 -07:00
|
|
|
<p className=" text-white text-opacity-80 text-xs font-base">
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("onboarding.userSetup.adminUsernameReq")}
|
2024-08-16 12:55:14 -07:00
|
|
|
</p>
|
2024-01-04 15:54:31 -08:00
|
|
|
<div className="mt-4">
|
|
|
|
<label
|
|
|
|
htmlFor="name"
|
|
|
|
className="block mb-3 text-sm font-medium text-white"
|
|
|
|
>
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("onboarding.userSetup.adminPassword")}
|
2024-01-04 15:54:31 -08:00
|
|
|
</label>
|
|
|
|
<input
|
|
|
|
name="password"
|
|
|
|
type="password"
|
2024-11-25 21:07:23 -08:00
|
|
|
className="border-none bg-theme-settings-input-bg text-white text-sm rounded-lg block w-full p-2.5 focus:outline-primary-button active:outline-primary-button placeholder:text-theme-text-secondary outline-none"
|
2024-01-04 15:54:31 -08:00
|
|
|
placeholder="Your admin password"
|
|
|
|
minLength={8}
|
|
|
|
required={true}
|
|
|
|
autoComplete="off"
|
|
|
|
onChange={handlePasswordChange}
|
|
|
|
/>
|
|
|
|
</div>
|
2024-08-16 12:55:14 -07:00
|
|
|
<p className=" text-white text-opacity-80 text-xs font-base">
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("onboarding.userSetup.adminPasswordReq")}
|
2024-01-04 15:54:31 -08:00
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2024-11-18 15:40:18 -08:00
|
|
|
<div className="flex w-full justify-between items-center px-6 py-4 space-x-6 border-t rounded-b border-theme-sidebar-border">
|
|
|
|
<div className="text-theme-text-secondary text-opacity-80 text-xs font-base">
|
2025-02-11 15:46:08 -08:00
|
|
|
{t("onboarding.userSetup.teamHint")}
|
2024-01-04 15:54:31 -08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<button
|
|
|
|
type="submit"
|
|
|
|
ref={myTeamSubmitRef}
|
|
|
|
hidden
|
|
|
|
aria-hidden="true"
|
|
|
|
></button>
|
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|