/* eslint-disable react/no-multi-comp */ 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 { ConnectingAirportsOutlined, ConstructionOutlined, DoneRounded, Done as DoneIcon } from '@mui/icons-material'; import { Checkbox, CircularProgress, TextField, Button, Paper, Typography, Tooltip, TableHead, TableRow, TableContainer, TableCell, TableBody, Table, } from '@mui/material'; const hrefStyle = { color: "#FF8444", fontSize: "14px", textDecoration: "none", display: "flex", } 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" }, marketplaceButton: { width: "100%", justifyContent: "flex-start", padding: "12px", marginBottom: "12px", backgroundColor: "#1A1A1A", border: "1px solid #494949", borderRadius: "8px", color: "white", opacity: 0.7, '&:hover': { opacity: 1, cursor: "not-allowed", }, }, 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 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: "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) => ( ))}
{/* Read more about marketplaces */} Learn more about self hosting
); }; const LoginPage = props => { const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, inregister, serverside, checkLogin, } = props; let navigate = useNavigate(); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); const [loginLoading, setLoginLoading] = useState(false); 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 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(() => { if (!isCloud) { checkAdmin() } }, []) 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" } } // 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%" : "550px", 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 { // Stay = 0 users // Redirect = >1 user if (responseJson.reason === "stay") { setTimeout(() => { navigate("/adminsetup") }, 2500) } } }) ) .catch((error) => { setTimeout(() => { navigate("/adminsetup") }, 2500) }) }; const onSubmit = (e) => { //toast("Testing from login page") setMessage("") setLoginLoading(true) e.preventDefault() // Just use this one? var data = { "username": username, "password": password } if (MFAValue !== undefined && MFAValue !== null && MFAValue.length > 0) { data["mfa_code"] = MFAValue } localStorage.setItem("globalUrl", "") var baseurl = globalUrl if (register) { var url = baseurl + '/api/v1/login'; if (loginWithSSO === true) { url = baseurl + '/api/v1/login/sso' setLoginInfo("Logging in with SSO. Please wait while we find a relevant org...") } 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!"); } 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) } //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 ?
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" } //
{formButton}
{loginInfo}
{isMobile ? null : ( <>
OR
)} ); const loadedCheck = isLoaded ?
{basedata}
:
return (
{loadedCheck}
) } export default LoginPage;