@@ -1210,12 +1594,14 @@ const DocsWrapper = memo(({isLoggedIn, isLoaded, children })=>{
return (
{children}
diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx
index 719a4717..9bcc6966 100755
--- a/frontend/src/views/LoginPage.jsx
+++ b/frontend/src/views/LoginPage.jsx
@@ -1,546 +1,1007 @@
/* eslint-disable react/no-multi-comp */
-import React, { useState, useEffect } from "react";
-import { makeStyles } from "@mui/styles";
+import React, { useState, useEffect, } from 'react';
+import { makeStyles } from '@mui/styles';
+import { useNavigate, Link, useParams } from "react-router-dom";
+import { isMobile } from "react-device-detect";
+import { toast } from 'react-toastify';
import { useInterval } from "react-powerhooks";
-import theme from '../theme.jsx';
+import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
+import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
+import {
+ ConnectingAirportsOutlined,
+ ConstructionOutlined,
+ DoneRounded,
+ Done as DoneIcon
+} from '@mui/icons-material';
import {
- CircularProgress,
- TextField,
- Button,
- Paper,
- Typography,
-} from "@mui/material";
-
-import { useNavigate } from "react-router-dom";
+ Checkbox,
+ CircularProgress,
+ TextField,
+ Button,
+ Paper,
+ Typography,
+ Tooltip,
+ TableHead,
+ TableRow,
+ TableContainer,
+ TableCell,
+ TableBody,
+ Table,
+} from '@mui/material';
const hrefStyle = {
- color: "white",
- textDecoration: "none",
-};
+ color: "#FF8444",
+ fontSize: "14px",
+ textDecoration: "none",
+ display: "flex",
+}
-const bodyDivStyle = {
- margin: "auto",
- marginTop: 150,
- width: "500px",
-};
+const googleLoginIcon = {
+ boxSizing: "border-box",
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "center",
+ alignItems: "center",
+ padding: "16px",
+ gap: "8px",
+ // position: "sticky",
+ width: 171,
+ height: "51px",
+ left: "352px",
+ top: "725px",
+ background: "#1A1A1A",
+ border: "1px solid #494949",
+ borderRadius: "8px",
+
+}
+
+const githubLoginIcon = {
+ boxSizing: "border-box",
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "center",
+ alignItems: "center",
+ padding: "16px",
+ gap: "8px",
+ position: "sticky",
+ width: "173px",
+ height: "51px",
+ left: "539px",
+ top: "725px",
+ backgroundColor: "#1A1A1A",
+ border: "1px solid #494949",
+ borderRadius: "8px",
+}
+
+const surfaceColor = "#27292D"
+const inputColor = "#383B40"
const useStyles = makeStyles({
- notchedOutline: {
- borderColor: "#f85a3e !important",
- },
+ notchedOutline: {
+ borderColor: "#f85a3e !important"
+ },
+ marketplaceIcon: {
+ width: 28,
+ height: 28,
+ marginRight: 12
+ },
+ divider: {
+ display: "flex",
+ alignItems: "center",
+ margin: "0 20px",
+ '&::before, &::after': {
+ content: '""',
+ flex: 1,
+ borderBottom: "1px solid #494949"
+ },
+ '& span': {
+ margin: "0 10px",
+ color: "#9E9E9E"
+ }
+ },
+ freePlanCard: {
+ padding: "40px",
+ background: "#212121",
+ borderRadius: "12px",
+ width: "100%",
+ maxWidth: "500px"
+ },
+ freePlanTitle: {
+ fontSize: "28px",
+ fontWeight: 600,
+ color: "white",
+ marginTop: 0,
+ marginBottom: "32px"
+ },
+ featureItem: {
+ display: "flex",
+ alignItems: "center",
+ marginBottom: "20px",
+ color: "white",
+ fontSize: "16px",
+ fontWeight: 500
+ },
+ checkIcon: {
+ color: "#4CAF50",
+ marginRight: "16px",
+ width: "24px",
+ height: "24px"
+ }
});
-const LoginDialog = (props) => {
- const {
- globalUrl,
- isLoaded,
- isLoggedIn,
- setIsLoggedIn,
- setCookie,
- register,
- checkLogin,
- } = props;
+const FreePlanCard = ({ classes }) => {
+ const features = [
+ "Access to All Apps",
+ "10,000 App Runs",
+ "Monthly Runs Refresh",
+ "Unlimited Users & Workflows",
+ "Multi-Tenancy & -Region",
+ "Support & Discord Access"
+ ];
+
+ return (
+
+
+ The free plan includes:
+
+
+ {features.map((feature, index) => (
+
+
+ {feature}
+
+ ))}
+
+ );
+};
+
+const MarketplaceCard = ({ classes }) => {
+ const marketplaceOptions = [
+ {
+ name: "Open Source Install",
+ logo: "https://static.cdnlogo.com/logos/g/69/github-icon.svg",
+ tooltipText: "Click to install!",
+ valid: true,
+
+ link: "https://github.com/shuffle/shuffle/blob/main/.github/install-guide.md"
+ },
+ {
+ name: "Amazon Web Services",
+ logo: "https://cdn.cdnlogo.com/logos/a/19/aws.svg",
+ tooltipText: "Coming soon to AWS Marketplace!",
+ valid: false,
+ },
+ {
+ name: "Microsoft Azure",
+ logo: "https://cdn.cdnlogo.com/logos/a/12/azure.svg",
+ tooltipText: "Coming soon to Azure Marketplace!",
+ valid: false,
+ },
+ {
+ name: "Google Cloud Platform",
+ logo: "https://cdn.cdnlogo.com/logos/g/75/google-cloud.svg",
+ tooltipText: "Coming soon to Google Cloud Marketplace!",
+ valid: false,
+ }
+ ];
+
+ return (
+
+
+ Self Host
+
+
+ {marketplaceOptions.map((option, index) => {
+ return (
+
+
+
+ )
+ })}
+
+
+
+
+ );
+};
+
+
+const LoginPage = props => {
+ const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, inregister, serverside, checkLogin, } = props;
let navigate = useNavigate();
- const classes = useStyles();
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const [message, setMessage] = useState("");
+ const [loginLoading, setLoginLoading] = useState(false);
- const [username, setUsername] = useState("");
- const [password, setPassword] = useState("");
- const [firstRequest, setFirstRequest] = useState(true);
- const [loginLoading, setLoginLoading] = useState(false);
- const [loginViewLoading, setLoginViewLoading] = useState(false);
- const [ssoUrl, setSSOUrl] = useState("");
+ const [MFAField, setMFAField] = useState(false);
+ const [MFAValue, setMFAValue] = useState("");
+ const [register, setRegister] = useState(inregister);
+ const [checkboxClicked, setCheckboxClicked] = useState(false);
+ const [loginWithSSO, setLoginWithSSO] = useState(false)
+ const [showPassword, setShowPassword] = useState(false)
+ const [ssoUrl, setSSOUrl] = useState("");
- const [MFAField, setMFAField] = useState(false);
- const [MFAValue, setMFAValue] = useState("");
-
-
- // Used to swap from login to register. True = login, false = register
+ const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io";
+ const parsedsearch = serverside === true ? "" : window.location.search
useEffect(() => {
- checkAdmin()
- }, [loginViewLoading])
+ if (!isCloud) {
+ checkAdmin()
+ }
+ }, [])
- // Error messages etc
- const [loginInfo, setLoginInfo] = useState("");
-
- const handleValidateForm = () => {
- return username.length > 1 && password.length > 1;
- };
-
- if (isLoggedIn === true) {
- //window.location.pathname = "/workflows";
- navigate("/workflows")
- }
-
- const checkAdmin = () => {
- const url = globalUrl + "/api/v1/checkusers";
- fetch(url, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
+ const { start, stop } = useInterval({
+ duration: 3000,
+ startImmediate: false,
+ callback: () => {
+ checkAdmin()
},
})
+
+ if (serverside !== true) {
+ const tmpMessage = new URLSearchParams(window.location.search).get("message")
+ if (tmpMessage !== undefined && tmpMessage !== null && message !== tmpMessage) {
+ setMessage(tmpMessage)
+ }
+ }
+
+ if (document !== undefined) {
+ if (register) {
+ document.title = "Login to Shuffle SaaS"
+ } else {
+ document.title = "Register to Shuffle SaaS"
+ }
+ }
+
+ useEffect(() => {
+
+ if (loginWithSSO && window?.location?.pathname === "/register") {
+ setLoginWithSSO(false)
+ }
+
+ },[window?.location?.pathname])
+
+ // Just a way to force location loading properly
+ // Register & login should be split :3
+ if (window !== undefined) {
+ const path = window.location.pathname;
+ if (path.includes("/login") && register === false) {
+ console.log("Should register instead of login!")
+ setRegister(!register)
+ } else if (path.includes("/register") && register === true) {
+ if (!isCloud) {
+ setRegister(true)
+ navigate("/login")
+ }
+
+ console.log("Should login instead of register!")
+ setRegister(!register)
+ } else {
+ console.log("Path: " + path, "Register: " + register)
+ }
+ }
+
+ const bodyDivStyle = {
+ marginTop: 100,
+ width: isMobile ? "100%" : "100%",
+ maxWidth: "1200px", // Increased max-width to accommodate larger cards
+ background: "#1A1A1A",
+ margin: "auto",
+ display: isMobile ? "block" : "flex",
+ padding: "40px",
+ gap: "40px",
+ overflow: "hidden",
+ alignItems: "center"
+ };
+
+ const boxStyle = {
+ color: "white",
+ padding: "40px",
+ flex: 1,
+ maxWidth: isMobile ? "100%" : 410,
+ minWidth: 410,
+ background: "#212121",
+ borderRadius: "12px",
+ display: "flex",
+ // flexDirection: "column",
+ };
+
+
+
+ const paperStyleReg = {
+ width: 300,
+ height: 350,
+ background: "#212121",
+ borderRadius: "8px",
+ marginLeft: isMobile ? 80 : register ? "455px" : "485px",
+ marginTop: isMobile ? 10 : 110,
+ position: isMobile ? "" : "absolute",
+
+ }
+ const paperStyleLog = {
+ position: "absolute",
+ width: isMobile ? "400px" : "532px",
+ padding: "0px 30px 0px",
+ // marginTop: "135px",
+ // background: "#212121",
+ borderRadius: "8px",
+ }
+ const createData = (icon, title) => {
+ return {
+ icon,
+ title,
+ }
+ }
+
+ // Used to swap from login to register. True = login, false = register
+ const activeIcon =
+ const rows = [
+ createData(activeIcon, "Access to All Apps"),
+ createData(activeIcon, "10,000 App Runs"),
+ createData(activeIcon, "Monthly Runs Refresh"),
+ createData(activeIcon, "Unlimited Users & Workflows"),
+ createData(activeIcon, "Multi-Tenancy & -Region"),
+ createData(activeIcon, "Support & Discord Access"),
+ ];
+ const classes = useStyles();
+ // Error messages etc
+ const [loginInfo, setLoginInfo] = useState("");
+
+ const handleValidateForm = (username, password) => {
+ if (loginWithSSO) {
+ return username.length > 1
+ }
+
+ if (!isCloud) {
+ return (username.length > 0 && password.length > 0);
+ }
+
+ return (username.length > 1 && password.length > 8);
+ }
+
+ if (isLoggedIn === true && serverside !== true) {
+ const tmpView = new URLSearchParams(window.location.search).get("view")
+ if (tmpView !== undefined && tmpView !== null && tmpView === "pricing") {
+ window.location.pathname = "/pricing"
+ return
+ } else if (tmpView !== undefined && tmpView !== null) {
+ window.location.pathname = tmpView
+ return
+ }
+
+ window.location.pathname = "/workflows"
+ }
+
+ const checkAdmin = () => {
+ const url = globalUrl + "/api/v1/checkusers";
+ fetch(url, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
setLoginInfo(responseJson["reason"]);
} else {
-
if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) {
setSSOUrl(responseJson.sso_url);
}
- if (loginViewLoading) {
- setLoginViewLoading(false);
- checkLogin();
- stop();
-
- if (
- responseJson.reason !== undefined &&
- responseJson.reason !== null
- ) {
- setLoginInfo(responseJson.reason);
- }
- }
-
+ // Stay = 0 users
+ // Redirect = >1 user
if (responseJson.reason === "stay") {
- navigate("/adminsetup")
- }
+ setTimeout(() => {
+ navigate("/adminsetup")
+ }, 2500)
+ }
}
})
)
.catch((error) => {
- if (!loginViewLoading) {
- setLoginViewLoading(true);
- start();
- }
- });
- };
-
- const { start, stop } = useInterval({
- duration: 3000,
- startImmediate: false,
- callback: () => {
- checkAdmin();
- },
- });
-
- if (firstRequest) {
- setFirstRequest(false);
- checkAdmin();
- }
-
- const onSubmit = (e) => {
- setLoginLoading(true);
- e.preventDefault();
- setLoginInfo("");
- // FIXME - add some check here ROFL
-
- // Just use this one?
- var data = { username: username, password: password };
- if (MFAValue !== undefined && MFAValue !== null && MFAValue.length > 0) {
- data["mfa_code"] = MFAValue;
+ setTimeout(() => {
+ navigate("/adminsetup")
+ }, 2500)
+ })
}
- var baseurl = globalUrl;
- if (register) {
- var url = baseurl + "/api/v1/login";
- fetch(url, {
- mode: "cors",
- method: "POST",
- body: JSON.stringify(data),
- credentials: "include",
- crossDomain: true,
- withCredentials: true,
- headers: {
- "Content-Type": "application/json; charset=utf-8",
- },
- })
- .then((response) =>
- response.json().then((responseJson) => {
- setLoginLoading(false);
- if (responseJson["success"] === false) {
- setLoginInfo(responseJson["reason"]);
- } else {
- if (responseJson["reason"] === "MFA_REDIRECT") {
- setLoginInfo(
- "MFA required. Please enter the 6-digit code from your authenticator"
- );
- setMFAField(true);
- return;
- } else if (responseJson["reason"] === "MFA_SETUP") {
- window.location.href = `/login/${responseJson.url}/mfa-setup`;
- return;
- }
+ const onSubmit = (e) => {
+ //toast("Testing from login page")
- setLoginInfo("Successful login, rerouting");
- for (var key in responseJson["cookies"]) {
- setCookie(
- responseJson["cookies"][key].key,
- responseJson["cookies"][key].value,
- { path: "/" }
- );
- }
+ setMessage("")
+ setLoginLoading(true)
+ e.preventDefault()
- if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) {
- console.log("RUN Welcome!!")
- setTimeout(() => {
- navigate("/welcome?tab=2")
- },200)
- // window.location.pathname = ""
- return
- }
+ // Just use this one?
+ var data = { "username": username, "password": password }
+ if (MFAValue !== undefined && MFAValue !== null && MFAValue.length > 0) {
+ data["mfa_code"] = MFAValue
+ }
- const tmpView = new URLSearchParams(window.location.search).get("view")
- if (tmpView !== undefined && tmpView !== null) {
- //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}`
- const newUrl = `/${tmpView}`
- window.location.pathname = newUrl
- } else {
- window.location.pathname = "/workflows"
- }
+ localStorage.setItem("globalUrl", "")
- setIsLoggedIn(true);
- }
- })
- )
- .catch((error) => {
- setLoginLoading(false);
- setLoginInfo("Error logging in: " + error);
- });
- } else {
- url = baseurl + "/api/v1/users/register";
- fetch(url, {
- method: "POST",
- body: JSON.stringify(data),
- headers: {
- "Content-Type": "application/json",
- },
- })
- .then((response) =>
- response.json().then((responseJson) => {
- if (responseJson["success"] === false) {
- setLoginInfo(responseJson["reason"]);
- } else {
- setLoginInfo("Successful register!");
- }
- })
- )
- .catch((error) => {
- setLoginInfo("Error in from backend: ", error);
- });
- }
- };
+ var baseurl = globalUrl
+ if (register) {
+ var url = baseurl + '/api/v1/login';
- const onChangeUser = (e) => {
- setUsername(e.target.value);
- };
-
- const onChangePass = (e) => {
- setPassword(e.target.value);
- };
-
- //const onClickRegister = () => {
- // if (props.location.pathname === "/login") {
- // window.location.pathname = "/register"
- // } else {
- // window.location.pathname = "/login"
- // }
-
- // setLoginCheck(!register)
- //}
-
- //var loginChange = register ? (
Want to register? Click here.
) : (
Go back to login? Click here.
);
- var formtitle = register ?
Login
:
Register
;
- const imgsize = 100;
- const basedata = (
-
-
-
-

-
-
- {loginViewLoading ? (
-
-
- Waiting for the Shuffle database to become available. This may
- take up to a minute.
-
-
- {loginInfo === undefined ||
- loginInfo === null ||
- loginInfo.length === 0 ? null : (
-
Database Response: {loginInfo}
- )}
-
-
-
-
-
- Are you sure Shuffle is{" "}
-
- installed correctly
-
- ?
-
-
-
- 1. Make sure shuffle-database folder has correct access, and that you have a minimum of 2Gb of RAM available:{" "}
-
-
- sudo chown -R 1000:1000 shuffle-database
-
-
- 2. Disable memory swap on the host:
-
-
- sudo swapoff -a
-
-
- 3. Restart the database:
-
-
- sudo docker restart shuffle-opensearch
-
-
-
- Need help?{" "}
-
- Join the Discord!
-
-
-
- ) : (
-
- )}
-
-
- );
-
- const loadedCheck = isLoaded ?
{basedata}
:
;
-
- useEffect(() => {
- setTimeout(() => {
- if (ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0) {
- //id="sso_button"
- const ssoBtn = document.getElementById("sso_button");
- if (ssoBtn !== undefined && ssoBtn !== null) {
- //console.log("SSO BTN: ", ssoBtn)
- const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
- var tmpView = new URLSearchParams(cursearch).get("autologin");
- if (tmpView !== undefined && tmpView !== null) {
- if (tmpView === "true") {
- console.log("Tmp: ", tmpView)
- ssoBtn.click()
- }
- }
- }
+ if (loginWithSSO === true) {
+ url = baseurl + '/api/v1/login/sso'
+ setLoginInfo("Logging in with SSO. Please wait while we find a relevant org...")
}
- }, 200);
- }, [ssoUrl])
- return
{loadedCheck}
;
-};
+ fetch(url, {
+ mode: 'cors',
+ method: 'POST',
+ body: JSON.stringify(data),
+ credentials: 'include',
+ crossDomain: true,
+ withCredentials: true,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for login:O!");
+ }
-export default LoginDialog;
+ return response.json();
+ })
+ .then((responseJson) => {
+
+ setLoginLoading(false)
+
+ console.log("Resp from backend: ", responseJson)
+
+ if (responseJson["success"] === false) {
+ setLoginInfo(responseJson["reason"])
+ }
+ else {
+
+ if (responseJson["reason"] === "MFA_REDIRECT") {
+ setLoginInfo("Enter the 6-digit MFA code.")
+ setMFAField(true)
+ return
+
+ }
+ else if (responseJson["reason"] === "MFA_SETUP") {
+ window.location.href = `/login/${responseJson.url}/mfa-setup`;
+ return;
+ }
+ else if (responseJson["reason"] === "SSO_REDIRECT") {
+ //navigate(responseJson["url"])
+ window.location.href = responseJson["url"]
+ return
+
+ }
+ else if (responseJson["reason"] !== undefined && responseJson["reason"] !== null && responseJson["reason"].includes("error")) {
+ setLoginInfo(responseJson["reason"])
+ return
+ }
+
+
+ setLoginInfo("Successful login! Redirecting you in 3 seconds...")
+ for (var key in responseJson["cookies"]) {
+ setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" })
+ }
+
+ const tmpView = new URLSearchParams(window.location.search).get("view")
+ if (tmpView !== undefined && tmpView !== null) {
+ //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}`
+ // Check if slash in the url
+
+ var newUrl = `/${tmpView}`
+ if (tmpView.startsWith("/")) {
+ newUrl = `${tmpView}`
+ }
+
+ console.log("Found url: ", newUrl)
+
+ window.location.pathname = newUrl
+ return
+ }
+
+ console.log("LOGIN DATA: ", responseJson)
+ if (responseJson.tutorials !== undefined && responseJson.tutorials !== null) {
+ // Find welcome in responseJson.tutorials under key name
+ const welcome = responseJson.tutorials.find(function (element) {
+ return element.name === "welcome";
+ })
+
+ console.log("Welcome: ", welcome)
+ if (welcome === undefined || welcome === null) {
+ console.log("RUN login Welcome!!")
+ // window.location.pathname = "/welcome?tab=2"
+ // window.location = "/welcome?tab=2"
+ window.location.href = "/welcome?tab=2"
+ return
+ }
+ }
+
+ window.location.pathname = "/workflows"
+ }
+ })
+ .catch(error => {
+ setLoginInfo("Error from login API: " + error)
+ setLoginLoading(false)
+ });
+ } else {
+ url = baseurl + '/api/v1/register';
+ fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(data),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(response =>
+ response.json().then(responseJson => {
+ if (responseJson["success"] === false) {
+ setLoginInfo(responseJson["reason"])
+ } else {
+ if (responseJson["reason"] === "shuffle_account") {
+ window.location.href = "/login?message=Please+login+with+your+Shuffle+account"
+ return
+ }
+
+ //setLoginInfo("Successful register!")
+ //var newpath = "/login?message=Successfully signed up. You can now sign in."
+ //const tmpMessage = new URLSearchParams(window.location.search).get("message")
+ setLoginInfo("Successful registration! Redirecting in 3 seconds...")
+ for (var key in responseJson["cookies"]) {
+ setCookie(responseJson["cookies"][key].key, responseJson["cookies"][key].value, { path: "/" })
+ }
+
+ setTimeout(() => {
+ console.log("LOGIN DATA: ", responseJson)
+
+ const tmpView = new URLSearchParams(window.location.search).get("view")
+ if (tmpView !== undefined && tmpView !== null) {
+ //const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}`
+ const newUrl = `/${tmpView}`
+ window.location.pathname = newUrl
+ return
+ }
+
+ //if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) {
+ console.log("RUN Welcome!!")
+ //window.location.pathname = "/welcome?tab=2"
+ window.location.href = "/welcome"
+ }, 1500);
+ }
+ setLoginLoading(false)
+ }),
+ )
+ .catch(error => {
+ setLoginInfo("Error in login. Please try again, or contact support@shuffler.io if the problem persists.")
+ setLoginLoading(false)
+ });
+ }
+ }
+
+ const onChangeUser = (e) => {
+ setUsername(e.target.value)
+ }
+
+ const onChangePass = (e) => {
+ setPassword(e.target.value)
+ }
+
+ const HandleLoginWithSSO = () => {
+ setPassword("")
+ setLoginInfo("")
+ setLoginWithSSO(true)
+ }
+
+ var formtitle = register ?
Welcome Back!
:
Create your account
+ var formButton = !isCloud ? "" : register ?
Don’t have an account yet?
Register here
: <>
+
+
Already have an account?
Login here
+ >
+ //
Click here to Login
+
+ //
{formtitle}
+
+ const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)"
+
+ const buttonStyle = { borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(username, password) || loginLoading || (checkboxClicked && register) ? buttonBackground : "grey", color: "white" }
+ //