mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-21 07:02:32 +02:00
631 lines
23 KiB
React
631 lines
23 KiB
React
import { useEffect, useState } from "react";
|
|
import Parse from "parse";
|
|
import { useDispatch } from "react-redux";
|
|
import axios from "axios";
|
|
import { NavLink, useNavigate, useLocation } from "react-router";
|
|
import login_img from "../assets/images/login_img.svg";
|
|
import { useWindowSize } from "../hook/useWindowSize";
|
|
import ModalUi from "../primitives/ModalUi";
|
|
import {
|
|
emailRegex,
|
|
} from "../constant/const";
|
|
import Alert from "../primitives/Alert";
|
|
import { appInfo } from "../constant/appinfo";
|
|
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
|
import { showTenant } from "../redux/reducers/ShowTenant";
|
|
import {
|
|
getAppLogo,
|
|
saveLanguageInLocal,
|
|
usertimezone
|
|
} from "../constant/Utils";
|
|
import Loader from "../primitives/Loader";
|
|
import { useTranslation } from "react-i18next";
|
|
import SelectLanguage from "../components/pdf/SelectLanguage";
|
|
|
|
function Login() {
|
|
const appName =
|
|
"OpenSign™";
|
|
const { t, i18n } = useTranslation();
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const dispatch = useDispatch();
|
|
const { width } = useWindowSize();
|
|
const [state, setState] = useState({
|
|
email: "",
|
|
password: "",
|
|
alertType: "success",
|
|
alertMsg: "",
|
|
passwordVisible: false,
|
|
loading: false,
|
|
thirdpartyLoader: false,
|
|
});
|
|
const [userDetails, setUserDetails] = useState({
|
|
Company: "",
|
|
Destination: ""
|
|
});
|
|
const [isModal, setIsModal] = useState(false);
|
|
const [image, setImage] = useState();
|
|
const [errMsg, setErrMsg] = useState();
|
|
useEffect(() => {
|
|
handleUserExist();
|
|
// eslint-disable-next-line
|
|
}, []);
|
|
|
|
const handleUserExist = async () => {
|
|
checkUserExt();
|
|
};
|
|
|
|
|
|
const setLocalVar = (user) => {
|
|
localStorage.setItem("accesstoken", user.sessionToken);
|
|
localStorage.setItem("UserInformation", JSON.stringify(user));
|
|
localStorage.setItem("userEmail", user.email);
|
|
if (user.ProfilePic) {
|
|
localStorage.setItem("profileImg", user.ProfilePic);
|
|
} else {
|
|
localStorage.setItem("profileImg", "");
|
|
}
|
|
};
|
|
|
|
const showToast = (type, msg) => {
|
|
setState({ ...state, loading: false, alertType: type, alertMsg: msg });
|
|
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
|
};
|
|
|
|
const checkUserExt = async () => {
|
|
const app = await getAppLogo();
|
|
if (app?.error === "invalid_json") {
|
|
setErrMsg(t("server-down", { appName: appName }));
|
|
} else if (
|
|
app?.user === "not_exist"
|
|
) {
|
|
navigate("/addadmin");
|
|
}
|
|
if (app?.logo) {
|
|
setImage(app?.logo);
|
|
} else {
|
|
setImage(appInfo?.applogo || undefined);
|
|
}
|
|
dispatch(fetchAppInfo());
|
|
if (localStorage.getItem("accesstoken")) {
|
|
setState({ ...state, loading: true });
|
|
GetLoginData();
|
|
}
|
|
};
|
|
const handleChange = (event) => {
|
|
let { name, value } = event.target;
|
|
if (name === "email") {
|
|
value = value?.toLowerCase()?.replace(/\s/g, "");
|
|
}
|
|
setState({ ...state, [name]: value });
|
|
};
|
|
|
|
const handleLogin = async (
|
|
) => {
|
|
const email = state?.email
|
|
const password = state?.password
|
|
|
|
if (!email || !password) {
|
|
return;
|
|
}
|
|
localStorage.removeItem("accesstoken");
|
|
try {
|
|
setState({ ...state, loading: true });
|
|
localStorage.setItem("appLogo", appInfo.applogo);
|
|
const _user = await Parse.Cloud.run("loginuser", { email, password });
|
|
if (!_user) {
|
|
setState({ ...state, loading: false });
|
|
return;
|
|
}
|
|
// Get extended user data (including 2FA status) using cloud function
|
|
try {
|
|
await Parse.User.become(_user.sessionToken);
|
|
setLocalVar(_user);
|
|
await continueLoginFlow();
|
|
} catch (error) {
|
|
console.error("Error checking 2FA status:", error);
|
|
showToast("danger", t("something-went-wrong-mssg"));
|
|
}
|
|
} catch (error) {
|
|
console.error("Error while logging in user", error);
|
|
if (error?.code === 1001) {
|
|
showToast("danger", t("action-prohibited"));
|
|
} else {
|
|
showToast("danger", t("invalid-username-password-region"));
|
|
}
|
|
}
|
|
};
|
|
const handleLoginBtn = async (event) => {
|
|
event.preventDefault();
|
|
if (!emailRegex.test(state.email)) {
|
|
alert(t("valid-email-alert"));
|
|
return;
|
|
}
|
|
await handleLogin();
|
|
};
|
|
|
|
const setThirdpartyLoader = (value) => {
|
|
setState({ ...state, thirdpartyLoader: value });
|
|
};
|
|
|
|
const thirdpartyLoginfn = async (sessionToken) => {
|
|
const baseUrl = localStorage.getItem("baseUrl");
|
|
const parseAppId = localStorage.getItem("parseAppId");
|
|
const res = await axios.get(baseUrl + "users/me", {
|
|
headers: {
|
|
"X-Parse-Session-Token": sessionToken,
|
|
"X-Parse-Application-Id": parseAppId
|
|
}
|
|
});
|
|
await Parse.User.become(sessionToken).then(() => {
|
|
window.localStorage.setItem("accesstoken", sessionToken);
|
|
});
|
|
if (res.data) {
|
|
let _user = res.data;
|
|
setLocalVar(_user);
|
|
// Check extended class user role and tenentId
|
|
try {
|
|
const userSettings = appInfo.settings;
|
|
const extUser = await Parse.Cloud.run("getUserDetails");
|
|
if (extUser) {
|
|
const IsDisabled = extUser?.get("IsDisabled") || false;
|
|
if (!IsDisabled) {
|
|
const userRole = extUser?.get("UserRole");
|
|
const menu =
|
|
userRole && userSettings.find((menu) => menu.role === userRole);
|
|
if (menu) {
|
|
const _currentRole = userRole;
|
|
const redirectUrl =
|
|
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
|
const _role = _currentRole.replace("contracts_", "");
|
|
const extInfo = JSON.parse(JSON.stringify(extUser));
|
|
localStorage.setItem("_user_role", _role);
|
|
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
|
localStorage.setItem("userEmail", extInfo?.Email);
|
|
localStorage.setItem("username", extInfo?.Name);
|
|
if (extInfo?.TenantId) {
|
|
const tenant = {
|
|
Id: extInfo?.TenantId?.objectId || "",
|
|
Name: extInfo?.TenantId?.TenantName || ""
|
|
};
|
|
localStorage.setItem("TenantId", tenant?.Id);
|
|
dispatch(showTenant(tenant?.Name));
|
|
localStorage.setItem("TenantName", tenant?.Name);
|
|
}
|
|
localStorage.setItem("PageLanding", menu.pageId);
|
|
localStorage.setItem("defaultmenuid", menu.menuId);
|
|
localStorage.setItem("pageType", menu.pageType);
|
|
navigate(redirectUrl);
|
|
} else {
|
|
showToast("danger", t("role-not-found"));
|
|
logOutUser();
|
|
}
|
|
} else {
|
|
showToast("danger", t("do-not-access-contact-admin"));
|
|
logOutUser();
|
|
}
|
|
} else {
|
|
showToast("danger", t("user-not-found"));
|
|
logOutUser();
|
|
}
|
|
} catch (error) {
|
|
console.error("err in fetching extUser", err);
|
|
showToast("danger", `${err.message}`);
|
|
const payload = { sessionToken: _user.sessionToken };
|
|
handleSubmitbtn(payload);
|
|
} finally {
|
|
setThirdpartyLoader(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
const GetLoginData = async () => {
|
|
setState({ ...state, loading: true });
|
|
try {
|
|
const user = await Parse.User.become(localStorage.getItem("accesstoken"));
|
|
const _user = user.toJSON();
|
|
setLocalVar(_user);
|
|
const userSettings = appInfo.settings;
|
|
const extUser = await Parse.Cloud.run("getUserDetails");
|
|
if (extUser) {
|
|
const IsDisabled = extUser?.get("IsDisabled") || false;
|
|
if (!IsDisabled) {
|
|
const userRole = extUser.get("UserRole");
|
|
const _currentRole = userRole;
|
|
const menu =
|
|
userRole && userSettings.find((menu) => menu.role === userRole);
|
|
if (menu) {
|
|
const extInfo = JSON.parse(JSON.stringify(extUser));
|
|
const _role = _currentRole.replace("contracts_", "");
|
|
localStorage.setItem("_user_role", _role);
|
|
const redirectUrl =
|
|
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
|
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
|
localStorage.setItem("userEmail", extInfo.Email);
|
|
localStorage.setItem("username", extInfo.Name);
|
|
if (extInfo?.TenantId) {
|
|
const tenant = {
|
|
Id: extInfo?.TenantId?.objectId || "",
|
|
Name: extInfo?.TenantId?.TenantName || ""
|
|
};
|
|
localStorage.setItem("TenantId", tenant?.Id);
|
|
dispatch(showTenant(tenant?.Name));
|
|
localStorage.setItem("TenantName", tenant?.Name);
|
|
}
|
|
localStorage.setItem("PageLanding", menu.pageId);
|
|
localStorage.setItem("defaultmenuid", menu.menuId);
|
|
localStorage.setItem("pageType", menu.pageType);
|
|
navigate(redirectUrl);
|
|
} else {
|
|
setState({ ...state, loading: false });
|
|
logOutUser();
|
|
}
|
|
} else {
|
|
showToast("danger", t("do-not-access-contact-admin"));
|
|
logOutUser();
|
|
}
|
|
} else {
|
|
showToast("danger", t("user-not-found"));
|
|
logOutUser();
|
|
}
|
|
} catch (error) {
|
|
showToast("danger", t("something-went-wrong-mssg"));
|
|
console.log("err", error);
|
|
}
|
|
};
|
|
|
|
const togglePasswordVisibility = () => {
|
|
setState({ ...state, passwordVisible: !state.passwordVisible });
|
|
};
|
|
|
|
const handleSubmitbtn = async (e) => {
|
|
e.preventDefault();
|
|
if (userDetails.Destination && userDetails.Company) {
|
|
setThirdpartyLoader(true);
|
|
const payload = { sessionToken: localStorage.getItem("accesstoken") };
|
|
const userInformation = JSON.parse(
|
|
localStorage.getItem("UserInformation")
|
|
);
|
|
if (payload && payload.sessionToken) {
|
|
const params = {
|
|
userDetails: {
|
|
name: userInformation.name,
|
|
email: userInformation.email,
|
|
phone: userInformation?.phone || "",
|
|
role: "contracts_User",
|
|
company: userDetails.Company,
|
|
jobTitle: userDetails.Destination,
|
|
timezone: usertimezone
|
|
}
|
|
};
|
|
const userSignUp = await Parse.Cloud.run("usersignup", params);
|
|
if (userSignUp && userSignUp.sessionToken) {
|
|
const LocalUserDetails = {
|
|
name: userInformation.name,
|
|
email: userInformation.email,
|
|
phone: userInformation?.phone || "",
|
|
company: userDetails.Company,
|
|
jobTitle: userDetails.JobTitle
|
|
};
|
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
|
thirdpartyLoginfn(userSignUp.sessionToken);
|
|
} else {
|
|
alert(userSignUp.message);
|
|
}
|
|
} else if (
|
|
payload &&
|
|
payload.message.replace(/ /g, "_") === "Internal_server_err"
|
|
) {
|
|
alert(t("server-error"));
|
|
}
|
|
} else {
|
|
showToast("warning", t("fill-required-details!"));
|
|
}
|
|
};
|
|
|
|
const logOutUser = async () => {
|
|
setIsModal(false);
|
|
try {
|
|
await Parse.User.logOut();
|
|
} catch (err) {
|
|
console.log("Err while logging out", err);
|
|
}
|
|
let appdata = localStorage.getItem("userSettings");
|
|
let applogo = localStorage.getItem("appLogo");
|
|
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
|
let PageLanding = localStorage.getItem("PageLanding");
|
|
let baseUrl = localStorage.getItem("baseUrl");
|
|
let appid = localStorage.getItem("parseAppId");
|
|
let favicon = localStorage.getItem("favicon");
|
|
|
|
localStorage.clear();
|
|
saveLanguageInLocal(i18n);
|
|
|
|
localStorage.setItem("appLogo", applogo);
|
|
localStorage.setItem("defaultmenuid", defaultmenuid);
|
|
localStorage.setItem("PageLanding", PageLanding);
|
|
localStorage.setItem("userSettings", appdata);
|
|
localStorage.setItem("baseUrl", baseUrl);
|
|
localStorage.setItem("parseAppId", appid);
|
|
localStorage.setItem("favicon", favicon);
|
|
};
|
|
|
|
const continueLoginFlow = async () => {
|
|
try {
|
|
const userSettings = appInfo.settings;
|
|
const extUser = await Parse.Cloud.run("getUserDetails");
|
|
if (extUser) {
|
|
const IsDisabled = extUser?.get("IsDisabled") || false;
|
|
if (!IsDisabled) {
|
|
const userRole = extUser?.get("UserRole");
|
|
const menu =
|
|
userRole && userSettings?.find((menu) => menu.role === userRole);
|
|
if (menu) {
|
|
const _currentRole = userRole;
|
|
const redirectUrl =
|
|
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
|
const _role = _currentRole.replace("contracts_", "");
|
|
localStorage.setItem("_user_role", _role);
|
|
const checkLanguage = extUser?.get("Language");
|
|
if (checkLanguage) {
|
|
checkLanguage && i18n.changeLanguage(checkLanguage);
|
|
}
|
|
const extInfo = JSON.parse(JSON.stringify(extUser));
|
|
// Continue with storing user data and redirecting
|
|
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
|
localStorage.setItem("userEmail", extInfo.Email);
|
|
localStorage.setItem("username", extInfo.Name);
|
|
if (extInfo?.TenantId) {
|
|
const tenant = {
|
|
Id: extInfo?.TenantId?.objectId || "",
|
|
Name: extInfo?.TenantId?.TenantName || ""
|
|
};
|
|
localStorage.setItem("TenantId", tenant?.Id);
|
|
dispatch(showTenant(tenant?.Name));
|
|
localStorage.setItem("TenantName", tenant?.Name);
|
|
}
|
|
localStorage.setItem("PageLanding", menu.pageId);
|
|
localStorage.setItem("defaultmenuid", menu.menuId);
|
|
localStorage.setItem("pageType", menu.pageType);
|
|
setState({ ...state, loading: false });
|
|
navigate(redirectUrl);
|
|
} else {
|
|
setState({ ...state, loading: false });
|
|
setIsModal(true);
|
|
}
|
|
} else {
|
|
showToast("danger", t("do-not-access-contact-admin"));
|
|
logOutUser();
|
|
}
|
|
} else {
|
|
showToast("danger", t("user-not-found"));
|
|
logOutUser();
|
|
}
|
|
} catch (error) {
|
|
console.error("Error during login flow", error);
|
|
showToast("danger", error.message || t("something-went-wrong-mssg"));
|
|
}
|
|
};
|
|
|
|
return errMsg ? (
|
|
<div className="h-screen flex justify-center text-center items-center p-4 text-gray-500 text-base">
|
|
{errMsg}
|
|
</div>
|
|
) : (
|
|
<>
|
|
{state.loading && (
|
|
<div
|
|
aria-live="assertive"
|
|
className="fixed w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-50"
|
|
>
|
|
<Loader />
|
|
</div>
|
|
)}
|
|
{appInfo && appInfo.appId ? (
|
|
<>
|
|
<div
|
|
aria-labelledby="loginHeading"
|
|
role="region"
|
|
className="pb-1 md:pb-4 pt-10 md:px-10 lg:px-16 h-full"
|
|
>
|
|
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
|
|
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
|
{image && (
|
|
<img
|
|
src={image}
|
|
className="object-contain h-full"
|
|
alt="applogo"
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
|
<div>
|
|
<form onSubmit={handleLoginBtn} aria-label="Login Form">
|
|
<h1 className="text-[30px] mt-6">{t("welcome")}</h1>
|
|
<fieldset>
|
|
<legend className="text-[12px] text-[#878787]">
|
|
{t("Login-to-your-account")}
|
|
</legend>
|
|
<div className="w-full px-6 py-3 my-1 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
|
|
<label className="block text-xs" htmlFor="email">
|
|
{t("email")}
|
|
</label>
|
|
<input
|
|
id="email"
|
|
type="email"
|
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
name="email"
|
|
autoComplete="username"
|
|
value={state.email}
|
|
onChange={handleChange}
|
|
required
|
|
onInvalid={(e) =>
|
|
e.target.setCustomValidity(t("input-required"))
|
|
}
|
|
onInput={(e) => e.target.setCustomValidity("")}
|
|
/>
|
|
<hr className="my-1 border-none" />
|
|
<label className="block text-xs" htmlFor="password">
|
|
{t("password")}
|
|
</label>
|
|
<div className="relative">
|
|
<input
|
|
id="password"
|
|
type={
|
|
state.passwordVisible ? "text" : "password"
|
|
}
|
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
name="password"
|
|
value={state.password}
|
|
autoComplete="current-password"
|
|
onChange={handleChange}
|
|
onInvalid={(e) =>
|
|
e.target.setCustomValidity(
|
|
t("input-required")
|
|
)
|
|
}
|
|
onInput={(e) => e.target.setCustomValidity("")}
|
|
required
|
|
/>
|
|
<span
|
|
className="absolute cursor-pointer top-[50%] right-[10px] -translate-y-[50%] text-base-content"
|
|
onClick={togglePasswordVisibility}
|
|
>
|
|
{state.passwordVisible ? (
|
|
<i className="fa-light fa-eye-slash text-xs pb-1" /> // Close eye icon
|
|
) : (
|
|
<i className="fa-light fa-eye text-xs pb-1 " /> // Open eye icon
|
|
)}
|
|
</span>
|
|
</div>
|
|
<div className="relative mt-1">
|
|
<NavLink
|
|
to="/forgetpassword"
|
|
className="text-[13px] op-link op-link-primary underline-offset-1 focus:outline-none ml-1"
|
|
>
|
|
{t("forgot-password")}?
|
|
</NavLink>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold mt-2">
|
|
<button
|
|
type="submit"
|
|
className="op-btn op-btn-primary"
|
|
disabled={state.loading}
|
|
>
|
|
{state.loading ? t("loading") : t("login")}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{width >= 768 && (
|
|
<div className="place-self-center">
|
|
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
|
|
<img
|
|
src={login_img}
|
|
alt="The image illustrates a person from behind, seated at a desk with a four-monitor computer setup, in an environment with a light blue and white color scheme, featuring a potted plant to the right."
|
|
width="100%"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<SelectLanguage />
|
|
{state.alertMsg && (
|
|
<Alert type={state.alertType}>{state.alertMsg}</Alert>
|
|
)}
|
|
</div>
|
|
<ModalUi
|
|
isOpen={isModal}
|
|
title={t("additional-info")}
|
|
showClose={false}
|
|
>
|
|
<form className="px-4 py-3 text-base-content">
|
|
<div className="mb-3">
|
|
<label
|
|
htmlFor="Company"
|
|
style={{ display: "flex" }}
|
|
className="block text-xs font-semibold"
|
|
>
|
|
{t("company")}{" "}
|
|
<span className="text-[red] text-[13px]">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
id="Company"
|
|
value={userDetails.Company}
|
|
onChange={(e) =>
|
|
setUserDetails({
|
|
...userDetails,
|
|
Company: e.target.value
|
|
})
|
|
}
|
|
onInvalid={(e) =>
|
|
e.target.setCustomValidity(t("input-required"))
|
|
}
|
|
onInput={(e) => e.target.setCustomValidity("")}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="mb-3">
|
|
<label
|
|
htmlFor="JobTitle"
|
|
style={{ display: "flex" }}
|
|
className="block text-xs font-semibold"
|
|
>
|
|
{t("job-title")}
|
|
<span className="text-[red] text-[13px]">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
id="JobTitle"
|
|
value={userDetails.Destination}
|
|
onChange={(e) =>
|
|
setUserDetails({
|
|
...userDetails,
|
|
Destination: e.target.value
|
|
})
|
|
}
|
|
onInvalid={(e) =>
|
|
e.target.setCustomValidity(t("input-required"))
|
|
}
|
|
onInput={(e) => e.target.setCustomValidity("")}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="mt-4 gap-2 flex flex-row">
|
|
<button
|
|
type="button"
|
|
className="op-btn op-btn-primary"
|
|
onClick={(e) => handleSubmitbtn(e)}
|
|
>
|
|
{t("login")}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="op-btn op-btn-ghost text-base-content"
|
|
onClick={logOutUser}
|
|
>
|
|
{t("cancel")}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</ModalUi>
|
|
</>
|
|
) : (
|
|
<div
|
|
aria-live="assertive"
|
|
className="fixed w-full h-full flex justify-center items-center z-50"
|
|
>
|
|
<Loader />
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
export default Login;
|