import React, { useState, useEffect, useContext } from "react";

import {getTheme} from "../theme.jsx";
import ReactGA from "react-ga4";
import Markdown from "react-markdown";
import algoliasearch from "algoliasearch/lite";
import ReactJson from "react-json-view-ssr";
//import { useAlert
import { ToastContainer, toast } from "react-toastify" 
import { makeStyles, createStyles } from "@mui/styles";
import { useParams, useNavigate, Link } from "react-router-dom";

import {
  Autocomplete,
  Box,
  Zoom,
  Card,
  CardActionArea,
  Fade,
  Tabs,
  Tab,
  CircularProgress,
  DialogActions,
  Dialog,
  DialogTitle,
  DialogContent,
  Tooltip,
  IconButton,
  Menu,
  Paper,
  Button,
  Typography,
  Divider,
  MenuItem,
  Avatar,
  TextField,
  Breadcrumbs,
  Checkbox,
  Chip,
  Select,
} from "@mui/material";

import {
  Business as BusinessIcon,
  Edit as EditIcon,
  CloudDownload as CloudDownloadIcon,
  Warning as WarningIcon,
  VerifiedUser as VerifiedUserIcon,
  Close as CloseIcon,
  LockOpen as LockOpenIcon,
  PlayArrow as PlayArrowIcon,
  GetApp as GetAppIcon,
  Apps as AppsIcon,
  Description as DescriptionIcon,
  ShowChart as ShowChartIcon,
  Person as PersonIcon,
  Polyline as PolylineIcon,
  OpenInNew as OpenInNewIcon, 
} from "@mui/icons-material";

import ForkRightIcon from '@mui/icons-material/ForkRight';

import Alert from "@mui/material/Alert";
import { Context } from "../context/ContextApi.jsx";

import {
  SearchBox,
  StaticRefinementList,
  RefinementList,
  InstantSearch,
  connectSearchBox,
  connectHits,
  Index,
} from "react-instantsearch-dom";
import AppStats from "../components/AppStats.jsx";
import ParsedAction from "../components/ParsedAction.jsx";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import { base64_decode, appCategories } from "../views/AppCreator.jsx";
import { triggers as workflowTriggers } from "../views/AngularWorkflow.jsx";
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
import AuthenticationWindow from "../components/AuthenticationWindow.jsx";
import { CodeHandler, Img, OuterLink, CopyToClipboard, } from "../views/Docs.jsx";
import { useStyles, } from "../components/ParsedAction.jsx";
import { sortByKey } from "../views/AngularWorkflow.jsx";

import { v4 as uuidv4 } from "uuid";
import aa from "search-insights";


// AppTypes:
// 0 = OpenAPI (VALID)
// 1 = Normal app (Python)
// 2 = OpenAPI (Invalid)
const searchClient = algoliasearch(
  "JNSS5CFDZZ",
  "c8f882473ff42d41158430be09ec2b4e"
)

const AppExplorer = (props) => {
  const {
    globalUrl,
    userdata,
    setUserData,
    checkLogin,
    isLoaded,
    selectedApp,
    serverside,
    isMobile,
    isLoggedIn,
    selectedDoc,
    secondApp,
  } = props;

  //const alert = useAlert();
  const classes = useStyles()
  let navigate = useNavigate()

  const { leftSideBarOpenByClick, themeMode, supportEmail } = useContext(Context);
  const theme = getTheme(themeMode);

  const surfaceColor = "#27292D";
const inputColor = theme.palette.textFieldStyle.backgroundColor;

const chipStyle = {
  marginTop: 5,
  backgroundColor: theme.palette.chipStyle.backgroundColor,
  height: 30,
  marginRight: 5,
  paddingLeft: 5,
  paddingRight: 5,
  cursor: "pointer",
  borderColor: theme.palette.chipStyle.borderColor,
  color: theme.palette.chipStyle.color,
};

const actionListStyle = {
  paddingLeft: 10,
  paddingRight: 10,
  paddingTop: 10,
  marginTop: 5,
  backgroundColor: inputColor,
  display: "flex",
  color: theme.palette.text.primary,
  maxWidth: 350,
  minWidth: 350,
  maxHeight: 54,
  overflow: "hidden",
};

const boxStyle = {
  color: theme.palette.text.primary,
  flex: "3",
  margin: 10,
  paddingLeft: 30,
  paddingRight: 30,
  paddingBottom: 30,
  paddingTop: 30,
  display: "flex",
  flexDirection: "column",
  position: "relative",
  maxHeight: 180,
  overflow: "hidden",
};

const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)";

  const params = useParams();
  //var props = JSON.parse(JSON.stringify(defaultprops))
  //props.match = {}
  //params = params

  const bodyDivStyle = {
    margin: "auto",
    maxWidth: isMobile ? "100%" : 1350,
    scrollX: "hidden",
    overflowX: "hidden",
  };

  var upload = "";
  const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"];
  const authenticationOptions = [
    "No authentication",
    "API key",
    "Bearer auth",
    "Basic auth",
  ];
  const apikeySelection = ["Header", "Query"];

  const [app, setApp] = useState({});

  const [openapi, setOpenapi] = useState({});
  const [name, setName] = useState("");
  const [appId, setAppId] = useState("");
  const [contact, setContact] = useState("");
  const [file] = useState("");
  const [fileBase64, setFileBase64] = useState("");
  const [isAppLoaded, setIsAppLoaded] = useState(false);
  const [, setDescription] = useState("");
  const [, setBaseUrl] = useState("");
  const [, setAuthenticationRequired] = useState(false);
  const [, setAuthenticationOption] = useState(authenticationOptions[0]);
  const [newWorkflowTags, setNewWorkflowTags] = React.useState([]);
  const [, setParameterName] = useState("");
  const [, setParameterLocation] = useState(
    apikeySelection.length > 0 ? apikeySelection[0] : ""
  );
  const [, setUrlPath] = useState("");
  const [urlPathQueries, setUrlPathQueries] = useState([]);
  const [, setBasedata] = React.useState({});
  const [actions, setActions] = useState([]);
  const [errorCode] = useState("");
  const [reloadUrl, setReloadUrl] = React.useState(
    serverside === true ? "" : window.location.href
  );
  const [relatedWorkflows, setRelatedWorkflows] = useState(0);
  const [relatedApps, setRelatedApps] = useState(0);
  const [appAuthentication, setAppAuthentication] = React.useState([]);
  const [authLoaded, setAuthLoaded] = useState(false);
  const baseResult = "The execution result will show up here";
  const [anchorElAvatar, setAnchorElAvatar] = React.useState(null);
  const [executionResult, setExecutionResult] = useState({
    valid: false,
    result: baseResult,
  });
  const [executing, setExecuting] = useState(false);
  const [anchorEl, setAnchorEl] = React.useState(null);
  const [creatorProfile, setCreatorProfile] = React.useState({});
  const [selectedTab, setSelectedTab] = React.useState(0);
  const defaultDocs = `\n\n## No Shuffle-specific app documentation is available yet.\n\n## Need more information about the app? [Contact us](/contact) and [Join the Community](https://discord.gg/B2CBzUm) and find others using this app.`
  const [sharingConfiguration, setSharingConfiguration] = React.useState("you");
  const [appdata, setAppData] = React.useState({});
  const [appDocumentation, setAppDocumentation] = useState(defaultDocs)
  const [secondaryApp, setSecondaryApp] = useState({});
  const [firstRequest, setFirstRequest] = useState(true);
  const [publishModalOpen, setPublishModalOpen] = React.useState(false);

  const [categories, setCategories] = useState(appCategories)
  const [newWorkflowCategories, setNewWorkflowCategories] = React.useState([]);
  const [update, setUpdate] = useState("");
  const [triggers, setTriggers] = useState([])
  const [selectedOrganization, setSelectedOrganization] = React.useState(undefined)
  const [selectedValidationAction, setSelectedValidationAction] = React.useState({})

  const [selectedMeta, setSelectedMeta] = React.useState({
    link: "https://github.com/Shuffle/openapi-apps/new/master/docs",
    read_time: 1,
  })

  const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");


  // FIXME: This is used, as useEffect() creates an issue with apps not loading at all
  var to_be_copied = "";

  // 0 = VALID OpenAPI, 1 = Python, 2 = INVALID OpenAPI
  const [appType, setAppType] = React.useState(0);

  function handleClick(event) {
    setAnchorEl(event.currentTarget);
  }

  function handleClose() {
    setAnchorEl(null);
  }


  const loadOrganization = (orgId) => {
	fetch(`${globalUrl}/api/v1/orgs/${orgId}`, {
      method: "GET",
      credentials: "include",
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then((response) => {
        if (response.status === 401) {
        } else {
		}

        return response.json();
      })
      .then((responseJson) => {
		  if (responseJson.success === false) {

		  } else {
		  	setSelectedOrganization(responseJson)
		  }
	  })
	  .catch((error) => {
		  console.log("Error in fetching organization: ", error)
	  })
  }


 useEffect(() => {
    if (selectedApp !== undefined && selectedApp !== null && Object.getOwnPropertyNames(selectedApp).length > 0) {
      //console.log("Firstrequest!!!")
    } else {
      if (serverside) {
        console.log("Not getting app because serverside.");
      } else {
        if (params.appid.length === 32 || params.appid.length === 36) {
          handleEditApp(params.appid);
          runAlgoliaAppSearch(params.appid, false, true);
        } else {
          runAlgoliaAppSearch(params.appid);

          //handleEditApp()
        }
      }
      //parseIncomingOpenapiData(YAML.parse(data))
    }

    if (serverside !== true) {
      const urlSearchParams = new URLSearchParams(window.location.search);
      const queries = Object.fromEntries(urlSearchParams.entries());
      const foundTab = queries["tab"];
      console.log("PROPS: ", queries);

      if (params.integrationid !== undefined) {
        console.log(
          "Should search for connection integration with ",
          params.integrationid
        );
        setSelectedTab(3);

        runAlgoliaAppSearch(params.integrationid, false);
      } else if (foundTab !== null && foundTab !== undefined) {
        if (foundTab === "stats") {
          setSelectedTab(2);
        } else if (foundTab === "run") {
          setSelectedTab(1);
        } else if (foundTab === "docs" || foundTab === "documentation") {
          setSelectedTab(0);
        }
      } else {
        //setSelectedTab(1);
      }

    }
  }, []);

  if (serverside === false && firstRequest && isLoggedIn === true && selectedOrganization === undefined && userdata !== undefined && userdata.active_org !== undefined && userdata.active_org !== null && userdata.active_org.id !== undefined && userdata.active_org.id !== null) {
  	  loadOrganization(userdata.active_org.id) 
  }

  var activateButton = (
    <Link to={`/apps/new?id=${appId}`} style={{ textDecoration: "none" }}>
      <Button
        variant="contained"
        component="label"
        color="primary"
        onClick={() => {
          ReactGA.event({
            category: "Appexplorer",
            action: "app_build",
            label: params.appid,
          });
          toast("INTERESTED!");
        }}
        style={{
          padding: 15,
          marginTop: 15,
          borderRadius: 25,
          height: 50,
          margin: "15px 0px 15px 0px",
          fontSize: 14,
          color: "white",
          backgroundImage: buttonBackground,
          width: "200px",
        }}
      >
        I'm interested
      </Button>
    </Link>
    );

  const Heading = (props) => {
    const element = React.createElement(`h${props.level}`,{ style: { marginTop: props.level === 1 ? 20 : 50 } },props.children);
    
    const [hover, setHover] = useState(false);

    var extraInfo = "";
    if (props.level === 1) {
      extraInfo = (
        <div
          style={{
            backgroundColor: theme.palette.textFieldStyle.backgroundColor,
            padding: 15,
            borderRadius: theme.palette?.textFieldStyle.borderRadius,
            marginBottom: 30,
            display: "flex",
          }}
        >
          <div
            style={{
              flex: 3,
              display: "flex",
              vAlign: "center",
              position: "sticky",
              top: 50,
            }}
          >
            {isMobile === true ? null : (
              <Typography style={{ display: "inline", marginTop: 6 }}>
                <a
                  rel="noopener noreferrer"
                  target="_blank"
                  href={selectedMeta.link}
                  style={{ textDecoration: "none", color: "#f85a3e" }}
				  onClick={() => {
					ReactGA.event({
						category: "Appexplorer",
						action: "github_docs_edit_click",
						label: params.appid,
					});
				  }}
                >
                  <Button style={{}} variant="outlined">
                    <EditIcon /> &nbsp;&nbsp;Edit
                  </Button>
                </a>
              </Typography>
            )}
            {isMobile === true ? null : (
              <div
                style={{
                  height: "100%",
                  width: 1,
                  backgroundColor: "white",
                  marginLeft: 50,
                  marginRight: 50,
                }}
              />
            )}
            <Typography style={{ display: "inline", marginTop: 11 }}>
              {selectedMeta.read_time} minute
              {selectedMeta.read_time === 1 ? "" : "s"} to read
            </Typography>
          </div>

          <div style={{ flex: 2 }}>
            {isMobile === true ||
            selectedMeta.contributors === undefined ||
            selectedMeta.contributors === null ? (
              ""
            ) : (
              <div style={{ margin: 10, height: "100%", display: "inline" }}>
                {selectedMeta.contributors.slice(0, 7).map((data, index) => {
                  return (
                    <a
                      key={index}
                      rel="noopener noreferrer"
                      target="_blank"
                      href={data.url}
                      target="_blank"
                      style={{ textDecoration: "none", color: "#f85a3e" }}
                    >
                      <Tooltip title={data.url} placement="bottom">
                        <img
                          alt={data.url}
                          src={data.image}
                          style={{
                            marginTop: 5,
                            marginRight: 10,
                            height: 40,
                            borderRadius: 40,
                          }}
                        />
                      </Tooltip>
                    </a>
                  );
                })}
              </div>
            )}
          </div>
        </div>
      );
    }

		// Still inside Heading
		
		// Find appCategory in appCategories
		const appCategory = newWorkflowCategories.length === 0 ? "" : (newWorkflowCategories[0]).toLowerCase();
		const foundCategory = appCategories.find((category) => {
			if (category.name.toLowerCase() === appCategory) {
				return category
			}
		})
		const bgColor = foundCategory === undefined ? "" : foundCategory.color

		const getAllLabels = () => {
			const actionLabels = actions.filter((input_action) => {
				return input_action.action_label !== undefined && input_action.action_label !== null && input_action.action_label !== "No Label"
			})

			var labels = []
			actionLabels.forEach((action) => {
				labels.push(action.action_label)
			})

			return labels
		}

		const allLabels = getAllLabels()

		const findRelevantAction = (action_label) => {
			const foundAction = actions.find((input_action) => {
				if (input_action.action_label.toLowerCase() === action_label.toLowerCase()) {
					return input_action
				}
			})

			if (foundAction !== undefined) {
				setCurrentAction(foundAction)
				setCurrentActionMethod(foundAction.method);
				setSelectedTab(1);
			} else {
				console.log("Could not find action with label: ", action_label)
			}
		}

		// Parses out extra category info and such for the app
		const extraAppInfo = props.level === 1 ? 
			<div style={{marginBottom: 15, }}>
				{/*
				<div style={{display: "flex" }}>
					<Typography style={{width: 100, }}>
						Category: 
					</Typography>
					<Chip
						style={{
							backgroundColor: bgColor,
							color: "white",
							borderRadius: 5,
							minWidth: 80,
							marginRight: 10,
							marginTop: 2,
							cursor: "pointer",
							fontSize: 14,
						}}
						label={appCategory}
					/>
				</div>
				*/}
				{triggers.length === 0 ? null :
					<div style={{display: "flex", marginTop: 10,  }}>
						{triggers.map((trigger, index) => {

							return (
								<Chip
									key={index}
									onClick={() => {
										console.log("Clicked: ", trigger.name)
									}}
									style={{
										cursor: "pointer", 
                    backgroundColor: theme.palette.chipStyle.backgroundColor,
                    borderColor: theme.palette.chipStyle.borderColor,
										color: theme.palette.chipStyle.color,
										borderRadius: 40,
										minWidth: 80,
										marginRight: 10,
										marginTop: 2,
										fontSize: 14,
									}}
									avatar={<Avatar alt={trigger.name} src={trigger.large_image} />}
									label={trigger.name}
								/>
							)
						})}
					</div>
				}

				{serverside === false && foundCategory !== undefined && foundCategory !== null && foundCategory.action_labels.length > 0 ?
					<div style={{display: "flex", marginTop: 10,  }}>
						{foundCategory.action_labels.slice(0,5).map((action_label, index) => {
							const included = allLabels.includes(action_label)
              const iconInfo = GetIconInfo({ name: action_label });
              const useIcon = iconInfo.originalIcon;

							return (
								<Chip
									key={index}
									onClick={() => {
										findRelevantAction(action_label)
									}}
									disabled={included === false}
									style={{
										cursor: included ? "pointer" : "default",
										color: theme.palette.chipStyle.color,
                    borderColor: theme.palette.chipStyle.borderColor,
                    backgroundColor: theme.palette.chipStyle.backgroundColor,
										borderRadius: 40,
										minWidth: 80,
										marginRight: 10,
										marginTop: 2,
										fontSize: 14,
										textDecoration: included ? "none" : "line-through",
									}}
									avatar={useIcon}
									label={action_label}
								/>
							)
						})}
					</div>
				: null}
			</div>
			: null

    return (
      <Typography
        onMouseOver={() => {
          setHover(true);
        }}
      >
        {props.level !== 1 ? (
          <Divider
            style={{
              width: "90%",
              marginTop: 40,
              backgroundColor: theme.palette.inputColor,
            }}
          />
        ) : null}
        {element}
		{extraAppInfo}
        {extraInfo}
      </Typography>
    );
  };

  const [, setCurrentActionMethod] = useState(actionNonBodyRequest[0]);

  // Selectedaction = Shuffle style action
  // Currentaction = OpenAPI style
  const [selectedAction, setSelectedAction] = useState({});
  const [currentAction, setCurrentAction] = useState({
    name: "",
    description: "",
    url: "",
    headers: "",
    paths: [],
    queries: [],
    body: "",
    errors: [],
    method: actionNonBodyRequest[0],
  });

  if (params.appid === "new") {
    return null;
  }

  const WorkflowHits = ({ hits }) => {
    //console.log("WORKFLOWS: ", hits)

    setRelatedWorkflows(hits.length);
    return hits.length;
  };

  const AppHits = ({ hits }) => {
    if (hits.length >= 1) {
      setRelatedApps(hits.length - 1);

      return hits.length - 1;
    } else {
      setRelatedApps(0);
      return 0;
    }
  };

  const getUserProfile = (username) => {
    if (serverside === true) {
      return;
    }

    fetch(`${globalUrl}/api/v1/users/creators/${username}`, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      credentials: "include",
    })
      .then((response) => {
        if (response.status !== 200) {
          console.log("Status not 200 for WORKFLOW EXECUTION :O!");
        }

        return response.json();
      })
      .then((responseJson) => {
        if (responseJson.success !== false) {
          setCreatorProfile(responseJson);
        }
      })
      .catch((error) => {
        console.log(error);
      });
  };

  const SearchBox = ({ currentRefinement, refine, isSearchStalled }) => {
    return null;
  };

  const CustomWorkflowHits = connectHits(WorkflowHits);
  const CustomAppHits = connectHits(AppHits);
  const CustomSearchBox = connectSearchBox(SearchBox);

  const HandleJsonCopy = (base, copy, base_node_name) => {
    console.log("COPY: ", copy);
    var newitem = JSON.parse(base);
    to_be_copied = "$" + base_node_name;
    for (var key in copy.namespace) {
      if (copy.namespace[key].includes("Results for")) {
        continue;
      }

      if (newitem !== undefined && newitem !== null) {
        newitem = newitem[copy.namespace[key]];
        if (!isNaN(copy.namespace[key])) {
          to_be_copied += ".#";
        } else {
          to_be_copied += "." + copy.namespace[key];
        }
      }
    }
  };

  const handleReactJsonClipboard = (copy) => {
    console.log("COPY: ", copy);

    const elementName = "copy_element_shuffle";
    var copyText = document.getElementById(elementName);
    if (copyText !== null && copyText !== undefined) {
      navigator.clipboard.writeText(JSON.stringify(copy));
      copyText.select();
      copyText.setSelectionRange(0, 99999); /* For mobile devices */

      /* Copy the text inside the text field */
      document.execCommand("copy");
      toast("Copied data");
    }
  };

  const activateApp = (action) => {
    if (serverside === true) {
      return
    }

	const appExists = userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId)
	var url = appExists ? `${globalUrl}/api/v1/apps/${appId}/deactivate` : `${globalUrl}/api/v1/apps/${appId}/activate`
	if (action !== undefined && action !== null) {
		url = `${globalUrl}/api/v1/apps/${appId}/${action}`
	}
	fetch(url, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      credentials: "include",
    })
      .then((response) => {
        if (response.status !== 200) {
          console.log("Failed to activate");
        }

        return response.json();
      })
      .then((responseJson) => {
        if (responseJson.success === false) {
			if (action === undefined || action === null) {
				if (responseJson.reason !== undefined) {
					toast("Failed to activate the app: "+responseJson.reason);
				} else {
					toast("Failed to activate the app");
				}
			} else {
				if (responseJson.reason !== undefined) {
					toast("Failed to perform action: "+responseJson.reason);
				} else {
					toast(`Failed to perform action. Please try again or contact ${supportEmail}`);
				}
			}
      } else {
          if (checkLogin !== undefined && checkLogin !== null) {
            checkLogin()
          }

		  if (action === undefined || action === null) {
			  if (appExists) {
				toast("App deactivated for your organization! Existing workflows with the app will continue to work.")
			  } else {
				toast("App activated for your organization!")
			  }
		  } else {
		  }
        }
      })
      .catch((error) => {
        toast(error.toString());
      });
  };

  const handleEditApp = (appid) => {
    if (serverside === true) {
      return;
    }

		setAppId(appid)

    fetch(globalUrl + "/api/v1/apps/" + appid + "/config", {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      credentials: "include",
    })
      .then((response) => {
        if (response.status !== 200) {
          //console.log("App doesn't exist or isn't available to you.")
          //toast("Something went wrong - this app is not available to you. Redirecting you back to search.")
          ReactGA.event({
            category: "appexplorer",
            action: `app_not_found`,
            label: appid,
          });
        } else {
          ReactGA.event({
            category: "appexplorer",
            action: `app_found`,
            label: appid,
          });
        }

        return response.json();
      })
      .then((responseJson) => {
        if (
          responseJson.success === false ||
          responseJson.success === undefined
        ) {
          toast("Failed to get the app")
          setIsAppLoaded(true)
          setTimeout(() => {
          	navigate("/search")
          }, 1000);
          return;
        } else {
          parseIncomingOpenapiData(responseJson);
        }
      })
      .catch((error) => {
        toast("Error in app fetch: " + error.toString());
      });
  };

  const parseIncomingAppdata = (data, openapiExists) => {
    document.title = data.name + " App - OpenAPI and API";

	

    setExecutionResult({
      valid: false,
      result: baseResult,
    });

    setName((data.name.charAt(0).toUpperCase() + data.name.substring(1)).replaceAll("_"," "));
    
    setDescription(data.description);
    setFileBase64(data.large_image);
    setContact(data.contact_info);

	if (data.categories !== undefined && data.categories !== null) {
		setNewWorkflowCategories(data.categories);
	}
    setAppType(1);

    if (data.owner !== undefined && data.owner !== null) {
      getUserProfile(data.owner);
      //console.log("DATA: ", data)
    }

    setAppData(data)
	if (data.reference_info.triggers !== undefined && data.reference_info.triggers !== null && data.reference_info.triggers.length > 0) {
		var parsedtriggers = []
		for (var key in data.reference_info.triggers) {
			const curtrigger = data.reference_info.triggers[key]

			const foundTrigger = workflowTriggers.find((trigger) => trigger.name.toLowerCase() === curtrigger.toLowerCase())
			if (foundTrigger !== undefined && foundTrigger !== null) {
				parsedtriggers.push(foundTrigger)
			}
		}

		setTriggers(parsedtriggers)
	}

    var newactions = [];
    if (!openapiExists) {
      console.log("Skipping openapi");
      for (var key in data.actions) {
        const action = data.actions[key];
        newactions.push({
          name: action.name,
          description: action.description,
          url: "",
          headers: "",
          paths: [],
          queries: [],
          body: "",
          errors: [],
          method: "CUSTOM",
        });
      }
    }


    if (newactions.length > 0) {
      setCurrentAction(newactions[0]);

      if (data.actions !== undefined) {
        //var methodName = `${data.method}_${data.name}`.toLowerCase()
        //if (data.name.toLowerCase().startsWith(data.method.toLowerCase())) {
        //	methodName = data.name.toLowerCase()
        //}
        //var newselectedaction = data.actions.find(item => item.name.toLowerCase() === methodName)
        //if (newselectedaction === undefined || newselectedaction === null) {
        //	toast(`Name ${methodName} not found. Please contact us.`)
        //	return
        //}

        //var newselectedaction = data.actions.find(item => item.name.toLowerCase() === )
        const newselectedaction = data.actions[0];
        newselectedaction.app_id = data.id;
        newselectedaction.app_name = data.name;
        newselectedaction.app_version = data.app_version;

        newselectedaction.authentication = selectedAction.authentication;

        newselectedaction.authentication_id = selectedAction.authentication_id;
        newselectedaction.selectedAuthentication = selectedAction.selectedAuthentication;
          
        if (
          data.authentication.required &&
          newselectedaction.authentication_id !== undefined &&
          newselectedaction.authentication_id !== null &&
          newselectedaction.authentication_id.length === 0
        ) {
          const tmpParams = selectedAction.parameters;
          selectedAction.parameters = [];

          for (let paramkey in data.authentication.parameters) {
            var item = data.authentication.parameters[paramkey];
						console.log("PARAM1: ", item)
            item.configuration = true;

            const found = selectedAction.parameters.find((param) => param.name === item.name);
            
            if (found === null || found === undefined) {
              selectedAction.parameters.push(item);
            }
          }

          for (let paramkey in tmpParams) {
            var item = tmpParams[paramkey];
						console.log("PARAM2: ", item)
            //item.configuration = true
            const found = selectedAction.parameters.find((param) => param.name === item.name);
            
            if (found === null || found === undefined) {
              selectedAction.parameters.push(item);
            }
          }
        }

        setSelectedAction(newselectedaction);
      }


		const firstActions = newactions.filter(data => data.action_label !== undefined && data.action_label !== null && data.action_label !== "No Label")
		//console.log("First actions: ", firstActions)
		const secondActions = newactions.filter(data => data.action_label === undefined || data.action_label === null || data.action_label === "No Label")
		const newActions = firstActions.concat(secondActions)
      setActions(newActions);
    }

    setIsAppLoaded(true);
  };

  // Sets the data up as it should be at later points
  // This is the data FROM the database, not what's being saved
  const parseIncomingOpenapiData = (data) => {
    var appexists = false;
    var nameExists = false;
    var parsedapp = {};
    if (data.app !== undefined && data.app !== null) {
      // Should basically always be true if openapi exists too
	  var parsedBaseapp = ""
	  try { 
	  	parsedBaseapp = base64_decode(data.app)
	  } catch (e) {
	  	console.log("Failed JSON parsing: ", e)
	  	parsedBaseapp = data
	  }

	  parsedapp = JSON.parse(parsedBaseapp)
	  parsedapp.name = parsedapp.name.replaceAll("_", " ");

	  setAppDocumentation("# "+parsedapp.name+defaultDocs);
	  setApp(parsedapp);
    setSharingConfiguration(parsedapp.sharing === true ? "public" : "you")

      appexists =
        parsedapp.name !== undefined &&
        parsedapp.name !== null &&
        parsedapp.name.length !== 0;

      if (appexists) {
        getAppDocs(parsedapp.name, "python", parsedapp.app_version);
      }

      if (data.openapi === undefined || data.openapi === null) {
		console.log("Parsed app: ", parsedapp)
        parseIncomingAppdata(parsedapp, false);
      } else {
        parseIncomingAppdata(parsedapp, true);
      }
    }

    if (data.openapi === undefined || data.openapi === null) {
      return;
    }

		var parsedDecoded = ""
		try { 
			parsedDecoded = base64_decode(data.openapi)
    } catch (e) {
			console.log("Failed JSON parsing: ", e)
			parsedDecoded = data
		}

    setAppType(0);
    parsedapp = JSON.parse(parsedDecoded);
    data = parsedapp.body === undefined ? parsedapp : JSON.parse(parsedapp.body);
    setOpenapi(data);

    getAppDocs(data.info.title, "openapi", data.app_version);

    setBasedata(data);
    if (!appexists) {
      setName(
        (
          data.info.title.charAt(0).toUpperCase() + data.info.title.substring(1)
        ).replaceAll("_", " ")
      );
      setDescription(data.info.description);

      console.log("Found name: ", data.info.title);
    }

    if (serverside !== true) {
			var doctitle = "Shuffle App for " + data.info.title 
			if (!data.info.title.toLowerCase().includes("api")) {
				doctitle += " API"
			}

      document.title = doctitle
    }

    if (data.info !== null && data.info !== undefined) {
      if (data.info["x-logo"] !== undefined) {
        setFileBase64(data.info["x-logo"]);
      }

      if (data.info.contact !== undefined) {
        setContact(data.info.contact);
      }

      if (data.info["x-categories"] !== undefined && data.info["x-categories"].length > 0) {
        setNewWorkflowCategories(data.info["x-categories"]);
      }
    }

    if (data.tags !== undefined && data.tags.length > 0) {
      for (var key in data.tags) {
        newWorkflowTags.push(data.tags[key].name);
      }

      setNewWorkflowTags(newWorkflowTags);
    }

    var securitySchemes = data?.components?.securityDefinitions;
    if (securitySchemes === undefined) {
      securitySchemes = data?.securitySchemes;
    }

    if (securitySchemes === undefined) {
      securitySchemes = data?.components?.securitySchemes;
    }

    const allowedfunctions = [
      "GET",
      "CONNECT",
      "HEAD",
      "DELETE",
      "POST",
      "PATCH",
      "PUT",
    ];

    // FIXME - headers?
    var newActions = [];
    var wordlist = {};
    if (data.paths !== null && data.paths !== undefined) {
      for (let [path, pathvalue] of Object.entries(data.paths)) {
        if (path === "tmp0") {
          setAppType(2);
        }

        for (let [method, methodvalue] of Object.entries(pathvalue)) {
          if (methodvalue === null) {
            toast("Skipped method " + method);
            continue;
          }

          if (!allowedfunctions.includes(method.toUpperCase())) {
            continue;
          }

          var tmpname = methodvalue.summary;
          if (
            methodvalue.operationId !== undefined &&
            methodvalue.operationId !== null &&
            methodvalue.operationId.length > 0
          ) {
            tmpname = methodvalue.operationId;
          }

          var newaction = {
            name: tmpname,
            description: methodvalue.description,
            url: path,
            method: method.toUpperCase(),
            headers: "",
            queries: [],
            paths: [],
            body: "",
            errors: [],
            example_response: "",
			action_label: "No Label",
			required_bodyfields: [],
          }

			// Related to Label Management
			if (methodvalue["x-label"] !== undefined && methodvalue["x-label"] !== null) {
				// Check if there are commas in it then loop and find the correct one
				// Should ignore 'No Label' and 'No label'
				var correctlabel = "" 
				const labels = methodvalue["x-label"].split(",")
				for (let labelkey in labels) {
					var label = labels[labelkey].trim()
					if (label.toLowerCase() === "no label") {
						continue
					}

					// Remove quotes and escapes
					label = label.replace(/['"]+/g, '')
					label = label.replace(/\\/g, '')

					//label = label.replace("_", " ", -1)
					//label = label.charAt(0).toUpperCase() + label.slice(1)

					correctlabel = label
					break
				}

				// FIX: Map labels only if they're actually in the category list
				newaction.action_label = correctlabel
			}

			if (methodvalue["x-required-fields"] !== undefined && methodvalue["x-required-fields"] !== null) {
				newaction.required_bodyfields = methodvalue["x-required-fields"]
			}

          for (key in methodvalue.parameters) {
            const parameter = methodvalue.parameters[key];
            if (parameter.in === "query") {
              var tmpaction = {
                description: parameter.description,
                name: parameter.name,
                required: parameter.required,
                in: "query",
              };

              if (parameter.required === undefined) {
                tmpaction.required = false;
              }

              newaction.queries.push(tmpaction);
            } else if (parameter.in === "path") {
              // FIXME - parse this to the URL too
              newaction.paths.push(parameter.name);

              // FIXME: This doesn't follow OpenAPI3 exactly.
              // https://swagger.io/docs/specification/describing-request-body/
              // https://swagger.io/docs/specification/describing-parameters/
              // Need to split the data.
            } else if (parameter.in === "body") {
              // FIXME: Add tracking for components
              // E.G: https://raw.githubusercontent.com/owentl/Shuffle/master/gosecure.yaml
              if (parameter.example !== undefined) {
                newaction.body = parameter.example;
              }
            } else if (parameter.in === "header") {
              newaction.headers += `${parameter.name}=${parameter.example}\n`;
            }
          }

          if (newaction.name === "" || newaction.name === undefined) {
            // Find a unique part of the string
            // FIXME: Looks for length between /, find the one where they differ
            // Should find others with the same START to their path
            // Make a list of reserved names? Aka things that show up only once
            if (Object.getOwnPropertyNames(wordlist).length === 0) {
              for (let [newpath] of Object.entries(data.paths)) {
                const newpathsplit = newpath.split("/");
                for (key in newpathsplit) {
                  const pathitem = newpathsplit[key].toLowerCase();
                  if (wordlist[pathitem] === undefined) {
                    wordlist[pathitem] = 1;
                  } else {
                    wordlist[pathitem] += 1;
                  }
                }
              }
            }

            //console.log("WORDLIST: ", wordlist)

            // Remove underscores and make it normal with upper case etc
            const urlsplit = path.split("/");
            if (urlsplit.length > 0) {
              var curname = "";
              for (key in urlsplit) {
                var subpath = urlsplit[key];
                if (wordlist[subpath] > 2 || subpath.length < 1) {
                  continue;
                }

                curname = subpath;
                break;
              }

              // FIXME: If name exists,
              // FIXME: Check if first part of parsedname is verb, otherwise use method
              const parsedname = curname
                .split("_")
                .join(" ")
                .split("-")
                .join(" ")
                .split("{")
                .join(" ")
                .split("}")
                .join(" ")
                .trim();
              if (parsedname.length === 0) {
                newaction.errors.push("Missing name");
              } else {
                const newname =
                  method.charAt(0).toUpperCase() +
                  method.slice(1) +
                  " " +
                  parsedname;
                const searchactions = newActions.find(
                  (data) => data.name === newname
                );
                //console.log("SEARCH: ", searchactions);
                if (searchactions !== undefined) {
                  newaction.errors.push("Missing name");
                } else {
                  newaction.name = newname;
                }
              }
            } else {
              newaction.errors.push("Missing name");
            }
          }
          newActions.push(newaction);
        }

        if (data.servers !== undefined && data.servers.length > 0) {
          var firstUrl = data.servers[0].url;
          if (
            firstUrl.includes("{") &&
            firstUrl.includes("}") &&
            data.servers[0].variables !== undefined
          ) {
            const regex = /{\w+}/g;
            const found = firstUrl.match(regex);
            if (found !== null) {
              for (key in found) {
                const item = found[key].slice(1, found[key].length - 1);
                const foundVar = data.servers[0].variables[item];
                if (foundVar["default"] !== undefined) {
                  firstUrl = firstUrl.replaceAll(
                    found[key],
                    foundVar["default"]
                  );
                }
              }
            }
          }

          if (firstUrl.endsWith("/")) {
            setBaseUrl(firstUrl.slice(0, firstUrl.length - 1));
          } else {
            setBaseUrl(firstUrl);
          }
        }
      }
    }

    // FIXME: Have multiple authentication options?
    if (securitySchemes !== undefined) {
      for (const [, value] of Object.entries(securitySchemes)) {
        if (value.scheme === "bearer") {
          setAuthenticationOption("Bearer auth");
          setAuthenticationRequired(true);
          break;
        } else if (value.type === "apiKey") {
          setAuthenticationOption("API key");

          value.in = value.in.charAt(0).toUpperCase() + value.in.slice(1);
          setParameterLocation(value.in);
          if (!apikeySelection.includes(value.in)) {
            //console.log("APIKEY SELECT: ", apikeySelection)
            toast("Might be error in setting up API key authentication");
          }

          //console.log("PARAM NAME: ", value.name)
          setParameterName(value.name);
          setAuthenticationRequired(true);
          break;
        } else if (value.scheme === "basic") {
          setAuthenticationOption("Basic auth");
          setAuthenticationRequired(true);
          break;
        }
      }
    }

		const firstActions = newActions.filter(data => data.action_label !== undefined && data.action_label !== null && data.action_label !== "No Label")
		//console.log("First actions: ", firstActions)
		const secondActions = newActions.filter(data => data.action_label === undefined || data.action_label === null || data.action_label === "No Label")
		newActions = firstActions.concat(secondActions)
    setActions(newActions);
    setIsAppLoaded(true);

    if (newActions.length > 0) {
      setCurrentAction(newActions[0]);
      setCurrentActionMethod(newActions[0].method);
      setUrlPathQueries(newActions[0].queries);
      setUrlPath(newActions[0].url);
      //setActionsModalOpen(true)
    }
  };

  const getAppDocs = (appname, location, version) => {
    if (serverside === true) {
      return;
    }

    fetch(
      `${globalUrl}/api/v1/docs/${appname}?location=${location}&version=${version}`,
      {
        headers: {
          Accept: "application/json",
        },
        credentials: "include",
      }
    )
      .then((response) => {
        if (response.status === 200) {
          //toast("Successfully GOT app "+appId)
        } else {
          //toast("Failed getting app");
        }

        return response.json();
      })
      .then((responseJson) => {
				var setMeta = false
        if (responseJson.success === true) {
          if (responseJson.reason !== undefined && responseJson.reason !== undefined && responseJson.reason.length > 0) {
          
            if (!responseJson.reason.includes("404: Not Found") && responseJson.reason.length > 25) {
			  const imgRegex = /<img.*?src="(.*?)"/g;
			  const newdata = responseJson.reason.replace(imgRegex, '![]($1)');

              setAppDocumentation(newdata)

              const urlSearchParams = new URLSearchParams(
                window.location.search
              );
              const queries = Object.fromEntries(urlSearchParams.entries());
              const foundTab = queries["tab"];
              if (foundTab !== null && foundTab !== undefined) {

              } else {
                if (params.integrationid === undefined) {
                  setSelectedTab(0);
                }
              }

							if (responseJson.meta !== undefined) {
								setSelectedMeta(responseJson.meta);
								setMeta = true 
							} 
            }
          }
        } else {
        }

				if (!setMeta) {
					setSelectedMeta({
  					"name": appname,
  					"contributors": [],
  					"edited": "",
  					"read_time": 1,
						"link": `https://github.com/Shuffle/openapi-apps/new/master/docs?filename=${appname.toLowerCase().replaceAll(" ", "_").replaceAll("%20", "_")}.md`,
					})
				}
      })
      .catch((error) => {
        toast("Error in doc loading: " + error.toString());
      });
  };

  const runAlgoliaAppSearch = (appname, isOriginal, triggerOnly) => {
    const index = searchClient.initIndex("appsearch");

    console.log("Running appsearch for: ", appname);


    index
      .search(appname)
      .then(({ hits }) => {
        const appsearchname = appname.replaceAll("_", " ").toLowerCase();
		var found = false

		if (hits !== undefined && hits !== null && hits.length === 1) {
			found = true
			if (isOriginal !== false) {
				handleEditApp(hits[0].objectID)
			} else {
				setSecondaryApp(hits[0])
			}
		}

        for (var key in hits) {
          const hit = hits[key];

          if (hit["name"] === null || hit["name"] === undefined) {
            continue;
          }

          if (hit["name"].replaceAll("_", " ").toLowerCase().includes(appsearchname) || hit["objectID"] === appname) {
			  /*
			if (hit.triggers !== undefined && hit.triggers !== null && hit.triggers.length > 0) {
				var parsedtriggers = []
				for (var key in hit.triggers) {
					const curtrigger = hit.triggers[key].toLowerCase()

					const foundTrigger = workflowTriggers.find((trigger) => trigger.name.toLowerCase() === curtrigger)
					if (foundTrigger !== undefined && foundTrigger !== null) {
						parsedtriggers.push(foundTrigger)
					}
				}

				setTriggers(parsedtriggers)
			}
			*/

			if (triggerOnly === true) {

			} else {
				if (isOriginal !== false) {
					found = true
					handleEditApp(hit.objectID);
				} else {
					console.log("Found second app: ", hit);
					hit.name = hit.name.charAt(0).toUpperCase() + hit.name.slice(1);
					setSecondaryApp(hit);
				}
			}

            break;
          }
        }

	    if (!found) {
			if (hits.length > 0) {
				if (isOriginal !== false) {
					handleEditApp(hits[0].objectID)
				} else {
					setSecondaryApp(hits[0]);
				}

				return
			}

			//navigate("/search?message=App not found&q=" + appname + "&tab=apps")
			//toast("App not found. Please contact support@shuffler.io if you believe this is an error.")
			return
		}
      })
      .catch((err) => {
        console.log(err);
      });
  };


  

  if (serverside === true && firstRequest) {
    setFirstRequest(false);
    if (
      selectedApp !== undefined &&
      selectedApp !== null &&
      Object.getOwnPropertyNames(selectedApp).length > 0
    ) {
      parseIncomingOpenapiData(selectedApp);
    }

    if (
      selectedDoc !== undefined &&
      selectedDoc !== null &&
      Object.getOwnPropertyNames(selectedDoc).length > 0
    ) {
      setAppDocumentation(selectedDoc.reason);
      setSelectedTab(0);
    }

    if (
      secondApp !== undefined &&
      secondApp !== null &&
      Object.getOwnPropertyNames(secondApp).length > 0
    ) {
      setSelectedTab(3);
      setSecondaryApp(secondApp);
    }
  }

  //, [])

  if (serverside !== true && window.location.href !== reloadUrl) {
    setReloadUrl(window.location.href);
    setAppDocumentation(defaultDocs);
	setTriggers([])
    //handleEditApp(params.appid)

    if (params.appid.length === 32 || params.appid.length === 36) {
      handleEditApp(params.appid);
			runAlgoliaAppSearch(params.appid, false, true);
    } else {
      runAlgoliaAppSearch(params.appid);
    }
  }

  const loopQueries =
    urlPathQueries === undefined ||
    urlPathQueries === null ||
    urlPathQueries.length === 0 ? null : (
      <div>
        <Divider
          style={{
            marginBottom: "10px",
            marginTop: "30px",
            height: "1px",
            width: "100%",
            backgroundColor: "grey",
          }}
        />
        Queries
        {urlPathQueries.map((data, index) => {
          return (
            <Paper key={index} style={actionListStyle}>
              <div style={{ marginLeft: "5px", width: "100%" }}>
                <TextField
                  required
                  fullWidth={true}
                  defaultValue={data.name}
                  placeholder={"Query name"}
                  onBlur={(e) => {
                    //urlPathQueries[index].name = e.target.value
                    //setUrlPathQueries(urlPathQueries)
                  }}
                  InputProps={{
                    style: {
                      color: "white",
                    },
                  }}
                />
              </div>
            </Paper>
          );
        })}
        <Divider
          style={{
            marginBottom: "10px",
            marginTop: "30px",
            height: "1px",
            width: "100%",
            backgroundColor: "grey",
          }}
        />
      </div>
    );

  const executeSingleAction = (appid, thisaction) => {
    if (serverside === true) {
      return;
    }

	if (isCloud) {
		thisaction.environment = "Cloud"
	} else {
		thisaction.environment = "Shuffle"
	}

    setExecutionResult({
      valid: false,
      result: baseResult,
    });

    setExecuting(true);

    fetch(globalUrl + "/api/v1/apps/" + appid + "/run", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify(thisaction),
      credentials: "include",
    })
      .then((response) => {
        if (response.status !== 200) {
          console.log("Status not 200 for stream results :O!");
        }

        return response.json();
      })
      .then((responseJson) => {
        //console.log("RESPONSE: ", responseJson)
        if (
          responseJson.success === true &&
          responseJson.result !== null &&
          responseJson.result !== undefined &&
          responseJson.result.length > 0
        ) {
          const result = responseJson.result.slice(0, 50) + "...";
          //toast("SUCCESS: "+result)

          const validate = validateJson(responseJson.result);
          setExecutionResult(validate);
        } else if (
          responseJson.success === false &&
          responseJson.reason !== undefined &&
          responseJson.reason !== null
        ) {
          toast(responseJson.reason);
          setExecutionResult({ valid: false, result: responseJson.reason });
        } else if (responseJson.success === true) {
          setExecutionResult({
            valid: false,
            result:
              "Couldn't finish execution. Please fill all the required fields, and retry the execution.",
          });
        } else {
          setExecutionResult({
            valid: false,
            result:
              "Couldn't finish execution OR no result was returned (2). Please fill all the required fields, and validate the execution.",
          });
        }

        setExecuting(false);
      })
      .catch((error) => {
        toast("Execution error: " + error.toString());
        setExecuting(false);
      });
  };

  const MethodWrapper = (props) => {
    const { data } = props;

    var bgColor = "#61afee";
    if (data.method === "POST") {
      bgColor = "#49cc90";
    } else if (data.method === "PUT") {
      bgColor = "#fca130";
    } else if (data.method === "PATCH") {
      bgColor = "#50e3c2";
    } else if (data.method === "DELETE") {
      bgColor = "#f93e3e";
    } else if (data.method === "HEAD") {
      bgColor = "#9012fe";
    }

    return (
      <Chip
        style={{
          backgroundColor: bgColor,
          color: "white",
          borderRadius: theme.palette?.borderRadius,
          minWidth: 80,
          marginRight: 10,
          marginTop: 2,
          cursor: "pointer",
          fontSize: 14,
        }}
        label={data.method}
      />
    );
  };

  const parseName = (name, length) => {
		if (name === undefined || name === null) {
			return ""
		}

    var parsedName = name.charAt(0).toUpperCase() + name.slice(1);
    parsedName = parsedName.replaceAll("_", " ");
    if (
      length !== undefined &&
      length !== null &&
      length > 3 &&
      length < parsedName.length
    ) {
      parsedName = parsedName.slice(0, length) + "..";
    }

    return parsedName;
  };

	const SubAction = (props) => {
		const { data, selected, hovered, index } = props;

    const [hoveredItem, setHoveredItem] = useState(true);

		var urlPath = data.url !== undefined && data.url !== null && data.url.length > 0 ? data.url : ""
		var wrappedStyle = JSON.parse(JSON.stringify(actionListStyle));
		wrappedStyle.backgroundColor = selected || hovered ? theme.palette.platformColor : theme.palette.inputColor;
		wrappedStyle.paddingBottom = urlPath.length > 0 ? 0 : 10
		wrappedStyle.border = selected || hovered ? "1px solid rgba(255,255,255,0.3)" : ""

		var methodName = `${data.method}_${data.name}`;
		if ((data.name !== undefined && data.name !== null && data.method !== undefined && data.method !== null ) && (data.method.toLowerCase() === "custom" || data.name.toLowerCase().startsWith(data.method.toLowerCase()))) {
			methodName = data.name;
		}

		const invalid_keys = [".", "(", ")", "'", ",", "[", "]"];
		methodName = methodName.toLowerCase().replaceAll(" ", "_").replaceAll("-", "_");

		for (var key in invalid_keys) {
			methodName = methodName.replaceAll(invalid_keys[key], "");
		}

		const parsedName = parseName(data.name, 35);
		if (parsedName.length === 0) {
			return null
		}

		const actionLabels = foundCategory !== undefined && foundCategory !== null && foundCategory.name !== "Other" && foundCategory.action_labels.length > 0 ? ["No Label"].concat(foundCategory.action_labels) : []

		var bgColor = "#61afee";
		if (data.method === "POST") {
			bgColor = "#49cc90";
		} else if (data.method === "PUT") {
			bgColor = "#fca130";
		} else if (data.method === "PATCH") {
			bgColor = "#50e3c2";
		} else if (data.method === "DELETE") {
			bgColor = "#f93e3e";
		} else if (data.method === "HEAD") {
			bgColor = "#9012fe";
		}

		//console.log("Category: ", foundCategory, actionLabels)

		return (
			<Paper
				style={wrappedStyle}
				onMouseOver={() => {
					setHoveredItem(true);
				}}
				onMouseOut={() => {
					setHoveredItem(false);
				}}
			>
				<Tooltip
					title={data.description === null || data.description === undefined ? urlPath : <Typography variant="body2"> {parsedName} <br/><br/>{data.method} {urlPath}<br/><br />{data.description}
					</Typography>
					}
					placement="left"
				>
					<div
						style={{
							marginLeft: 10,
							width: "100%",
							cursor: "pointer",
							maxWidth: 725,
							overflowX: "hidden",
							overflowY: "hidden",
						}}
						onClick={() => {
							if (app.actions !== undefined) {
								var newselectedaction = app.actions.find((item) => item.name.toLowerCase().replaceAll(" ", "_").replaceAll(".", "").replaceAll("(", "").replaceAll(")", "") === methodName)
								
								if (newselectedaction === undefined || newselectedaction === null) {
									newselectedaction = app.actions.find((item) => item.name.toLowerCase().replaceAll(" ", "_").replaceAll(".", "").replaceAll("(", "").replaceAll(")", "") === data.name.toLowerCase().replaceAll(" ", "_").replaceAll(".", "").replaceAll("(", "").replaceAll(")", ""))
									if (newselectedaction === undefined || newselectedaction === null) {
										for (var key in app.actions) {
											console.log(methodName, app.actions[key].name.toLowerCase().replaceAll(" ", "_"));
										}

										toast(`Name ${methodName} not found. Please contact us.`);
										return;
									}
								}

								newselectedaction.app_id = app.id;
								newselectedaction.app_name = app.name;
								newselectedaction.app_version = app.app_version;

								newselectedaction.authentication = selectedAction.authentication;
								newselectedaction.authentication_id =  selectedAction.authentication_id;
								newselectedaction.selectedAuthentication = selectedAction.selectedAuthentication;
									
								if (
									app.authentication.required &&
									newselectedaction.authentication_id !== undefined &&
									newselectedaction.authentication_id !== null &&
									newselectedaction.authentication_id.length === 0
								) {
									const tmpParams = selectedAction.parameters;
									selectedAction.parameters = [];

									for (var paramkey in app.authentication.parameters) {
										var item = app.authentication.parameters[paramkey];
										item.configuration = true;

										const found = selectedAction.parameters.find(
											(param) => param.name === item.name
										);
										if (found === null || found === undefined) {
											selectedAction.parameters.push(item);
										}
									}

									for (var paramkey in tmpParams) {
										var item = tmpParams[paramkey];
										//item.configuration = true

										const found = selectedAction.parameters.find(
											(param) => param.name === item.name
										);
										if (found === null || found === undefined) {
											selectedAction.parameters.push(item);
										}
									}
								}

								setSelectedAction(newselectedaction);
							}

							setCurrentAction(data);
							setCurrentActionMethod(data.method);
							setUrlPathQueries(data.queries);
							setUrlPath(data.url);

							/*
							if (selectedTab !== 1) {
								setSelectedTab(1);
							}
							*/
						}}
					>
						<div style={{ display: "flex", marginBottom: 5 }}>
							<MethodWrapper data={data} />
							<span style={{maxWidth: 175, minWidth: 175, overflow: "hidden", marginRight: 5, }}>
								<Typography
									variant="body1"
									style={{
										marginTop: urlPath.length > 0 ? "auto" : 3,
										marginBottom: "auto",
										textAlign: "left",
										overflow: "hidden",
										maxHeight: 27,
										maxWidth: 175,
									}}
								>
									{parsedName}
								</Typography>
								{urlPath.length > 0 ? 
									<Typography variant="body2" color="textSecondary"> 
										{urlPath}
									</Typography>
								: null}
							</span>
							{actionLabels.length > 0 && newWorkflowCategories !== undefined && newWorkflowCategories !== null && newWorkflowCategories.length > 0 && categories.length > 0 ? 
								<Select
									fullWidth
									onChange={(e) => {
										console.log("Should change: ", e.target.value, " Index: ", index)

										const foundIndex = actions.findIndex((action) => action.name === data.name)
										console.log("Found index: ", foundIndex)
										if (foundIndex !== undefined && foundIndex !== null && foundIndex >= 0) {
											actions[foundIndex].action_label = e.target.value
											setActions(actions)
											setUpdate(Math.random())

											// Should send recommendations to the owner
												
											if (creatorProfile.github_username !== undefined && creatorProfile.github_username !== null && creatorProfile.github_username.length > 0) {
												const labelData = {
													"app_id": app.id,
													"action_name": data.name,
													"label": e.target.value
												}

												// Should send recommendations to the owner
												var url = `${globalUrl}/api/v1/apps/label`;
												fetch(url, {
													method: "POST",
													headers: {
													  "Content-Type": "application/json",
													  "Accept": "application/json",
													},
													body: JSON.stringify(labelData),
													credentials: "include",
												})
													.then((response) => response.json())
													.then((responseJson) => {
														if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
															toast(responseJson.reason)
														}
													})
													.catch((error) => {
														console.log("Error: ", error)
													});

											}
										}
									}}
									value={data.action_label.toLowerCase().replaceAll(" ", "_")}
									style={{
										border: data.action_label === undefined || data.action_label === "No Label" ? "" : `2px solid ${bgColor}`,
										borderRadius: theme.shape.borderRadius,
										backgroundColor: inputColor,
										paddingLeft: 10,
										color: "white",
										height: 30,
										maxWidth: 35, 
										marginLeft: 10, 
										marginRight: 10, 
										overflow: "hidden",
									}}
									inputProps={{
										name: "Method",
										id: "method-option",
									}}
								>
									{actionLabels.map((label, labelindex) => {
										//label = label.toLowerCase().replaceAll(" ", "_")
										if (label === undefined || label === null) {
											return null
										}

										return (
											<MenuItem
												key={labelindex}
												value={label.toLowerCase().replaceAll(" ", "_")}
												style={{ 
												}}
											>
												{label}
											</MenuItem>
										)
									})}
									<Divider />
									<MenuItem
										key={"app_validation"}
										value={"app_validation"}
										style={{ 
										}}
									>
										App Validation
									</MenuItem>
								</Select>
							: null}
						</div>
					</div>
				</Tooltip>
			</Paper>
		);
	};

	const foundCategory = newWorkflowCategories !== undefined && newWorkflowCategories !== null && newWorkflowCategories.length > 0 ? categories.find((x) => x.name === newWorkflowCategories[0]) : undefined
  const LoopActions = (props) => {
    const { actions } = props;

    //const [activeActions] = useState(actions === undefined ? [] : actions);

    if (actions === undefined || actions === null || actions.length === 0) {
      return null;
    }

    return (
      <div>
        {actions.map((data, index) => {
					if (data.action_label === undefined || data.action_label === null || data.action_label.length === 0) {
						data.action_label = "No Label"
					}

          return (
            <SubAction
              key={index}
              data={data}
              selected={currentAction.name === data.name}
              index={index}
            />
          );
        })}
      </div>
    );
    //</Scrollbars>
  };

  const ParsedActionHandler = () => {
    const passedOrg = { id: "", name: "" };
    const owner = "";
    const passedTags = ["single test"];

    const [, setUpdate] = useState();
    const [authenticationModalOpen, setAuthenticationModalOpen] = useState(false);
    const [selectedApp, setSelectedApp] = useState({
      versions: [
        {
          id: selectedAction.app_id,
          version: selectedAction.app_version,
        },
      ],
      loop_versions: [selectedAction.app_version],
      id: selectedAction.app_id,
      name: selectedAction.app_name,
      version: selectedAction.app_version,
    });

    const [requiresAuthentication, setRequiresAuthentication] = useState(
      app.authentication.required &&
        app.authentication.parameters !== undefined &&
        app.authentication.parameters !== null
    );
    const [workflow, setWorkflow] = useState({
      name: "",
      description: "",
      actions: [selectedAction],
      start: selectedAction.id,
      tags: passedTags,
      execution_org: passedOrg,
      org_id: passedOrg.id,
      id: uuidv4(),
      isValid: true,
      owner: owner,
      created: Date.now(),
    });

    const EndpointData = () => {
      const [tmpVar, setTmpVar] = React.useState("");

      return (
        <div>
          The API endpoint to use (URL) - predefined in the app
          <TextField
            style={{
              backgroundColor: inputColor,
              borderRadius: theme.palette?.borderRadius,
            }}
            InputProps={{
              style: {
                color: "white",
                height: 50,
                fontSize: "1em",
              },
            }}
            fullWidth
            type="text"
            color="primary"
            disabled={true}
            placeholder="Bearer token"
            defaultValue={selectedApp.link}
            onChange={(event) => {
              setTmpVar(event.target.value);
            }}
            onBlur={() => {
              selectedApp.link = tmpVar;
              console.log("LINK: ", selectedApp.link);
              setSelectedApp(selectedApp);
            }}
          />
        </div>
      );
    };

    const setAppActionAuthentication = (newauth) => {
      if (app.authentication.required) {
        var findAuthId = "";
        if (
          selectedAction.authentication_id !== null &&
          selectedAction.authentication_id !== undefined &&
          selectedAction.authentication_id.length > 0
        ) {
          findAuthId = selectedAction.authentication_id;
        }

        var baseAuthOptions = [];
        for (var key in newauth) {
          var item = newauth[key];

          const newfields = {};
          for (var filterkey in item.fields) {
            newfields[item.fields[filterkey].key] = item.fields[filterkey].value;
          }

          item.fields = newfields;
          if (item.app.name === app.name) {
            baseAuthOptions.push(item);

            if (item.id === findAuthId) {
              selectedAction.selectedAuthentication = item;
            }
          }
        }

        selectedAction.authentication = baseAuthOptions;
        //console.log("Authentication: ", authenticationOptions)
        if (
          selectedAction.selectedAuthentication === null ||
          selectedAction.selectedAuthentication === undefined ||
          selectedAction.selectedAuthentication.length === ""
        ) {
          selectedAction.selectedAuthentication = {};
        }
      } else {
        selectedAction.authentication = [];
        selectedAction.authentication_id = "";
        selectedAction.selectedAuthentication = {};
      }

      setSelectedAction(selectedAction);
      console.log("Action: ", selectedAction);
    };

    //{selectedAction.authentication !== undefined && selectedAction.authentication.length > 0 ?
    const getAppAuthentication = () => {
      if (serverside === true) {
        return;
      }

      fetch(globalUrl + "/api/v1/apps/authentication", {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        credentials: "include",
      })
        .then((response) => {
          if (response.status !== 200) {
            console.log("Status not 200 for app auth :O!");
          }

          return response.json();
        })
        .then((responseJson) => {
          if (responseJson.success && responseJson.data !== undefined && responseJson.data !== null && responseJson.data.length !== 0) {
            var newauth = [];

			//console.log("Got auth. Trying to map to selectedAction appname: ", selectedAction)
			var authUpdate = false;
			const appname = selectedAction.app_name.toLowerCase().replaceAll(" ", "_")
			selectedAction.authentication = []

            for (var key in responseJson.data) {
              if (responseJson.data[key].defined === false) {
                continue;
              }

              if (responseJson.data[key].active === false) {
                continue;
              }

              newauth.push(responseJson.data[key]);

				if (responseJson.data[key].app === undefined || responseJson.data[key].app === null) {
					continue
				}

				if (responseJson.data[key].app.name.toLowerCase().replaceAll(" ", "_") === appname) {
					console.log("Found matching app name: ", responseJson.data[key].app.name)
					selectedAction.authentication.push(responseJson.data[key])
					selectedAction.authentication_id = responseJson.data[key].id
					selectedAction.selectedAuthentication = responseJson.data[key]
					authUpdate = true;
				}
}

			console.log("New auth: ", newauth)
			if (authUpdate === true) {
				setSelectedAction(selectedAction)
			}

						//setUpdate(Math.random())
            setAppAuthentication(newauth);
            setAppActionAuthentication(newauth);
          } else {
            if (app.authentication.required) {
              const tmpParams = selectedAction.parameters;
              selectedAction.parameters = [];

              for (var paramkey in app.authentication.parameters) {
                var item = app.authentication.parameters[paramkey];
                item.configuration = true;

                const found = selectedAction.parameters.find(
                  (param) => param.name === item.name
                );
                if (found === null || found === undefined) {
                  selectedAction.parameters.push(item);
                }
              }

              for (var paramkey in tmpParams) {
                var item = tmpParams[paramkey];
                //item.configuration = true

                const found = selectedAction.parameters.find((param) => param.name === item.name);
                
                if (found === null || found === undefined) {
                  selectedAction.parameters.push(item);
                }
              }

              setSelectedAction(selectedAction);
            }

            //toast("Failed getting authentications")
          }
        })
        .catch((error) => {
          toast("Auth loading error: " + error.toString());
        });
    };

    if (!authLoaded && appAuthentication.length === 0 && selectedAction.id !== undefined) {
      setAuthLoaded(true);
      getAppAuthentication();
    } else if (
      selectedAction.id === undefined &&
      currentAction.name !== undefined &&
      currentAction.name !== null &&
      currentAction.name.length > 0
    ) {
      var methodName = `${currentAction.method}_${currentAction.name}`;
      if (
        currentAction.method.toLowerCase() === "custom" ||
        currentAction.name
          .toLowerCase()
          .startsWith(currentAction.method.toLowerCase())
      ) {
        methodName = currentAction.name;
      }

      methodName = methodName.toLowerCase().replaceAll(" ", "_");
      if (app.actions !== null && app.actions !== undefined) {
        var newselectedaction = app.actions.find(
          (item) => item.name.toLowerCase().replaceAll(" ", "_") === methodName
        );
        if (newselectedaction !== undefined && newselectedaction !== null) {
          newselectedaction.app_id = app.id;
          newselectedaction.app_name = app.name;
          newselectedaction.app_version = app.app_version;
          newselectedaction.authentication = [];
          newselectedaction.authentication_id = "";
          newselectedaction.selectedAuthentication = {};
          setSelectedAction(newselectedaction);
        }
      }
    }

    const setNewAppAuth = (appAuthData) => {
      if (serverside === true) {
        return;
      }

      //console.log("DAta: ", appAuthData)
      fetch(globalUrl + "/api/v1/apps/authentication", {
        method: "PUT",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: JSON.stringify(appAuthData),
        credentials: "include",
      })
        .then((response) => {
          if (response.status !== 200) {
            console.log("Status not 200 for setting app auth :O!");
          }

          return response.json();
        })
        .then((responseJson) => {
          if (!responseJson.success) {
            toast("Failed to set app auth: " + responseJson.reason);
          } else {
            getAppAuthentication(true);
            setAuthenticationModalOpen(false);

            // Needs a refresh with the new authentication..
            //toast("Successfully saved new app auth")
          }
        })
        .catch((error) => {
          toast("Auth error: ", error.toString());
        });
    };

    const AuthenticationData = (props) => {
      const selectedApp = props.app;

      const [authenticationOption, setAuthenticationOptions] = React.useState({
        app: JSON.parse(JSON.stringify(selectedApp)),
        fields: {},
        label: "",
        usage: [
          {
            workflow_id: workflow.id,
          },
        ],
        id: uuidv4(),
        active: true,
      });

      if (selectedApp.authentication === undefined) {
        return null;
      }

      if (
        selectedApp.authentication.parameters === null ||
        selectedApp.authentication.parameters === undefined ||
        selectedApp.authentication.parameters.length === 0
      ) {
        return null;
      }

      authenticationOption.app.actions = [];

      for (var key in selectedApp.authentication.parameters) {
        if (
          authenticationOption.fields[
            selectedApp.authentication.parameters[key].name
          ] === undefined
        ) {
          authenticationOption.fields[
            selectedApp.authentication.parameters[key].name
          ] = "";
        }
      }

      const handleSubmitCheck = () => {
        if (authenticationOption.label.length === 0) {
          authenticationOption.label = `Auth for ${selectedApp.name}`;
          //toast("Label can't be empty")
          //return
        }

        for (var key in selectedApp.authentication.parameters) {
          if (
            authenticationOption.fields[
              selectedApp.authentication.parameters[key].name
            ].length === 0
          ) {
            toast(
              "Field " +
                selectedApp.authentication.parameters[key].name +
                " can't be empty"
            );
            return;
          }
        }

        console.log("Action: ", selectedAction);
        selectedAction.authentication_id = authenticationOption.id;
        selectedAction.selectedAuthentication = authenticationOption;
        if (
          selectedAction.authentication === undefined ||
          selectedAction.authentication === null
        ) {
          selectedAction.authentication = [authenticationOption];
        } else {
          selectedAction.authentication.push(authenticationOption);
        }

        setSelectedAction(selectedAction);

        var newAuthOption = JSON.parse(JSON.stringify(authenticationOption));
        var newFields = [];
        for (const key in newAuthOption.fields) {
          const value = newAuthOption.fields[key];
          newFields.push({
            key: key,
            value: value,
          });
        }

        console.log("FIELDS: ", newFields);
        newAuthOption.fields = newFields;
        setNewAppAuth(newAuthOption);
        //appAuthentication.push(newAuthOption)
        //setAppAuthentication(appAuthentication)
        //

        setUpdate(authenticationOption.id);

        /*
					{selectedAction.authentication.map(data => (
					<MenuItem key={data.id} style={{backgroundColor: inputColor, color: "white"}} value={data}>
				*/
      };

      if (
        authenticationOption.label === null ||
        authenticationOption.label === undefined
      ) {
        authenticationOption.label = selectedApp.name + " authentication";
      }

      return (
        <div>
          <DialogContent>
            <a
              target="_blank"
              rel="norefferer"
              href="https://shuffler.io/docs/apps#authentication"
              style={{ textDecoration: "none", color: "#f85a3e" }}
            >
              What is this?
            </a>
            <div />
            These are required fields for authenticating with {selectedApp.name}
            <div style={{ marginTop: 15 }} />
            <b>Name - what is this used for?</b>
            <TextField
              style={{
                backgroundColor: inputColor,
                borderRadius: theme.palette?.borderRadius,
              }}
              InputProps={{
                style: {
                  color: "white",
                  marginLeft: "5px",
                  maxWidth: "95%",
                  height: 50,
                  fontSize: "1em",
                },
              }}
              fullWidth
              color="primary"
              placeholder={"Auth july 2020"}
              defaultValue={`Auth for ${selectedApp.name}`}
              onChange={(event) => {
                authenticationOption.label = event.target.value;
              }}
            />
            {selectedApp.link.length > 0 ? (
              <div style={{ marginTop: 15 }}>
                <EndpointData />
              </div>
            ) : null}
            <Divider
              style={{
                marginTop: 15,
                marginBottom: 15,
                backgroundColor: "rgb(91, 96, 100)",
              }}
            />
            <div style={{}} />
            {selectedApp.authentication.parameters !== undefined &&
            selectedApp.authentication.parameters !== null
              ? selectedApp.authentication.parameters.map((data, index) => {
                  return (
                    <div key={index} style={{ marginTop: 10 }}>
                      <LockOpenIcon style={{ marginRight: 10 }} />
                      <b>{data.name}</b>
                      <TextField
                        style={{
                          backgroundColor: inputColor,
                          borderRadius: theme.palette?.borderRadius,
                        }}
                        InputProps={{
                          style: {
                            color: "white",
                            marginLeft: "5px",
                            maxWidth: "95%",
                            height: 50,
                            fontSize: "1em",
                          },
                        }}
                        fullWidth
                        type={
                          data.example !== undefined &&
                          data.example.includes("***")
                            ? "password"
                            : "text"
                        }
                        color="primary"
                        placeholder={data.example}
                        onChange={(event) => {
                          authenticationOption.fields[data.name] =
                            event.target.value;
                        }}
                      />
                    </div>
                  );
                })
              : null}
          </DialogContent>
          <DialogActions>
            <Button
              style={{ borderRadius: "0px" }}
              onClick={() => {
                setAuthenticationModalOpen(false);
              }}
              color="primary"
            >
              Cancel
            </Button>
            <Button
              style={{ borderRadius: "0px" }}
              onClick={() => {
                handleSubmitCheck();
              }}
              color="primary"
            >
              Submit
            </Button>
          </DialogActions>
        </div>
      );
    };

    const authenticationModal = authenticationModalOpen ? (
      <Dialog
        open={authenticationModalOpen}
        onClose={() => {
          //setAuthenticationModalOpen(false)
        }}
        PaperProps={{
          style: {
            backgroundColor: surfaceColor,
            color: "white",
            minWidth: 600,
            padding: 15,
          },
        }}
      >
        <IconButton
          style={{
            zIndex: 5000,
            position: "absolute",
            top: 14,
            right: 14,
            color: "grey",
          }}
          onClick={() => {
            setAuthenticationModalOpen(false);
          }}
        >
          <CloseIcon />
        </IconButton>
        <DialogTitle>
          <div style={{ color: "white" }}>
            Authentication for {selectedApp.name}
          </div>
        </DialogTitle>

        {/*<AuthenticationData app={app} />*/}

				{app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app" ?
					<AuthenticationOauth2
						selectedApp={app}
						selectedAction={{
							"app_name": app.name,
							"app_id": app.id,
							"app_version": app.version,
							"large_image": app.large_image,
						}}
						authenticationType={app.authentication}
						isCloud={true}
						authButtonOnly={true}
						getAppAuthentication={getAppAuthentication}
    					isLoggedIn={isLoggedIn}
					/>
					:
					<AuthenticationWindow
						selectedApp={app}
						globalUrl={globalUrl}
						getAppAuthentication={getAppAuthentication}
						appAuthentication={appAuthentication}
						authenticationModalOpen={authenticationModalOpen}
						setAuthenticationModalOpen={setAuthenticationModalOpen}
    				isLoggedIn={isLoggedIn}
					/>
				}
      </Dialog>
    ) : null;

    const selectedNameChange = (event) => {
	  if (event.target === undefined || event.target === null || event.target.value === undefined || event.target.value === null || event.target.value.length === 0) {
		  return 
	  }


      //console.log("OLDNAME: ", selectedAction.name)
      event.target.value = event.target.value.replaceAll("(", "");
      event.target.value = event.target.value.replaceAll(")", "");
      event.target.value = event.target.value.replaceAll("$", "");
      event.target.value = event.target.value.replaceAll("#", "");
      event.target.value = event.target.value.replaceAll(".", "");
      event.target.value = event.target.value.replaceAll(",", "");
      event.target.value = event.target.value.replaceAll(" ", "_");
      selectedAction.label = event.target.value;
      setSelectedAction(selectedAction);
    };

    const actionStyling = {
      width: "100%",
    };

    //backgroundColor: "#1F2023",
    const appApiViewStyle = {
      display: "flex",
      flexDirection: "column",
      color: "white",
      minHeight: "100%",
      zIndex: 1000,
      resize: "vertical",
      overflowY: "auto",
      overflowX: "hidden",
      maxHeight: 680,
      paddingRight: 7,
    };
		// maxWidth: 420,

    return (
      <span>
        {authenticationModal}

        {selectedAction.id !== undefined ? (
          <ParsedAction
            selectedAction={selectedAction}
            workflow={workflow}
            setWorkflow={setWorkflow}
            setSelectedAction={setSelectedAction}
            setUpdate={setUpdate}
            selectedApp={app}
            setAuthenticationModalOpen={setAuthenticationModalOpen}
            selectedNameChange={selectedNameChange}
            appApiViewStyle={appApiViewStyle}
            globalUrl={globalUrl}
            requiresAuthentication={requiresAuthentication}
            hideExtraTypes={true}
            setNewSelectedAction={undefined}
            environments={[]}
            selectedActionEnvironment={undefined}
            setSelectedActionEnvironment={undefined}
            showEnvironment={false}
            rightsidebarStyle={actionStyling}
            setCodeModalOpen={undefined}
            setLastSaved={undefined}
            setVariablesModalOpen={undefined}
            setCurrentView={undefined}
            setSelectedTrigger={undefined}
            setSelectedEdge={undefined}
            setSelectedApp={setSelectedApp}
            getParents={undefined}
            setSelectedResult={undefined}
            cy={undefined}
            workflowExecutions={[]}
          />
        ) : null}
      </span>
    );
  };

  const SelectedActionView = (props) => {
    const { action } = props;
    //console.log("Parsedaction: ", selectedAction)
    const parsedName = parseName(action.name);
    const splitHeaders = action.headers === undefined || action.headers === null ? [] : action.headers.split("\n");

    return (
      <div style={{ minWidth: 400, maxWidth: 400, marginLeft: 10, marginRight: 20, }}>
        <div style={{ display: "flex", marginTop: 10 }}>
          <MethodWrapper data={action} />
          <Typography variant="body1" style={{ marginTop: 5 }}>
            {parsedName}
          </Typography>
        </div>

        <ParsedActionHandler />
      </div>
    );
  };


	const AppDetails = (props) => {
		const { title, inputTitle } = props

		const [details, setDetails] = useState("")


		return (
			<span>
				<TextField
					fullWidth
					margin="dense"
					id="outlined-basic"
					label={title}
					variant="outlined"
					defaultValue={inputTitle}
					style={{ 
						minWidth: 600,
						maxWidth: 600,
						marginRight: 10,
					}}
					// endornament for submit
					onChange={(event) => {
						setDetails(event.target.value)
					}}
					onBlur={(event) => {
						submitAppDetails(title.toLowerCase().replaceAll(" ", "_"), details)
					}}
				/>
			</span>
		)
	}

  const imageStyle = {
    borderRadius: theme.palette?.borderRadius,
    border: "1px solid rgba(255,255,255,0.6)",
    minWidth: 100,
    maxWidth: 100,
    minhHight: 100,
    maxHeight: 100,
  };

  const textStyle = {
    marginLeft: 15,
    marginTop: 15,
  };

	const submitAppDetails = (field, value) => {
		console.log("To submit. Skipping if value is empty: ", field, value)

		if (value === undefined || value === null || (value.length === 0 && field !== "triggers")) {
			return
		}

		//toast("Submitting details for field", field)
		const data = {
			field: field,
			value: value,
			app_id: app.id,
		}

		fetch(globalUrl + "/api/v1/apps/label", {
		  method: "POST",
		  headers: {
			"Content-Type": "application/json",
			Accept: "application/json",
		  },
		  body: JSON.stringify(data),
		  credentials: "include",
		})
		  .then((response) => {
			if (response.status !== 200) {
			  console.log("Status not 200 for stream results :O!");
			}

			return response.json();
		  })
		  .then((responseJson) => {
			  if (responseJson.success === false) {
				  if (responseJson.reason === undefined) {
					toast("Failed to submit details for field ", field, " because ", responseJson.reason)
				  } else {
					toast("Failed to submit details for field ", field, " because ", responseJson.reason)
				  }
			  } else {
				toast("Successfully submitted details for field ", field)
			  }
		  })
		  .catch((error) => {
			  console.log("Error: ", error)
			  toast("Error submitting details for field ", field)
		  })
	}

  const editTrigger = (triggerName) => {
	  console.log("Editing trigger: ", triggerName)

	  var found = false
	  for (var i = 0; i < triggers.length; i++) {
		  if (triggers[i].name === triggerName) {
			  found = true
			  break
		  }
	  }

	  var newtriggers = JSON.parse(JSON.stringify(triggers))
	  if (!found) {
		  // Find the trigger with the same name in imported workflowTriggers
		  var importedTrigger = undefined
		  for (var i = 0; i < workflowTriggers.length; i++) {
			  if (workflowTriggers[i].name.toLowerCase() === triggerName.toLowerCase()) {
				  newtriggers.push(workflowTriggers[i])
				  break
			  }
		  }

	  } else {
		  // Remove
		  newtriggers = newtriggers.filter((trigger) => trigger.name !== triggerName)
	  }

	  console.log("Triggers: ", newtriggers)
	  setTriggers(newtriggers)

	  var parsedtriggers = []
	  for (var i = 0; i < newtriggers.length; i++) {
		  parsedtriggers.push(newtriggers[i].name)
	  }

	  submitAppDetails("triggers", parsedtriggers.join(","))
  }

  	const removeAppFromSearchEngine = (appID) => { 
	  	toast(`Removing app ${appId} from search engine`)

		const field = "public"
	  	const data = {
			field: field,
			value: "false",
			app_id: appID,
		}

		fetch(globalUrl + "/api/v1/apps/label", {
		  method: "POST",
		  headers: {
			"Content-Type": "application/json",
			Accept: "application/json",
		  },
		  body: JSON.stringify(data),
		  credentials: "include",
		})
		.then((response) => {
			if (response.status !== 200) {
			  console.log("Status not 200 for stream results :O!");
			}

			return response.json();
		  })
		  .then((responseJson) => {
			  if (responseJson.success === false) {
				  if (responseJson.reason === undefined) {
					toast.error("Failed removing app from search engine.") 
				  } else {
					toast.error("Failed to remove app from search engine because: " + responseJson.reason)
				  }
			  } else {
				toast.info("Successfully unpublished app. It can still be accessed by direct link.")
			  }
		  })
		  .catch((error) => {
			  console.log("Error: ", error)
			  toast.info("Error when removing app from search engine.")
		  })
  }

  const deduplicateByName = (array) => {
    const uniqueNames = {};
    return array.filter(item => {
		if (!item?.hasOwnProperty('name') || !item?.name?.length) {
		  return true
		}
		if (!uniqueNames[item.name]) {
		  uniqueNames[item.name] = true
		  return true
		}
		return false
    })
  }

	const ActionSelectOption = (actionprops) => {
		const { option, newActionname, newActiondescription, useIcon, extraDescription, } = actionprops;
  		const [hover, setHover] = React.useState(false);

		return (
			<Tooltip
			  color="secondary"
			  title={newActiondescription}
			  placement="left"
			>
				<div style={{
					cursor: "pointer", 
					padding: 8, 
					paddingLeft: 14, 
					paddingBottom: 4,
					backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor,
				}} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
				onClick={(event) => {
					console.log("Clicked on action: ", option)


					if (option !== undefined && option !== null) { 
						setSelectedValidationAction(option)

						const labelData = {
							"app_id": app.id,
							"action_name": option.name,
							"label": "app_validation",
						}

						// Should send recommendations to the owner
						var url = `${globalUrl}/api/v1/apps/label`;
						fetch(url, {
							method: "POST",
							headers: {
							  "Content-Type": "application/json",
							  "Accept": "application/json",
							},
							body: JSON.stringify(labelData),
							credentials: "include",
						})
						.then((response) => response.json())
						.then((responseJson) => {
							if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) {
								toast(responseJson.reason)
							}
						})
						.catch((error) => {
							console.log("Error: ", error)
						})

						// Update the app itself?
						/*
                		setNewSelectedAction({ 
							target: { 
								value: option.name 
							} 
						});
						*/
              		}
				}}
				>
					<div style={{ display: "flex", marginBottom: 0,}}>
						<span
							style={{
								marginRight: 10,
								marginTop: "auto",
								marginBottom: 0,
							}}
						>
							{useIcon}
						</span>
						<span style={{marginBottom: 0, marginTop: 3, }}>{newActionname}</span>
					</div>
					{extraDescription.length > 0 ? 
						<Typography variant="body2" color="textSecondary" style={{marginTop: 0, overflow: "hidden", whiteSpace: "nowrap", display: "block",}}>
							{extraDescription}	
						</Typography>
					: null}
				</div>
			</Tooltip>
		)
	}

  const userRoles = ["you", "public"];

  const updateAppField = (app_id, fieldname, fieldvalue) => {
      const data = {};
      data[fieldname] = fieldvalue;
  
  
      console.log("DATA: ", data);
  
      fetch(globalUrl + "/api/v1/apps/" + app_id, {
        method: "PATCH",
        headers: {
          Accept: "application/json",
        },
        body: JSON.stringify(data),
        credentials: "include",
      })
        .then((response) => {
          //setAppSearchLoading(false)
          return response.json();
        })
        .then((responseJson) => {
          //console.log(responseJson)
          //toast(responseJson)
          if (responseJson.success) {
            toast("Successfully updated app configuration");
          } else {
            if (responseJson.reason !== undefined && responseJson.reason !== null) {
              toast("Error: "+responseJson.reason);
            } else {
              toast("Error updating app configuration. Are you the owner of this app?");
        }
          }
        })
        .catch((error) => {
          toast(error.toString());
        });
    };

  const sortByCategoryLabel = (a, b) => {
	  const aHasCategoryLabel = a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0
	  const bHasCategoryLabel = b.category_label !== undefined && b.category_label !== null && b.category_label.length > 0

	  // Sort by existence and length of "category_label"
	  if (aHasCategoryLabel && !bHasCategoryLabel) {
		return -1
	  } else if (!aHasCategoryLabel && bHasCategoryLabel) {
		return 1
	  } else {
		return 0
	  }
  }

  const getDownloadUrl = () => {

	  console.log("APP: ", app)

	  const appEnding = app?.public === true ? app?.app_version : app?.id

	  return `curl -L \ \\\n    "${globalUrl}/api/v1/download_docker_image?image=frikky/shuffle:${app?.name.toLowerCase().replaceAll(' ', '_')}_${appEnding}" \\\n    -H \"Authorization: Bearer APIKEY" \\\n    -o image.zip; \\\n    docker load -i image.zip`
  }

  const renderedActionOptions = deduplicateByName((
	  actions === undefined || actions === null ? [] : 
	  actions.filter((a) => 
		  a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(actions, "label"))
      ).sort(sortByCategoryLabel))

  const actionView =
    actions === undefined || actions === null ? null : (
      <div style={{ color: "white" }}>
        <div style={{ display: "flex" }}>

          {isMobile || appType === 0 || appType === 2 ? null : (
			<span style={{maxWidth: 350, minWidth: 350, marginRight: 10, marginLeft: 10, marginTop: 10, }}>
				<div
					style={{
						maxHeight: 800,
						overflowY: "auto",
						overflowX: "hidden",
					}}
				>
					<LoopActions actions={actions} />
				</div>
			</span>
          )}

          <Paper
            style={{
              flex: 6,
              margin: 10,
              padding: 30,
              backgroundColor: boxStyle.backgroundColor,
              color: theme.palette.text.primary,
              textAlign: "left",
              paddingBottom: 50,
              overflow: "hidden",

			  margin: "auto",
            }}
          >
            <Tabs
			  color="secondary"
              indicatorColor="primary"
			  textColor="secondary"
			  variant="fullWidth"
              value={selectedTab}
              onChange={(event, newValue) => {
				if (newValue === 1 && appType === 0 || appType === 2) {
					window.open(`/apis/${app.id}`, "_blank")
				} else {
                	setSelectedTab(newValue)
				}
              }}
              style={{ marginBottom: 0, marginLeft: 0, marginRight: 0, minWidth: 800, maxWidth: 800, margin: "auto", }}
              aria-label="disabled tabs example"
            >
              <Tab style={{marginLeft: 0, color: theme.palette.text.primary }} icon={<DescriptionIcon />} label="Docs" />
              <Tab icon={appType === 0 || appType === 2 ? <OpenInNewIcon /> : <AppsIcon />} label={appType === 0 || appType === 2 ? "Explore the API" : "Try it out"} />

              <Tab icon={<ShowChartIcon />} style={{color: theme.palette.text.primary}} label="Stats" />
              <Tab icon={<PolylineIcon />} disabled style={{color: theme.palette.text.secondary}} label="Integrations" />
              <Tab icon={<PersonIcon />} disabled={userdata.support !== true} style={{color: userdata.support !== true ? theme.palette.text.secondary: theme.palette.text.primary}} label="Creator" value={4}  />
            </Tabs>
            <div style={{ marginTop: 25 }}>
              {selectedTab === 1 && app.skipped_build == false ? (
                <div style={{ display: "flex", marginLeft: 25,  }}>
                  <div style={{ }}>
                    <SelectedActionView action={currentAction} />
                  </div>
                  <div style={{ marginLeft: 25, maxWidth: 350 }}>
										{currentAction.description !== undefined && currentAction.description !== null && currentAction.description !== "" ? 
											<div
                        style={{
                          maxHeight: 175,
                          overflowX: "hidden",
                          overflowY: "auto",
                          marginBottom: 25,
                        }}
                      >
                        <Typography variant="h6" style={{ flex: 10 }}>
                          Action Description
                        </Typography>
                        <Typography
                          variant="body1"
                          color="textSecondary"
                          style={{ maxWidth: 350, minWidth: 350 }}
                        >
                          {currentAction.description}
                        </Typography>
                      </div>
                    : app.description !== undefined &&
											app.description !== null &&
											app.description.length > 0 ? 
                      <div
                        style={{
                          maxHeight: 175,
                          overflowX: "hidden",
                          overflowY: "auto",
                          marginBottom: 25,
                        }}
                      >
                        <Typography variant="h6" style={{ flex: 10 }}>
                          App Description
                        </Typography>
                        <Typography
                          variant="body1"
                          color="textSecondary"
                          style={{ maxWidth: 350, minWidth: 350 }}
                        >
                          {app.description}
                        </Typography>
                      </div>
                     : null}
                    <div style={{ display: "flex" }}>
                      <Typography variant="h6" style={{ flex: 10 }}>
                        Result
                      </Typography>
                      <IconButton
                        disabled={executing || appType === 2}
                        color="primary"
                        style={{
                          border: `1px solid ${theme.palette.primary.main}`,
                        }}
                        variant="contained"
                        onClick={() => {
                          executeSingleAction(
                            selectedAction.app_id,
                            selectedAction
                          );
                        }}
                      >
                        <Tooltip title="Run Action" placement="top">
                          <PlayArrowIcon />
                        </Tooltip>
                      </IconButton>
                    </div>
                    {executing ? (
                      <div style={{ textAlign: "center", width: "100%" }}>
							{serverside === true ? null : 
								<CircularProgress
									style={{
										marginTop: 25,
										height: 35,
										width: 35,
										marginLeft: "auto",
										marginRight: "auto",
									}}
								/>
							}
                      </div>
                    ) : (
                      <div>
                        {executionResult.valid ? (
                          <ReactJson
                            src={executionResult.result}
                            theme={theme.palette.jsonTheme}
                            style={theme.palette.reactJsonStyle}
                            collapsed={false}
                            displayDataTypes={false}
                            enableClipboard={(copy) => {
                              handleReactJsonClipboard(copy);
                            }}
                            onSelect={(select) => {
                              HandleJsonCopy(
                                executionResult.result,
                                select,
                                "exec"
                              );
                              console.log("SELECTED!: ", select);
                            }}
                            name={"Result"}
                          />
                        ) : (
                          <Typography variant="body2">
                            {executionResult.result}
                          </Typography>
                        )}
                      </div>
                    )}
                  </div>
                </div>
              ) : selectedTab === 2 && app.id !== undefined ? (
                <div>

				  <Typography variant="h4">
				  	Use the App onprem (hybrid)
				  </Typography> 
				  <Typography variant="body2" color="textSecondary" style={{marginTop: 5, }}>
				  	Due to using docker containers with privately uploaded containers, we had to use a custom registry. Use the command below to download the image to the server if it fails to run.

				  	It will authenticate and authorize you, before redirecting to a Signed URL on https://storage.googleapis.com

				  	<b>&nbsp;Now also works for ARM containers!</b>
				  </Typography> 

				  <div
				  	style={{
						marginTop: 10, 
				  		padding: 15,
				  		minWidth: "50%",
				  		maxWidth: "100%",
				  		backgroundColor: theme.palette.inputColor,
				  		overflowY: "auto",
				  		// Have it inline
				  		borderRadius: theme.palette?.borderRadius,
				  	}}
				  >
					  <code
						style={{
							// Wrap if larger than X
							whiteSpace: "pre",
							overflow: "auto",
							marginRight: 40,
						}}
					  >
						{getDownloadUrl()}
					  </code>
				  </div>
				  <CopyToClipboard
				  	text={getDownloadUrl()}
				  />

				  <div style={{marginTop: 50, }}>
					  <AppStats
						globalUrl={globalUrl}
						appId={app.id}
						{...props}
					  />
				  </div>
                </div>
              ) : selectedTab === 0 ? (
                <div
                  style={{ minWidth: "100%", maxWidth: "100%", margin: "auto" }}
                >
                  <Markdown
					components={{
						img: Img,
						code: CodeHandler,
						h1: Heading,
						h2: Heading,
						h3: Heading,
						h4: Heading,
						h5: Heading,
						h6: Heading,
						a: OuterLink,
					}}
					id="markdown_wrapper"
					escapeHtml={false}
					style={{
						maxWidth: "100%", minWidth: "100%", 
					}}
                  >
				  	{appDocumentation}
				  </Markdown>
                </div>
              ) : selectedTab === 3 && secondaryApp.objectID !== undefined && app.name !== undefined ? (
                <div
                  style={{
                    maxWidth: "100%",
                    minWidth: "100%",
                    margin: "auto",
                    marginTop: 50,
                  }}
                >
                  <Typography
                    variant="h1"
                    style={{ textAlign: "center", fontSize: 22 }}
                  >
                    Connect {app.name.replaceAll("_", " ")} and{" "}
					{secondaryApp.name.replaceAll("_", " ")}
                  </Typography>
                  <Typography
                    variant="body2"
                    color="textSecondary"
                    style={{ textAlign: "center" }}
                  >
                    Using Shuffle, you can connect{" "}
                    {app.name.replaceAll("_", " ")} and{" "}
                    {secondaryApp.name.replaceAll("_", " ")} with no code.
                  </Typography>
                  <div style={{ display: "flex", marginTop: 25 }}>
                    <div
                      style={{
                        flex: 2,
                        display: "flex",
                        maxHeight: 100,
                        minHeight: 100,
                        border: "1px solid rgba(255,255,255,0.3",
                        marginRight: 10,
                        borderRadius: theme.palette?.borderRadius,
                      }}
                    >
                      <img
                        src={app.large_image}
                        alt={app.name}
                        id="logo"
                        style={imageStyle}
                      />
                      <Typography variant="h6" style={textStyle}>
                        {app.name.replaceAll("_", " ")}
                      </Typography>
                    </div>
                    <div
                      style={{
                        flex: 2,
                        display: "flex",
                        maxHeight: 100,
                        minHeight: 100,
                        border: "1px solid rgba(255,255,255,0.3",
                        marginleft: 10,
                        borderRadius: theme.palette?.borderRadius,
                      }}
                    >
                      <img
                        src={secondaryApp.image_url}
                        alt={secondaryApp.name}
                        id="logo"
                        style={imageStyle}
                      />
                      <Typography variant="h6" style={textStyle}>
                        {secondaryApp.name.replaceAll("_", " ")}
                      </Typography>
                    </div>

                  </div>
                  <div style={{ textAlign: "center", marginTop: 25 }}>
                    <Link
                      rel="noopener noreferrer"
                      to={`/register?app_one=${app.name}&app_two=${secondaryApp.name}&message=You need to login first to connect ${app.name} and ${secondaryApp.name}`}
                      style={{ textDecoration: "none" }}
                    >
                      <Button
                        variant="contained"
                        onClick={() => {
													const allapps = [app.name, secondaryApp.name].sort()

                          ReactGA.event({
                            category: "Appexplorer",
                            action: "app_connect_click",
                            label: `${allapps[0]}_and_${allapps[1]}`,
                          })
                        }}
                        style={{
                          padding: 15,
                          marginTop: 15,
                          borderRadius: 25,
                          height: 50,
                          margin: "15px 0px 15px 0px",
                          fontSize: 14,
                          color: "white",
                          backgroundImage: buttonBackground,
                          marginRight: 10,
                        }}
                      >
                        Connect {app.name.replaceAll("_", " ")} and{" "}
                        {secondaryApp.name.replaceAll("_", " ")}
                      </Button>
                    </Link>
                  </div>


                  <Divider style={{ marginTop: 50, marginBottom: 50 }} />
                  <div
                    style={{
                      minWidth: "100%",
                      maxWidth: "100%",
                      margin: "auto",
                    }}
                  >
                  	<Markdown
				  	  components={{
				  	  	img: Img,
				  	  	code: CodeHandler,
				  	  	h1: Heading,
				  	  	h2: Heading,
				  	  	h3: Heading,
				  	  	h4: Heading,
				  	  	h5: Heading,
				  	  	h6: Heading,
				  	  	a: OuterLink,
				  	  }}
				  	  id="markdown_wrapper"
				  	  escapeHtml={false}
				  	  style={{
				  	  	maxWidth: "100%", minWidth: "100%", 
				  	  }}
                  	>
				  		{appDocumentation}
				  	</Markdown>
                  </div>
                </div>
              ) : 
			  	selectedTab === 4 ? 
					<div>
						<Typography variant="h4">
							App Details
						</Typography> 
						<Typography variant="body2" style={{marginTop: 20, marginBottom: 10, }} color="textSecondary">
							Add more details about your app here. This is to help both the Shuffle team, and the public get easier access to this information. Data from these will be used to track app "completeness" for recommendation systems.
						</Typography> 
				  		<Divider style={{marginTop: 10, marginBottom: 10, }}/>
				  		<Typography variant="h6" style={{marginTop: 20, marginBottom: 10, }}>
				  			Validation Action
				  		</Typography>
						<Typography variant="body2" style={{marginBottom: 20, }} color="textSecondary">
				  			The validation action is the action that is used to validate the app. This is used both when a user wants to validate their auth, as well as when Shuffle runs automatic tests of the app. It is recommended that the action should be a GET request. Validation is decided based on whether the action is ran successfully in a workflow.
						</Typography> 

						<Autocomplete
							id="action_search"
							value={selectedValidationAction}
							classes={{ inputRoot: classes.inputRoot }}
							groupBy={(option) => {
								// Most popular
								// Is categorized
								// Uncategorized
								return option.category_label !== undefined && option.category_label !== null && option.category_label.length > 0 ? "Most used" : "All Actions";
							}}
							renderGroup={(params) => {

								return (
									<li key={params.key}>
										<Typography variant="body1" style={{textAlign: "center", marginLeft: 10, marginTop: 25, marginBottom: 10, }}>{params.group}</Typography>
										<Typography variant="body2">{params.children}</Typography>
									</li>
								)	
							}}
							options={renderedActionOptions}
							ListboxProps={{
							  style: {
								backgroundColor: theme.palette.surfaceColor,
								color: "white",
							  },
							}}
							filterOptions={(options, { inputValue }) => {
								const lowercaseValue = inputValue === null ? "" : inputValue.toLowerCase()
								options = options.filter((x) => {
									if (x.name === undefined || x.name === null) {
										x.name = ""
									}

									if (x.description === undefined || x.description === null) {
										x.description = ""
									}

									if (x.method !== "GET") {
										return null
									}

									return x.name.replaceAll("_", " ").toLowerCase().includes(lowercaseValue) || x.description.toLowerCase().includes(lowercaseValue)
								})

								return options
							}}
							getOptionLabel={(option) => {
							  if (option === undefined || option === null || option.name === undefined || option.name === null ) {
								return null;
							  }

							  const newname = (
								option.name.charAt(0).toUpperCase() + option.name.substring(1)
							  ).replaceAll("_", " ");

							  return newname;
							}}
							fullWidth
							style={{
							  backgroundColor: theme.palette.inputColor,
							  height: 50,
							  borderRadius: theme.palette?.borderRadius,
							}}
							onChange={(event, newValue) => {
							  // Workaround with event lol
							  console.log("Changed to: ", event, newValue)
							  toast("Changed validation action") 
							  if (newValue !== undefined && newValue !== null) {
								/*
								setNewSelectedAction({ 
									target: { 
										value: newValue.name 
									} 
								})
								*/
							  }
							}}
							renderOption={(props, option, state) => {
							  var newActionname = option.name;
							  if (option.label !== undefined && option.label !== null && option.label.length > 0) {
								newActionname = option.label;
							  }

							  var newActiondescription = option.description;
							  //console.log("DESC: ", newActiondescription)
							  if (option.description === undefined || option.description === null) {
								newActiondescription = "Description: No description defined for this action"
							  } else {
								newActiondescription = "Description: "+newActiondescription
							  }

							  const iconInfo = GetIconInfo({ name: option.name });
							  const useIcon = iconInfo.originalIcon;

							  if (newActionname === undefined || newActionname === null) {
								  newActionname = "No name"
								  option.name = "No name"
								  option.label = "No name"
							  }

							  newActionname = (newActionname.charAt(0).toUpperCase() + newActionname.substring(1)).replaceAll("_", " ");

								var method = ""
								var extraDescription = ""
								if (option.name.includes("get_")) {
									method = "GET"
								} else if (option.name.includes("post_")) {
									method = "POST"
								} else if (option.name.includes("put_")) {
									method = "PUT"
								} else if (option.name.includes("patch_")) {
									method = "PATCH"
								} else if (option.name.includes("delete_")) {
									method = "DELETE"
								} else if (option.name.includes("options_")) {
									method = "OPTIONS"
								} else if (option.name.includes("connect_")) {
									method = "CONNECT"
								}

								// FIXME: Should it require a base URL?
								if (method.length > 0 && option.description !== undefined && option.description !== null && option.description.includes("http")) {
									var extraUrl = ""
									const descSplit = option.description.split("\n")
									// Last line of descSplit
									if (descSplit.length > 0) {
										extraUrl = descSplit[descSplit.length-1]
									} 

									if (extraUrl.length > 0) {
										if (extraUrl.includes(" ")) {
											extraUrl = extraUrl.split(" ")[0]
										}

										if (extraUrl.includes("#")) {
											extraUrl = extraUrl.split("#")[0]
										}

										extraDescription = `${method} ${extraUrl}`
									} else {
										//console.log("No url found. Check again :)")
									}
								}

							  return (
								<ActionSelectOption
								  {...props}
									option={option}
									newActiondescription={newActiondescription}
									useIcon={useIcon}
									newActionname={newActionname}
									extraDescription={extraDescription}
								/>
							  );
							}}
							renderInput={(params) => {
								if (params.inputProps?.value) {
									const prefixes = ["Post", "Put", "Patch"];
									for (let prefix of prefixes) {
										if (params.inputProps.value.startsWith(prefix)) {
											let newValue = params.inputProps.value.replace(prefix + " ", "");
											if (newValue.length > 1) {
												newValue = newValue.charAt(0).toUpperCase() + newValue.substring(1);
											}
											// Set the new value without mutating inputProps
											params = { ...params, inputProps: { ...params.inputProps, value: newValue } };
											break;
										}
									}
									// Check if it starts with "Get List" and method is "Get"
									if (params.inputProps.value.startsWith("Get List")) {
										console.log("Get List")
									}
								}

								const actionDescription = ""
								const isIntegration = false

								  return (
									  <Tooltip title={actionDescription}
										placement="right" 
										open={false}
										PopperProps={{
											sx: {
											'& .MuiTooltip-tooltip': {
												backgroundColor: 'transparent',
												boxShadow: 'none',
											},
											'& .MuiTooltip-arrow': {
												color: 'transparent',
											},
											},
										}}
										>
											<TextField
											{...params}

											data-lpignore="true"
											autocomplete="off"
											dataLPIgnore="true"
											autoComplete="off"

											color="primary"
											id="checkbox-search"
											variant="body1"
											style={{
												backgroundColor: theme.palette.inputColor,
												borderRadius: theme.palette?.borderRadius,
											}}
											label={"Select Validation Action"}
											variant="outlined"
											name={`disable_autocomplete_${Math.random()}`}
											/>	
										</Tooltip>
								  )
								}}
						/>

				  		<Divider style={{marginTop: 30, marginBottom: 10, }}/>
				  		<Typography variant="h6" style={{marginTop: 20, marginBottom: 10, }}>
				  			Triggers
				  		</Typography>
						<div style={{display: "flex", }}>
							<Typography variant="body1" color="textSecondary" style={{marginTop: 7, }}>
								Schedule:
							</Typography>
							<Checkbox
								checked={triggers === undefined ? false : triggers.find(trigger => trigger.name === "Schedule") !== undefined}
								label="Schedule"
								onChange={(e) => {
									editTrigger("Schedule")

								}}
							/>
							<Typography variant="body1" color="textSecondary" style={{marginTop: 7, marginLeft: 20, }}>
								Webhook:
							</Typography>
							<Checkbox
								checked={triggers === undefined ? false : triggers.find(trigger => trigger.name === "Webhook") !== undefined}
								label="Webhook"
								onChange={() => {
									editTrigger("Webhook")

								}}
							/>
						</div>
				  		{triggers === undefined || triggers === null || triggers.find(trigger => trigger.name === "Webhook") === undefined ? null : 
							<TextField 
								label="Webhook Example response"
								variant="outlined"
								disabled
								fullWidth
								multiline
								style={{
									maxHeight: 200,
								}}

								onBlur={(e) => {
									submitAppDetails("extra_value", e.target.value)
								}}
							/>
						}
				  		<Divider />
				  		<Typography variant="h6" style={{marginTop: 20, marginBottom: 10, }}>
				  			External info	
				  		</Typography>
						<AppDetails 
							title="External Documentation URL"
							inputTitle={app.reference_info.documentation_url === undefined ? "" : app.reference_info.documentation_url}
						/>
						<AppDetails 
							title="Blogpost"
							inputTitle={app.blogpost === undefined ? "" : app.blogpost}
						/>
						<AppDetails 
							title="Video"
							inputTitle={app.video === undefined ? "" : app.video}
						/>

						<Typography variant="h6" style={{marginTop: 20, marginBottom: 10, }}>
							Partner Details
						</Typography> 
						<AppDetails 
							title="Partner Website"
							inputTitle={app.company_url === undefined ? "" : app.company_url}
						/>
						<AppDetails 
							title="Partner Contacts"
							inputTitle={app.reference_info.partner_contacts === undefined ? "" : app.reference_info.partner_contacts}
						/>

				  		<Divider style={{marginTop: 20, }} />

						<Typography variant="h6" style={{marginTop: 20, marginBottom: 10, }}>
				  			Public Status
						</Typography> 
				  		<Button
				  			style={{marginTop: 10, }}
				  			variant="outlined"
							onClick={() => {
								removeAppFromSearchEngine(app.id)
							}}
				  		>
				  			Unpublish App from Search Engine 
				  		</Button>

					</div>
				:
			  (
                <div>
                  <Typography
                    variant="h1"
					color="textSecondary"
                    style={{
					  margin: "auto",
                      textAlign: "center",
                      fontSize: 22,
                      marginTop: 20,
                      paddingTop: 20,
					  maxWidth: 550,
					  minWidth: 550,
                    }}
                  >
                    This app is currently in Beta, but is usable. Interested in using this app? Click the button below or contact us.
                    <div style={{ marginTop: 10 }}>
                    {activateButton}
                    </div>
                  </Typography>

					{app.description !== undefined && 
						app.description !== null &&
						app.description !== "" ? 
							<span style={{textAlign: "center", }}>
								<Typography variant="h6" style={{ marginTop: 50, }}>
									More about the app 
								</Typography>
								<Typography
									variant="body1"
									color="textSecondary"
									style={{ maxWidth: 550, minWidth: 550, margin: "auto", marginTop: 10,  }}
								>
									{app.description}
								</Typography>
							</span>
					: null}
                </div>
              )}
            </div>
          </Paper>
        </div>
      </div>
    );

  // Random names for type & autoComplete. Didn't research :^)
  const imageData = file.length > 0 ? file : fileBase64;
  const height = 100;
  const imageInfo = (
    <img
      src={imageData}
      alt=""
      id="logo"
      style={{
        borderRadius: theme.palette?.borderRadius,
        border: "1px solid rgba(255,255,255,0.6)",
        maxWidth: height,
        maxHeight: height,
        minWidth: height,
        minHeight: height,
        objectFit: "contain",
      }}
    />
  );

  const publishModal = publishModalOpen ? (
    <Dialog
      open={publishModalOpen}
      onClose={() => {
        setPublishModalOpen(false);
      }}
      PaperProps={{
        sx: {
          borderRadius: theme?.palette?.DialogStyle?.borderRadius,
          border: theme?.palette?.DialogStyle?.border,
          minWidth: '500px',
          fontFamily: theme?.typography?.fontFamily,
          backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
          zIndex: 1000,
          '& .MuiDialogContent-root': {
            backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
          },
          '& .MuiDialogTitle-root': {
            backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
          },
        }
      }}
    >
      <DialogTitle style={{ marginBottom: 0, padding: 50,  paddingBottom: 0, }}>
        <div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
          Are you sure you want to PUBLISH this app?
        </div>
      </DialogTitle>
      <DialogContent
        style={{ color: "rgba(255,255,255,0.65)", textAlign: "center", padding: 50, }}
      >
        <div>
          <Typography variant="body1" style={{ marginBottom: 20 }}>
            Before publishing, make sure to sanitize the App for anything you don't want public. 
          </Typography>
          <Typography variant="body1" style={{ marginBottom: 20 }}>
			The published App is yours, and you can always change your public Apps after they are released.
          </Typography>
        </div>
        <Button
          variant="contained"
          color="primary"
          style={{ borderRadius: "2px", textTransform: 'none', fontSize:16,  }}
          onClick={() => {
			updateAppField(selectedApp.id, "sharing", true);
            setPublishModalOpen(false);
          }}
        >
          Yes
        </Button>
        <Button
          style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: "#ff8544", marginLeft: 5 }}
          onClick={() => {
            setPublishModalOpen(false);
          }}
          color="primary"
        >
          No
        </Button>
      </DialogContent>
    </Dialog>
  ) : null;

  const landingpageDataBrowser = (
    <div
      style={{
        paddingTop: isMobile ? 0 : 25,
        paddingBottom: 100,
        color: "white",
      }}
    >
      {publishModal}
      <div style={{ display: "flex", position: "relative" }}>
        {isMobile ? null : (
          <Breadcrumbs
            aria-label="breadcrumb"
            separator="›"
            style={{ color: theme.palette.text.primary, marginLeft: 15, flex: 100 }}
          >
            <Link
              to="/search"
              style={{ textDecoration: "none", color: "inherit" }}
            >
              <h2 style={{ color: theme.palette.text.primary }}>
                <AppsIcon style={{ marginRight: 10 }} />
                Apps
              </h2>
            </Link>
            <Link
              to={`/apps/${params.appid}`}
              style={{ textDecoration: "none", color: "inherit" }}
            >
              <h2>{name}</h2>
            </Link>
          </Breadcrumbs>
        )}
        <div
          style={{ position: "absolute", top: 40, right: 10, display: "flex", gap: "10px",}}
        >
          {app.documentation_download_url !== undefined &&
          app.documentation_download_url !== null &&
          app.documentation_download_url.length > 0 ? (
            <IconButton
              color="primary"
              style={{ marginRight: 20 }}
              variant="contained"
              onClick={() => {
                const data = openapi;

                let linkElement = document.createElement("a");
                linkElement.setAttribute("target", "_blank");
                linkElement.setAttribute(
                  "href",
                  app.documentation_download_url
                );
                linkElement.setAttribute(
                  "download",
                  app.documentation_download_url
                );
                linkElement.click();
              }}
            >
              <Tooltip title="Download Documentation" placement="top">
                <DescriptionIcon color="secondary" />
              </Tooltip>
            </IconButton>
          ) : null}
          {appType === 0 || appType === 2 ? (
            <IconButton
              style={{ marginRight: 20 }}
              onClick={() => {
                const data = openapi;
                let exportFileDefaultName = name + ".json";

                let dataStr = JSON.stringify(data);
                let dataUri =
                  "data:application/json;charset=utf-8," +
                  encodeURIComponent(dataStr);
                let linkElement = document.createElement("a");
                linkElement.setAttribute("href", dataUri);
                linkElement.setAttribute("download", exportFileDefaultName);
                linkElement.click();

                const tmpurl = new URL(window.location.href);
                const searchParams = tmpurl.searchParams;
                const queryID = searchParams.get("queryID");

                if (queryID !== undefined && queryID !== null) {
                  aa("init", {
                    appId: "JNSS5CFDZZ",
                    apiKey: "c8f882473ff42d41158430be09ec2b4e",
                  });

                  const timestamp = new Date().getTime();
                  aa("sendEvents", [
                    {
                      eventType: "conversion",
                      eventName: "Public App Downloaded",
                      index: "appsearch",
                      objectIDs: [app.id],
                      timestamp: timestamp,
                      queryID: queryID,
                      userToken:
                        userdata === undefined ||
                        userdata === null ||
                        userdata.id === undefined
                          ? "unauthenticated"
                          : userdata.id,
                    },
                  ]);
                } else {
                  console.log("No query to handle when downloading");
                }
              }}
            >
              <Tooltip title="Download OpenAPI" placement="top">
                <CloudDownloadIcon color={theme.palette.text.primary} />
              </Tooltip>
            </IconButton>
          ) : null}

	  	  {selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.id !== undefined && userdata.support === true ?
			// Iconbutton for authentication with just an icon. Link to /apps/authentication?app_id=app.id
			<IconButton
				color="secondary"
			  	variant="contained"
			    style={{marginRight: 20, }}
			  	onClick={() => {
					window.open(`/appauth?app_id=${app.id}&auth=${selectedOrganization.org_auth.token}`, "_blank")
				}}
			>
				<Tooltip title="Public Authentication link for the current Organization. Times out every 24 hours." placement="top">
				  	<LockOpenIcon color={theme.palette.text.primary}/>
				</Tooltip>
			</IconButton>
		  : null}

	  	  {isMobile || userdata?.active_apps === undefined || userdata?.active_apps === null || !userdata?.active_apps?.includes(appId) ? null :
            <Button
			  variant={userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId) ? "outlined": "contained"}
              component="label"
              color="primary"
              onClick={() => {
				if (!isLoggedIn) {
					//navigate("/login?message=You must be logged in to activate this app&view=/apps/" + params.appid);
					toast("You must be logged in to activate apps! Go to /login first.") 
					return;
				}

				toast.info("Sending download job to relevant runtime locations")
                
				activateApp("distribute")

              }}
              style={{ height: 40, marginTop: 5 }}
            >
				Push Image
            </Button>
          }

          {isMobile ? null : (
            <Button
			  variant={userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId) ? "outlined": "contained"}
              component="label"
              color="primary"
              onClick={() => {
				if (!isLoggedIn) {
					//navigate("/login?message=You must be logged in to activate this app&view=/apps/" + params.appid);
					toast("You must be logged in to activate apps! Go to /login first.") 
					return;
				}

                const tmpurl = new URL(window.location.href);
                const searchParams = tmpurl.searchParams;
                const queryID = searchParams.get("queryID");

                if (queryID !== undefined && queryID !== null) {
                  aa("init", {
                    appId: "JNSS5CFDZZ",
                    apiKey: "c8f882473ff42d41158430be09ec2b4e",
                  });

                  const timestamp = new Date().getTime();
                  aa("sendEvents", [
                    {
                      eventType: "conversion",
                      eventName: "Public App Activated",
                      index: "appsearch",
                      objectIDs: [app.id],
                      timestamp: timestamp,
                      queryID: queryID,
                      userToken:
                        userdata === undefined ||
                        userdata === null ||
                        userdata.id === undefined
                          ? "unauthenticated"
                          : userdata.id,
                    },
                  ]);
                } else {
                  console.log("No query to handle when activating");
                }

                activateApp();
              }}
              style={{ height: 40, marginTop: 5 }}
            >
			  {userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appId) ?
				  "Deactivate App"
				  :
				  "Activate App"
			  }
            </Button>
          )}
			{appType === 1 ? null : 
  				userdata?.support || userdata?.id === app?.owner || (userdata?.admin === "true" && userdata?.active_org?.id === app?.reference_org) || app?.contributors?.includes(userdata?.id) || creatorProfile?.self === true ?
				<Tooltip title="Edit App" placement="top">
					<Button
						variant="outlined"
						component="label"
						color="secondary"
						onClick={() => {
							navigate(`/apps/edit/${appId}`)
						}}
						style={{ height: 40, marginTop: 5, marginLeft: 5, }}
					>
						<EditIcon style={{marginRight: 10, }}/> Edit 
					</Button>
				</Tooltip>
  			: isMobile || !isLoggedIn ? null : 
				<Tooltip title="Fork this app to edit it for yourself" placement="top">
					<Button
						variant="outlined"
						component="label"
						color="secondary"
						onClick={() => {
							navigate(`/apps/new?id=${appId}`)
						}}
						style={{ height: 40, marginTop: 5, marginLeft: 5, }}
					>
						<ForkRightIcon style={{marginRight: 10, }}/> Fork
					</Button>
				</Tooltip>
          }

          {appType === 0 || appType === 2 ? (
			<div style={{display: "flex", alignItems: 'center',gap: 10 }}>
				<a
				  rel="noopener noreferrer"
				  href={`/apis/${app.id}`}
				  target="_blank"
				  style={{ textDecoration: "none", color: "#f85a3e", }}
				>
				  <Button
					variant="outlined"
					color="secondary"
			  		style={{ height: 40, marginTop: 5, marginLeft: 5, }}
				  >
  					<OpenInNewIcon style={{marginRight: 5, }}/>
					Explore API
				  </Button>
				</a>
                <Select
                  value={sharingConfiguration}
			  	  disabled={!isCloud}
                  onChange={(event) => {
                    if ((appdata.owner !== userdata.id) && (!userdata.support)) {
                      toast("You're not the owner of this app. You can't update it.");
                      return;
                    }
                    setSharingConfiguration(event.target.value);
                    if (event.target.value === "you") {
                      toast("Changing sharing to " + event.target.value);
                      updateAppField(appdata.id, "sharing", false);
                    } else if (
                      event.target.value === "everyone" ||
                      event.target.value === "public"
                    ) {
  
                    if (!isCloud) {
                        setPublishModalOpen(true)
                    } else {
                        updateAppField(appdata.id, "sharing", true);
                      }
                    } else {
                      console.log(
                        "Can't handle value for sharing: ",
                        event.target.value
                      );
                    }
                  }}
                  style={{
                    width: 150,
                    backgroundColor: theme.palette.backgroundColor,
                    color: theme.palette.text.primary,
                    height: 40,
                    marginTop: 5
                  }}
                  SelectDisplayProps={{
                    style: {
                      marginLeft: 10,
                      display: 'flex'
                    },
                  }}
                >
                  {userRoles.map((data) => {
                    return (
                      <MenuItem
                        key={data}
                        sx={{ backgroundColor: inputColor, color: theme.palette.text.primary, display: 'flex', "&:hover": { backgroundColor: theme.palette.hoverColor } }}
                        value={data}
                      >
                        {data}
                      </MenuItem>
                    );
                  })}
                </Select>
			</div>
          ) : (
            <a
              rel="noopener noreferrer"
              href={"https://shuffler.io/docs/app_creation#python---building-apps-manually"}
              target="_blank"
              style={{ textDecoration: "none", color: "#f85a3e" }}
            >
              <img
                src={"/images/frameworks/python.jpeg"}
                alt="Python integration for Shuffle"
                id="logo"
                style={{
                  marginTop: 5,
                  marginLeft: 15,
                  height: 35,
                  borderRadius: theme.palette?.borderRadius,
                }}
              />
            </a>
          )}
        </div>
      </div>
      <div
        style={{ display: "flex", flexDirection: isMobile ? "column" : "row" }}
      >
        <Paper style={boxStyle}>
          <div
            style={{
              color: "white",
              flex: 1,
              display: "flex",
              flexDirection: "row",
            }}
          >
            <div
              style={{
                flex: 1,
                margin: 10,
                cursor: "pointer",
                backgroundColor: inputColor,
                maxWidth: height,
                maxHeight: height,
              }}
              onClick={() => {
                upload.click();
              }}
            >
              {imageInfo}
            </div>
            <div style={{ marginLeft: 15, color: "white" }}>
							<div style={{display: "flex", marginTop: 5, }}>
              	<Typography variant="h6" style={{  }}>
									{name}
								</Typography>
                <span style={{ marginTop: "auto", marginBottom: "auto" }}>
                  {(app.public || appType === 0) && app.skipped_build == false ? (
                    <Tooltip title="This app is verified" placement="top">
                      <VerifiedUserIcon style={{marginLeft: 10, }} />
                    </Tooltip>
                  ) : (
                    <Tooltip
                      title="This app may not work, and can't be tested yet"
                      placement="top"
                    >
                      <WarningIcon />
                    </Tooltip>
                  )}
                </span>

								{/* Handles category changing, but looks like shit. Should probably work as a suggestion */}
								{/*
								<Select
									onChange={(e) => {
										console.log("Changed: ", e.target.value)
          					setNewWorkflowCategories([e.target.value]);
									}}
        					value={newWorkflowCategories.length === 0 ? "Select a category" : newWorkflowCategories[0]}
        					style={{ 
										flexAlign: "right",
										color: "white", 
										height: 40,
										width: 150,
										borderRadius: 20,
										marginLeft: 200, 
									}}
								>
									{categories.map((category, categoryIndex) => {
										if (category === undefined || category === null || category === "") {
											return null
										}

										return (
											<MenuItem
												key={categoryIndex}
												value={category}
												style={{ 
												}}
											>
												{category.name}
											</MenuItem>
										)
									})}
								</Select>
								*/}
              </div>
              <div style={{ marginTop: 5 }}>
                <Typography variant="body1" color="textSecondary">
                 Version - {app?.app_version}
                </Typography>
              </div>
              <div style={{ marginTop: 5 }} />
              {Object.getOwnPropertyNames(creatorProfile).length !== 0 &&
				  creatorProfile.github_avatar !== undefined &&
				  creatorProfile.github_avatar !== null ? (
                <div style={{ display: "flex" }}>
                  <IconButton
                    color="primary"
                    style={{ padding: 0, marginRight: 10 }}
                    aria-controls="simple-menu"
                    aria-haspopup="true"
                    onClick={(event) => {
                      setAnchorElAvatar(event.currentTarget);
                    }}
                  >
                    <Link
                      to={`/creators/${creatorProfile.github_username}`}
                      style={{ textDecoration: "none", color: "#f86a3e" }}
                    >
                      <Avatar
                        style={{ height: 30, width: 30 }}
                        alt={contact.name}
                        src={creatorProfile.github_avatar}
                      />
                    </Link>
                  </IconButton>
                  <Typography
                    variant="body1"
                    color="textSecondary"
                    style={{ color: "" }}
                  >
                    Shared by{" "}
                    <Link
                      to={`/creators/${creatorProfile.github_username}`}
                      style={{ textDecoration: "none", color: "#f86a3e" }}
                    >
                      {creatorProfile.github_username}
                    </Link>
                    <Button
                      style={{
                        borderRadius: 25,
                        fontSize: 11,
                        color: theme.palette.text.primary,
                        backgroundColor: theme.palette.platformColor,
                        border: "1px solid #ddf4e1",
                        textTransform: "none",
                        marginLeft: 4,
                        padding: 1,
                      }}
                      onClick={() => {
                        ReactGA.event({
                          category: "hiring_button",
                          action: `${creatorProfile.github_username}_app_click`,
                          label: name,
                        });

                        if (window.drift !== undefined) {
                          window.drift.api.startInteraction({
                            interactionId: 342345,
                          });
                        } else {
                          console.log(
                            "Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ",
                            window.drift
                          );
                          toast(
                            `Thanks for showing interest in getting help from ${creatorProfile.github_username}`
                          );
                        }
                      }}
                    >
                      Get help
                    </Button>
				{contact.name !== undefined &&
                    contact.name !== null &&
                    !contact.name.includes("frikky") &&
                    contact.name.length > 0 &&
                    contact.name.toLowerCase() !==
                      creatorProfile.github_username.toLowerCase() &&
                    !(
                      contact.name.toLowerCase().includes("anon") &&
                      creatorProfile.github_username.length > 0
                    ) ? (
                      <span style={{ marginLeft: 15 }}>
                        {" "}
                        •&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Created by {contact.name}
                      </span>
                    ) : (
                      ""
                    )}
                  </Typography>
                </div>
              ) : contact.name !== undefined &&
                contact.name !== null &&
                contact.name.length > 0 ? (
                <Typography
                  variant="body1"
                  color="textSecondary"
                  style={{ color: "" }}
                >
                  Created by {contact.name}
                </Typography>
              ) : null}
              <div
                style={{
                  marginTop: 5,
                  minHeight: 30,
                  maxHeight: 30,
                  overflow: "hidden",
                }}
              >
                {newWorkflowTags.map((tag, index) => {
                  return (
                    <Chip
                      key={index}
                      style={chipStyle}
                      variant="outlined"
                      label={tag}
                      color="primary"
                    />
                  );
                })}
              </div>
            </div>
          </div>
        </Paper>
        {isMobile || serverside ? null : (
          <Card
          style={{
            flex: 1,
            margin: 10,
            backgroundColor: boxStyle.backgroundColor,
            color: "white",
            textAlign: "center",
          }}
          sx={{
            "&:hover": {
              backgroundColor: theme.palette.hoverColor,
            },
          }}
        >
          <CardActionArea
            component={Link}
            to={"/search?tab=workflows&q=" + name.split(" ")[0]}
            style={{ height: "100%" }}
          >
            <div style={{ padding: "50px 0" }}>
              <Typography variant="h4">
                {relatedWorkflows !== 0 ? (
                  relatedWorkflows
                ) : (
                  <InstantSearch
                    searchClient={searchClient}
                    indexName="workflows"
                  >
                    <CustomSearchBox defaultRefinement={name.split(" ")[0]} />
                    <CustomWorkflowHits />
                  </InstantSearch>
                )}
              </Typography>
              <Typography
                variant="h6"
                color="textSecondary"
                style={{ marginTop: 5 }}
              >
                Workflows
              </Typography>
            </div>
          </CardActionArea>
        </Card>
        
        )}
        {app.video !== undefined &&
        app.video !== null &&
        app.video.includes("http") ? (
          <div style={{ margin: 10 }}>
            {app.video.includes("loom.com/share") &&
            app.video.split("/").length > 4 ? (
              <div>
                <iframe
                  src={`https://www.loom.com/embed/${app.video.split("/")[4]}`}
                  frameBorder={false}
                  webkitallowFullscreen={true}
                  mozallowFullscreen={true}
                  allowFullScreen={true}
                  style={{
                    marginTop: 15,
                    top: 0,
                    left: 0,
                    maxWidth: 230,
                    minWidth: 230,
                  }}
                />
              </div>
            ) : app.video.includes("youtube.com") &&
              app.video.split("/").length > 3 &&
              app.video.includes("v=") ? (
              <div>
                <iframe
                  src={`https://www.youtube.com/embed/${new URL(
                    app.video
                  ).searchParams.get("v")}`}
                  frameBorder={false}
                  webkitallowFullscreen={true}
                  mozallowFullscreen={true}
                  allowFullScreen={true}
                  style={{
                    top: 0,
                    left: 0,
                    maxWidth: 230,
                    minWidth: 230,
                  }}
                />
              </div>
            ) : (
              <Typography variant="body1">{app.video}</Typography>
            )}
          </div>
        ) : isMobile || serverside ? null : (
          <Card
            style={{
              flex: 1,
              margin: 10,
              backgroundColor: boxStyle.backgroundColor,
              color: "white",
              textAlign: "center",
            }}
            sx={{
              "&:hover": {
                backgroundColor: theme.palette.hoverColor,
              },
            }}
          >
            <CardActionArea
              component={Link}
              to={"/search?tab=apps&q=" + name.split(" ")[0]}
              style={{
                height: "100%",
              }}
            >
              <div style={{ padding: "50px 0" }}>
                <Typography variant="h4">
                  {relatedApps !== 0 ? (
                    relatedApps
                  ) : (
                    <InstantSearch
                      searchClient={searchClient}
                      indexName="appsearch"
                    >
                      <CustomSearchBox defaultRefinement={name.split(" ")[0]} />
                      <CustomAppHits />
                    </InstantSearch>
                  )}
                </Typography>
                <Typography
                  variant="h6"
                  color="textSecondary"
                  style={{ marginTop: 5 }}
                >
                  Related apps
                </Typography>
              </div>
            </CardActionArea>
          </Card>

        )}
      </div>
      <div>
        <div style={{}}>{actionView}</div>
        {errorCode.length > 0 ? `Error: ${errorCode}` : null}
      </div>
    </div>
  );
  const loadedCheck =
    (isLoaded && isAppLoaded) || serverside === true ? (
      <div>
        {serverside === true ? (
          <div style={bodyDivStyle}>{landingpageDataBrowser}</div>
        ) : (
          <Fade
            in={true}
            timeout={1000}
            style={{ transitionDelay: `${150}ms` }}
          >
            <div style={bodyDivStyle}>{landingpageDataBrowser}</div>
          </Fade>
        )}
        <TextField
          id="copy_element_shuffle"
          value={to_be_copied}
          style={{ display: "none" }}
        />
      </div>
    ) : (
      <div>
		{serverside === true ? null : 
        	<div style={{ margin: "auto", textAlign: "center", width: 250 }}>
				<CircularProgress
					style={{
						marginTop: "27vh",
						minHeight: 35,
						maxHeight: 35,
						minWidth: 35,
						maxWidth: 35, 
						marginLeft: "auto",
						marginRight: "auto",
					}}
				/>
				<Typography variant="body1" color="textSecondary">
					Loading App
				</Typography>
			</div>
				}
      </div>
    );

  return <div style={{ marginLeft: !isLoggedIn || !isLoaded ? 0 : leftSideBarOpenByClick ? 280 : 80, transition: 'margin-left 0.3s ease'}}>{loadedCheck}</div>;
};
export default AppExplorer;
