From 376a6d51730be5fe6a4f94c51313f52f5eb84f9f Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 20 Dec 2024 21:56:33 +0200 Subject: [PATCH] Sync --- frontend/src/components/AppCreationModal.jsx | 64 +- frontend/src/components/AppModal.jsx | 43 +- frontend/src/components/AppSelection.jsx | 10 +- frontend/src/components/Billing.jsx | 376 ++++-- frontend/src/components/BillingStats.jsx | 152 ++- frontend/src/components/CacheView.jsx | 25 +- frontend/src/components/EditWorkflow.jsx | 4 +- frontend/src/components/Files.jsx | 114 +- frontend/src/components/LeftSideBar.jsx | 95 +- frontend/src/components/LicencePopup.jsx | 246 +++- frontend/src/components/NewHeader.jsx | 44 +- frontend/src/components/Priorities.jsx | 167 ++- frontend/src/components/Priority.jsx | 2 +- frontend/src/views/Admin2.jsx | 5 +- frontend/src/views/AngularWorkflow.jsx | 87 +- frontend/src/views/AppCreator.jsx | 11 +- frontend/src/views/Apps2.jsx | 224 ++-- frontend/src/views/Workflows2.jsx | 1124 +++++++++++------- 18 files changed, 1867 insertions(+), 926 deletions(-) diff --git a/frontend/src/components/AppCreationModal.jsx b/frontend/src/components/AppCreationModal.jsx index 7b650fbd..f5a25169 100644 --- a/frontend/src/components/AppCreationModal.jsx +++ b/frontend/src/components/AppCreationModal.jsx @@ -57,7 +57,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { : hover ? "" : "1px solid rgba(255,255,255,0.3)", - borderImage: makeFancy ? "linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1" : "none", + borderImage: makeFancy ? "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1" : "none", transition: 'all 0.2s ease-in-out', } @@ -340,7 +340,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { const circularLoader = validation ? : null - const errorText = openApiError?.length > 0 ?
Error: {openApiError}
: null + const errorText = openApiError?.length > 0 ?
Error: {openApiError}
: null // Common dialog styles const dialogStyle = { @@ -400,7 +400,13 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { Create New App { + setOpenApi("") + setOpenApiError("") + setAppValidation("") + setValidation(false) + onClose() + }} sx={{ color: 'rgba(255, 255, 255, 0.7)', '&:hover': { bgcolor: 'rgba(255, 255, 255, 0.1)' } @@ -477,7 +483,14 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { Create New App from Open API or Swagger setOpenApiModal(false)} + onClick={() => { + setOpenApiModal(false) + setGenerateAppModal(false) + setOpenApi("") + setOpenApiError("") + setAppValidation("") + setValidation(false) + }} sx={{ color: 'rgba(255,255,255,0.7)', '&:hover': { bgcolor: 'rgba(255,255,255,0.1)' } @@ -487,14 +500,16 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { -
- +
+ Paste in the URI for the OpenAPI or find out How to find URI for openAPI? @@ -506,12 +521,13 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { fullWidth variant="outlined" placeholder="Open API URI" + style={{ fontFamily: theme?.typography?.fontFamily, fontSize: '16px' }} sx={{ '& .MuiOutlinedInput-root': { - height: '40px' + height: '40px', }, '& .MuiOutlinedInput-input::placeholder': { - fontSize: '14px' + fontSize: '16px' } }} onChange={(e) => setOpenApi(e.target.value)} @@ -526,7 +542,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { px: 3, textTransform: 'none', height: '40px', - fontSize: '14px', + fontSize: '16px', fontFamily: theme?.typography?.fontFamily, cursor: 'pointer' }} @@ -562,7 +578,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { sx={{ color: '#FF8544', borderColor: '#FF8544', - px: 3, + px: 5, py: 1, '&:hover': { borderColor: '#FF8544', @@ -570,7 +586,8 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { }, textTransform: 'none', fontSize: '14px', - fontFamily: theme?.typography?.fontFamily + fontFamily: theme?.typography?.fontFamily, + height: '40px' }} > Upload @@ -605,7 +622,8 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { }, textTransform: 'none', py: 1.5, - fontSize: '14px', + mb: 1.5, + fontSize: '16px', fontWeight: 500, width: '100%', maxWidth: '300px', @@ -648,7 +666,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { Generate an app based on documentation (beta) @@ -656,6 +674,10 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { onClick={() => { setGenerateAppModal(false) setOpenApiModal(false) + setOpenApi("") + setOpenApiError("") + setAppValidation("") + setValidation(false) }} sx={{ color: 'rgba(255,255,255,0.7)', @@ -668,9 +690,9 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { Paste in a URL, and we will make it into an app for you. @@ -689,18 +711,15 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { color: 'white', '& fieldset': { borderWidth: '1px', - borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1', - borderImageSlice: 1, + borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1", }, '&:hover fieldset': { borderWidth: '1px', - borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1', - borderImageSlice: 1, + borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1", }, '&.Mui-focused fieldset': { borderWidth: '2px', - borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1', - borderImageSlice: 1, + borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1", } }, '& .MuiOutlinedInput-input::placeholder': { @@ -727,6 +746,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { { {circularLoader} { !validation && - + This may take multiple minutes based on the size of the documentation. } diff --git a/frontend/src/components/AppModal.jsx b/frontend/src/components/AppModal.jsx index 4fcc5d07..fce90fae 100644 --- a/frontend/src/components/AppModal.jsx +++ b/frontend/src/components/AppModal.jsx @@ -22,8 +22,8 @@ import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import LaunchIcon from '@mui/icons-material/Launch'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { CloudDownloadOutlined } from '@mui/icons-material'; -import { findSpecificApp } from './AppFramework'; -import theme from "../theme"; +import { findSpecificApp } from '../components/AppFramework.jsx'; +import theme from "../theme.jsx"; import YAML from 'yaml'; import { toast } from 'react-toastify'; import { Link } from 'react-router-dom'; @@ -433,7 +433,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { fontFamily: theme?.typography?.fontFamily }} > - + About {app?.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())} { boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.2)" }} /> -
+
{newAppname} - { - isCloud && ( - - - - - - ) - } + + + + +
{ if (canEditApp) { const editUrl = "/apps/edit/" + (app?.id || app?.objectID); navigate(editUrl) - }else{ + } else { const forkUrl = "/apps/new?id=" + (app?.id || app?.objectID); navigate(forkUrl) } @@ -735,7 +732,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { bgcolor: '#FF8544', '&:hover': { bgcolor: '#FF8544' }, textTransform: 'none', - borderRadius: 1, + borderRadius: "4px", py: 1, px: 7, fontSize: "14px", diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index e9539eeb..73528ec2 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -466,14 +466,16 @@ const AppSelection = props => { fontSize: 16, color: "rgba(158, 158, 158, 1)", fontFamily: theme?.typography?.fontFamily, + lineHeight: "24px" }} > - Your organization has no apps yet, select your starting apps here - or discover more apps using the { - navigate("/apps2?tab=all_apps") + navigate("/apps?tab=all_apps") }} - style={{ color: "#FF8444", fontWeight: "medium", fontSize: 16, cursor:"pointer" }}>App Library + style={{ color: "#FF8444", fontWeight: "medium", fontSize: 16, cursor:"pointer", marginLeft: 6 }}>App Library
) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index c1ce38a3..1bafe19b 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -51,11 +51,13 @@ import BillingStats from "./BillingStats.jsx"; import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" import DeleteIcon from '@mui/icons-material/Delete'; import { Context } from "../context/ContextApi.jsx"; +import LicencePopup from "./LicencePopup.jsx"; const Billing = memo((props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, clickedFromOrgTab } = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey,isLoaded, selectedOrganization, handleGetOrg, clickedFromOrgTab, removeCookie} = props; //const alert = useAlert(); let navigate = useNavigate(); + const [isLoggedIn, setIsLoggedIn] = useState(false) const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); const [dealList, setDealList] = React.useState([]); const [dealName, setDealName] = React.useState(""); @@ -73,14 +75,17 @@ const Billing = memo((props) => { const [currentAppRunsInNumber, setCurrentAppRunsInNumber] = useState(0); const [alertThresholds, setAlertThresholds] = useState(selectedOrganization.Billing !== undefined && selectedOrganization.Billing.AlertThreshold !== undefined && selectedOrganization.Billing.AlertThreshold !== null ? selectedOrganization.Billing.AlertThreshold : [{ percentage: '', count: '', Email_send: false }]); const [currentIndex, setCurrentIndex] = useState(0); + const [deleteAlertIndex, setDeleteAlertIndex] = useState(-1); const [deleteAlertVerification, setDeleteAlertVerification] = useState(false); - useEffect(() => { if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) { const percentage = (userdata.app_execution_usage / userdata.app_execution_limit) * 100; setCurrentAppRunsInPercentage(Math.round(percentage)); setCurrentAppRunsInNumber(userdata.app_execution_limit - userdata.app_execution_usage); } + if (userdata?.id?.length > 0 && isLoggedIn === false){ + setIsLoggedIn(true) + } }, [userdata]); const [BillingEmail, setBillingEmail] = useState(selectedOrganization?.Billing?.Email); @@ -182,7 +187,8 @@ const Billing = memo((props) => { const isCloud = window.location.host === "localhost:3002" || - window.location.host === "shuffler.io"; + window.location.host === "shuffler.io" || + window.location.host === "sandbox.shuffler.io"; billingInfo.subscription = { "active": true, @@ -419,8 +425,16 @@ const Billing = memo((props) => { const data = { org_id: orgId, email: newBillingEmail, + name: selectedOrganization.name, + description: selectedOrganization.description, + image: selectedOrganization.image, + defaults: selectedOrganization.defaults, + sso_config: selectedOrganization.sso_config, + mfa_required: selectedOrganization.mfa_required, billing: { email: newBillingEmail, + AlertThreshold: selectedOrganization?.Billing?.AlertThreshold, + Consultation: selectedOrganization?.Billing?.Consultation, }, }; @@ -747,13 +761,21 @@ const Billing = memo((props) => { open={openChangeEmailBox} onClose={handleCloseChangeEmailBox} PaperProps={{ - style: { - padding: '20px', - borderRadius: '8px', - maxWidth: '500px', - width: '100%' + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + minWidth: '440px', + 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, + }, } - }} + }} > Change Billing Email @@ -810,7 +832,7 @@ const Billing = memo((props) => { color="primary" style={{ marginTop: !userdata.has_card_available ? 20 : 10, - borderRadius: 8, + borderRadius: 4, height: 40, fontSize: 16, color: userdata.has_card_available ? "#ff8544" : "#1a1a1a", @@ -843,7 +865,7 @@ const Billing = memo((props) => { color="primary" style={{ marginTop: 10, - borderRadius: 8, + borderRadius: 4, height: 40, fontSize: 16, color: "#1a1a1a", @@ -933,11 +955,19 @@ const Billing = memo((props) => { const url = `${globalUrl}/api/v1/orgs/${selectedOrganization.id}`; const data = { org_id: selectedOrganization.id, + name: selectedOrganization.name, + description: selectedOrganization.description, + image: selectedOrganization.image, + defaults: selectedOrganization.defaults, + sso_config: selectedOrganization.sso_config, + mfa_required: selectedOrganization.mfa_required, Billing: { Consultation: { hours: String(inputHour), minutes: String(inputMinutes), }, + AlertThreshold: selectedOrganization.Billing.AlertThreshold, + email: selectedOrganization.Billing.email, } }; @@ -1030,9 +1060,9 @@ const Billing = memo((props) => {
{ color="primary" style={{ marginTop: userdata.support ? 0 : 10, - borderRadius: 8, + borderRadius: 4, height: 40, fontSize: 16, color: "#1A1A1A", @@ -1142,8 +1172,23 @@ const Billing = memo((props) => { > Buy - { setClickOnBuy(false) }} PaperProps={{style: {backgroundColor: "rgb(26, 26, 26)"}}}> - + { setClickOnBuy(false) }} PaperProps={{ + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + minWidth: '440px', + 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, + }, + } + }}> + You will be taken to Stripe to book professional service hours. You can adjust the number of hours on the left side of the Stripe page. @@ -1178,10 +1223,9 @@ const Billing = memo((props) => { color="primary" style={{ marginTop: userdata.support ? 5 : 10, - borderRadius: 8, + borderRadius: 4, height: 40, fontSize: 16, - color: "#ff8544", textTransform: 'none', cursor: getProfessionalServices ? 'pointer' : 'not-allowed', opacity: getProfessionalServices ? 1 : 0.6, @@ -1202,13 +1246,23 @@ const Billing = memo((props) => { fullWidth style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }} PaperProps={{ - style: { - width: 500, - margin: 0, + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + minWidth: '440px', + 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, + }, } - }} + }} > - + Upgrade Consultation Plan @@ -1244,7 +1298,7 @@ const Billing = memo((props) => {
-
+
- ) - } - setDeleteAlertVerification(false)} sx={{ '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)', }, }}> - Are you sure you want to delete this threshold? - - - - - -
+ + {alertThresholds[index].Email_send === true && ( + + + + )} + + {alertThresholds.length > 1 && ( + + )} + setDeleteAlertVerification(false)} + sx={{ + '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)' }, + }} + > + Are you sure you want to delete this threshold? + + + + + +
+ + ))}
@@ -2449,10 +2575,8 @@ const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => { : "auto", padding: "27px 10px 19px 27px", backgroundColor: '#212121', - borderRadius: '16px', height: '100%', boxSizing: 'border-box', - borderLeft: '1px solid #494949', overflow: 'hidden', maxHeight: "1700px", overflowY: "auto",scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }), [clickedFromOrgTab]); diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx index 8b5d4a1c..231b5686 100644 --- a/frontend/src/components/BillingStats.jsx +++ b/frontend/src/components/BillingStats.jsx @@ -648,12 +648,13 @@ const AppStats = (defaultprops) => { All shown statistics are gathered from Your Organisation Statistics. It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. The billing tracker is in Beta, and is always calculated manually before being invoiced.
-
+
+
{filteredStatistics !== undefined ?
{
: null} +
+ {clickedFromOrgTab? ( -
-
+
+
+ + Search from + } + slotProps={{ + textField: { + sx: { + "& .MuiOutlinedInput-root": { + "& fieldset": { + borderColor: "#494949 !important", + borderWidth: "1px !important", + }, + "&:hover fieldset": { + borderColor: "#FFFFFF !important", + }, + "&.Mui-focused fieldset": { + borderColor: "#FFFFFF !important", + }, + height: "35px", + fontSize: 16, + color: "#c8c8c8", + }, + }, + }, + }} + sx={{ + "& .MuiInputBase-root": { + height: "35px", + minHeight: "35px", + }, + "& .MuiInputBase-input": { + height: "35px", + padding: "0 14px", + boxSizing: "border-box", + color: "#c8c8c8", + fontSize: 16, + } + }} + ampm={false} + format="YYYY-MM-DD HH:mm:ss" + value={startTime} + onChange={handleStartTimeChange} /> -
-
+
+
+ + Search until + } + "& .MuiInputBase-root": { + height: "35px", + minHeight: "35px", + }, + "& .MuiInputBase-input": { + height: "35px", + padding: "0 14px", + boxSizing: "border-box", + color: "#c8c8c8", + fontSize: 16, + } + }} + ampm={false} + format="YYYY-MM-DD HH:mm:ss" + value={endTime} + onChange={handleEndTimeChange} /> -
+
- + ):(
@@ -794,8 +890,6 @@ const AppStats = (defaultprops) => {
)} - -
{appRuns === undefined ? diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 4e8c6c24..7929a7ab 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -303,7 +303,7 @@ const CacheView = memo((props) => { Key {
{
@@ -711,7 +711,7 @@ const Files = memo((props) => { }} /> @@ -1278,7 +1279,7 @@ useEffect(() => { }} disabled={userdata?.support === false} > - + • { display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", + fontSize: 18, color: userdata?.support && currentOpenTab === "detection" && currentPath.includes("/detection") ? "#F1F1F1" @@ -1329,7 +1331,7 @@ useEffect(() => { cursor: userdata?.support ? "pointer" : "not-allowed", }} > - + • { display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", + fontSize: 18, color: userdata?.support && currentOpenTab === "response" && currentPath.includes("/response") ? "#F1F1F1" @@ -1380,7 +1383,7 @@ useEffect(() => { cursor: userdata?.support ? "pointer" : "not-allowed", }} > - + • { display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", + fontSize: 18, color: userdata?.support && currentOpenTab === "response" && currentPath.includes("/response") ? "#F1F1F1" @@ -1458,7 +1462,7 @@ useEffect(() => { { }, transition: "opacity 0.3s ease", textTransform: "none", - fontSize: 16, + fontSize: 18, }} disableElevation disableRipple @@ -1718,13 +1722,14 @@ useEffect(() => { style={{ color: "#CDCDCD", margin: "0 5px", - fontSize: 16, + fontSize: 18, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: 150, textAlign: "center", marginLeft: 10, + }} > {userdata?.username} diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index 883f156d..b8d009a3 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -24,6 +24,7 @@ import { DialogActions, CardContent, ButtonGroup, + DialogContentText, } from "@mui/material"; import { useNavigate, Link } from "react-router-dom"; @@ -42,19 +43,17 @@ import { import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" +import Billing from "./Billing.jsx"; const LicencePopup = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile } = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile, selectedOrganization, isCloud } = props; //const alert = useAlert(); let navigate = useNavigate(); - const isCloud = typeof window === 'undefined' || window === undefined || window.location === undefined ? true : window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); const [dealList, setDealList] = React.useState([]); const [dealName, setDealName] = React.useState(""); const [dealAddress, setDealAddress] = React.useState(""); const [dealType, setDealType] = React.useState("MSSP"); - const [selectedOrganization, setSelectedOrganization] = React.useState({}); const [dealCountry, setDealCountry] = React.useState("United States"); const [dealCurrency, setDealCurrency] = React.useState("USD"); const [dealStatus, setDealStatus] = React.useState("initiated"); @@ -63,8 +62,8 @@ const LicencePopup = (props) => { const [dealerror, setDealerror] = React.useState(""); const [variant, setVariant] = useState(0) const [shuffleVariant, setShuffleVariant] = useState(isCloud ? 0 : 1) - - + const [BillingEmail, setBillingEmail] = useState(selectedOrganization?.Billing?.Email); + const [openChangeEmailBox, setOpenChangeEmailBox] = useState(false); // const parsedFields = maxFields === undefined ? 300 : maxFields const initialShuffleVariant = isCloud ? 0 : 1; const [paymentType, setPaymentType] = useState(0) @@ -78,6 +77,12 @@ const LicencePopup = (props) => { const [calculatedCost, setCalculatedCost] = useState("$600") const [selectedValue, setSelectedValue] = useState(100) + useEffect(() => { + if(selectedOrganization?.Billing?.Email !== BillingEmail) { + setBillingEmail(selectedOrganization?.Billing?.Email); + } + }, [selectedOrganization]) + // Onprem const [calculatedCores, setCalculatedCores] = useState('600') const [onpremSelectedValue, setOnpremSelectedValue] = useState(8) @@ -88,7 +93,7 @@ const LicencePopup = (props) => { const paperStyle = { padding: 20, borderRadius: theme.palette?.borderRadius, - height: "100%", + height: "100%" } billingInfo.subscription = { @@ -101,7 +106,8 @@ const LicencePopup = (props) => { "description": "Pay as you go", "features": [ "Basic Support", - "Limited App Runs (10.000)", + "Includes 10.000 app run/month for free. ", + "Pay for what you use with no minimum commitment and cancel anytime." ], "limit": 10000, } @@ -137,13 +143,14 @@ const LicencePopup = (props) => { }) } + const SubscriptionObject = (props) => { const { globalUrl, index, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, highlight, } = props; const [signatureOpen, setSignatureOpen] = React.useState(false); const [tosChecked, setTosChecked] = React.useState(subscription.eula_signed) const [hovered, setHovered] = React.useState(false) - + const [newBillingEmail, setNewBillingEmail] = useState(''); var top_text = "Base Cloud Access" if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) { subscription.name = "Enterprise" @@ -191,6 +198,58 @@ const LicencePopup = (props) => { // newPaperstyle.border = "1px solid #f85a3e" } + const handleClickOpen = () => { + setOpenChangeEmailBox(true); + }; + + const HandleChangeBillingEmail = (orgId) => { + const email = newBillingEmail; + const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; + if (!emailPattern.test(email)) { + toast("Please enter a valid email address"); + return; + } else { + setNewBillingEmail(email); + } + + toast("Updating billing email. Please Wait") + + const data = { + org_id: orgId, + email: newBillingEmail, + billing: { + email: newBillingEmail, + }, + }; + + const url = `${globalUrl}/api/v1/orgs/${orgId}/billing`; + fetch(url, { + method: "POST", + body: JSON.stringify(data), + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.status !== 200) { + console.log("Bad status code in get org:", response.status); + } + return response.json(); + }).then((responseJson) => { + if (responseJson.success === true) { + toast.success("Successfully updated billing email"); + setBillingEmail(newBillingEmail); + setOpenChangeEmailBox(false); + } else { + toast.error("Failed to update billing email. Please try again."); + } + }) + .catch((error) => { + console.log("Error getting org:", error); + }); + } + return ( { color="primary" /> : null} - + {top_text} @@ -343,7 +402,7 @@ const LicencePopup = (props) => {
- + {subscription.name} @@ -443,21 +502,142 @@ const LicencePopup = (props) => { }) : null} - {isCloud ? - Billing email: {selectedOrganization.org} + + {subscription.name.includes("Scale") ? + "" + : + + userdata.has_card_available === true ? + "While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month." + : + isCloud ? + `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` + : + `You are not subscribed to any plan and are using the free, open source plan. This plan has no enforced limits, but scale issues may occur due to CPU congestion.` + } + + {isCloud && (userdata.has_card_available === true || selectedOrganization?.Billing?.Email?.length > 0 )? +
+ Billing email: {BillingEmail} + + {setOpenChangeEmailBox(false)}} + PaperProps={{ + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + minWidth: '440px', + 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, + }, + '& .MuiDialogActions-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + } + }} + > + Change Billing Email + + + Enter the new billing email address. + + { if (event.key === 'Enter') HandleChangeBillingEmail(selectedOrganization.id) }} + onChange={(e) => setNewBillingEmail(e.target.value)} + /> + + + + + + +
: null}
+ {isCloud ? ( + ) : null} + - - + {/*
@@ -547,14 +726,13 @@ const LicencePopup = (props) => { } const handleChange = (event, newValue) => { - console.log("Event, value: ", event.target, newValue) if (shuffleVariant === 1) { setSelectedValue(newValue) if (newValue === 32) { setCalculatedCost(`Get A Quote`) } else { - setCalculatedCost(`$${newValue * 75}`) + setCalculatedCost(`$${newValue * 120}`) } } else { setSelectedValue(newValue) @@ -736,7 +914,7 @@ const LicencePopup = (props) => { return (
- + {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? {
- - - - - + +
+
+ + + +
) } diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index abdbcafd..e193460c 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -141,6 +141,7 @@ const Header = (props) => { const [subAnchorEl, setSubAnchorEl] = React.useState(null); const [upgradeHovered, setUpgradeHovered] = React.useState(false); const [showTopbar, setShowTopbar] = useState(false) // Set to true to show top bar + const [selectedOrganization, setSelectedOrganization] = useState({}); const stripeKey = typeof window === 'undefined' || window.location === undefined ? "" : window.location.origin === "https://shuffler.io" ? "pk_live_51PXYYMEJjT17t98N20qEqItyt1fLQjrnn41lPeG2PjnSlZHTDNKHuisAbW00s4KAn86nGuqB9uSVU4ds8MutbnMU00DPXpZ8ZD" : "pk_test_51PXYYMEJjT17t98NbDkojZ3DRvsFUQBs35LGMx3i436BXwEBVFKB9nCvHt0Q3M4MG3dz4mHheuWvfoYvpaL3GmsG00k1Rb2ksO" let navigate = useNavigate(); const classes = useStyles(); @@ -222,10 +223,45 @@ const Header = (props) => { window.location.host === "shuffler.io" || window.location.host === "localhost:5002"; + const curpath = (typeof window !== "undefined" && window.location && typeof window.location.pathname === "string") ? window.location.pathname : ""; + + const handleGetOrg = (orgId) => { + fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.status === 401) { + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson["success"] === false) { + console.log("Error getting org: ", responseJson); + } else { + setSelectedOrganization(responseJson); + } + }) + .catch((error) => { + console.log("Error getting org: ", error); + }); + }; + + + useEffect(() => { + if (isLoggedIn && userdata?.active_org?.id?.length > 0) { + handleGetOrg(userdata.active_org.id); + } + }, [isLoggedIn]); + // DEBUG HERE const handleClickLogout = () => { console.log("SHOULD LOG OUT"); @@ -441,7 +477,7 @@ const Header = (props) => { }) - + { handleClose(); @@ -537,9 +573,9 @@ const Header = (props) => { isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} - + selectedOrganization={selectedOrganization} billingInfo={billingInfo} - + isCloud={isCloud} userdata={userdata} stripeKey={stripeKey} setModalOpen={setModalOpen} @@ -590,7 +626,7 @@ const Header = (props) => { - + @@ -265,7 +265,7 @@ const Priorities = memo((props) => { {clickedFromOrgTab? null : }

Suggestions

- + Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company.
These range from simple configurations in Shuffle to Usecases you may have missed. 
{ {data.read === false ? : @@ -431,64 +431,119 @@ const NotificationItem = memo((props) => { : null } - + {data.description}
- - - {data.read === false ? ( - - ) : null} - - - - + + - - First seen: {new Date(data.created_at * 1000).toISOString().slice(0, 19)} - + {data.read === false ? ( + + ) : null} - - Last seen: {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} - + + + + + + + First seen:{" "} + {new Date(data.created_at * 1000).toISOString().slice(0, 19)} + + + + Last seen:{" "} + {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} + + + + Times seen: {data.amount} + +
- - Times seen: {data.amount} - - ); }) diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 4d37142f..4bd3a6f2 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -154,7 +154,7 @@ const Priority = (props) => { }
- @@ -2062,6 +2118,7 @@ const Apps2 = (props) => { handleAppClick={handleAppClick} leftSideBarOpenByClick={leftSideBarOpenByClick} userdata={userdata} + fetchApps={fetchApps} /> ))}
@@ -2106,6 +2163,7 @@ const Apps2 = (props) => { {appsToShow.map((data, index) => ( ))} diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index aa5bd735..7c124bf0 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -1,5 +1,5 @@ // React & Core imports -import React, { useEffect, useContext, memo, useState } from "react"; +import React, { useEffect, useContext, memo, useState, useRef } from "react"; import { useLocation, useNavigate, Link } from "react-router-dom"; import ReactDOM from "react-dom" @@ -104,7 +104,10 @@ import { MuiChipsInput } from "mui-chips-input"; import { v4 as uuidv4 } from "uuid"; import theme from "../theme.jsx"; import algoliasearch from 'algoliasearch/lite'; -import { InstantSearch, Configure, connectHits } from 'react-instantsearch-dom'; +import { InstantSearch, Configure, connectHits, connectSearchBox, connectRefinementList } from 'react-instantsearch-dom'; +import { debounce } from "lodash"; +import { removeQuery } from "../components/ScrollToTop.jsx"; + const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240"); @@ -668,6 +671,8 @@ const Workflows2 = (props) => { const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove const [selectedUsecases, setSelectedUsecases] = React.useState([]); const [filteredWorkflows, setFilteredWorkflows] = React.useState([]); + const [orgWorkflows, setOrgWorkflows] = React.useState([]); + const [myWorkflows, setMyWorkflows] = React.useState([]); const [selectedWorkflow, setSelectedWorkflow] = React.useState({}); const [workflowDone, setWorkflowDone] = React.useState(false); const [selectedWorkflowId, setSelectedWorkflowId] = React.useState(""); @@ -716,18 +721,47 @@ const Workflows2 = (props) => { const [apps, setApps] = React.useState([]); document.title = "Shuffle - Workflows"; + + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const tabParam = queryParams.get('tab'); + if (tabParam !== null && tabParam !== undefined) { + if (tabParam === 'org_workflows' && currTab !== 0) { + setCurrTab(0); + } else if (tabParam === 'my_workflows' && currTab !== 1) { + setCurrTab(1); + } else if (tabParam === 'all_workflows' && currTab !== 2) { + setCurrTab(2); + } + } + }, [location.search]); + const handleTabChange = (event, newValue) => { - // Set loading when switching to public workflows tab - if (newValue === 2) { + setCurrTab(newValue); + // Update URL query params based on tab index + const tabMapping = { + 0: 'org_workflows', + 1: 'my_workflows', + 2: 'all_workflows' + }; + const queryParams = new URLSearchParams(location.search); + queryParams.set('tab', tabMapping[newValue]); + + + navigate(`${location.pathname}?${queryParams.toString()}`); + }; + + useEffect(() => { + if (currTab === 2) { setIsLoadingPublicWorkflow(true); // Simulate loading time for the Algolia search results setTimeout(() => { setIsLoadingPublicWorkflow(false); - }, 2000); + }, 2500); } + }, [currTab]) + - setCurrTab(newValue); - }; const handleCreateWorkflow = () => { setModalOpen(true) @@ -1203,6 +1237,7 @@ const Workflows2 = (props) => { }, [isDropzone]); + const getFramework = () => { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { method: "GET", @@ -1240,6 +1275,7 @@ const Workflows2 = (props) => { const getAvailableWorkflows = (amount) => { var storageWorkflows = [] + setIsLoadingWorkflow(true) try { const storagewf = localStorage.getItem("workflows") storageWorkflows = JSON.parse(storagewf) @@ -1283,8 +1319,15 @@ const Workflows2 = (props) => { return response.json(); }) .then((responseJson) => { - console.log("Response : /api/v1/workflows", responseJson) - if (responseJson !== undefined) { + if (responseJson !== undefined && responseJson !== null) { + if (responseJson.success === false) { + } else if (responseJson.length === 0) { + if (currTab !== 2) { + toast("No workflows found. Showing workflow discovery") + setCurrTab(2) + } + } + var newarray = [] for (var wfkey in responseJson) { const wf = responseJson[wfkey] @@ -1527,13 +1570,13 @@ const Workflows2 = (props) => { overflow: "hidden", width: "100%", color: "white", - padding: "12px 12px 0px 15px", display: "flex", fontFamily: theme?.typography?.fontFamily, boxSizing: "border-box", position: "relative", borderRadius: "8px", // backgroundColor: "#212121", + padding: "15px 15px 0px 20px", }; const gridContainer = { @@ -2169,7 +2212,6 @@ const Workflows2 = (props) => { ); }; - // Replace the loading sections in the main component with this const LoadingWorkflowGrid = () => { return (
{ if ( parsedName !== undefined && parsedName !== null && - parsedName.length > 20 + parsedName.length > 35 ) { - parsedName = parsedName.slice(0, 21) + ".."; + parsedName = parsedName.slice(0, 34) + ".."; } const actions = data.actions !== null ? data.actions.length : 0; @@ -2442,7 +2484,7 @@ const Workflows2 = (props) => { } return ( -
+
{selectedCategory !== "" ? @@ -3906,59 +3948,25 @@ const Workflows2 = (props) => { // } useEffect(() => { - if (userdata !== undefined && userdata !== null && currTab !== 2) { - setIsLoadingWorkflow(true); - var filteredWorkflows = [] + if (currTab === 2) return; + if (userdata !== undefined && userdata !== null && filteredWorkflows.length > 0) { + var categoryWorkflows = [] if (currTab === 0) { - filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id) + categoryWorkflows = filteredWorkflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id) + setOrgWorkflows(categoryWorkflows) } else if (currTab === 1) { - filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id) + categoryWorkflows = filteredWorkflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id) + setMyWorkflows(categoryWorkflows) } - setFilteredWorkflows(filteredWorkflows) - setTimeout(() => { - setIsLoadingWorkflow(false); - }, 500); + setIsLoadingWorkflow(false); } - }, [currTab, workflows, userdata]) + }, [currTab, workflows, userdata, filteredWorkflows]) - useEffect(() => { - console.log("SearchQuery: ", searchQuery) - }, [searchQuery]) - const Hits = ({ hits }) => { - var counted = 0 - console.log("Public workflows", hits) - return ( - isLoadingPublicWorkflow ? - ( - - ) : ( - -
- {hits.map((data, index) => { - return - })} -
- ) - - ) - } - - const CustomHits = connectHits(Hits) const handleCategoryChange = (e) => { @@ -3972,13 +3980,255 @@ const Workflows2 = (props) => { borderRadius: '4px', padding: "12px 16px", cursor: 'pointer', - minWidth: '40px', + minWidth: '40px', height: 'auto', display: 'flex', alignItems: 'center', justifyContent: 'center', } + + const iconButtonDisabledStyle = { + ...iconButtonStyle, + cursor: 'not-allowed', + opacity: 0.9, // Reduce opacity + } + + const Hits = ({ hits, isSearchStalled, searchQuery }) => { + + return ( + isLoadingPublicWorkflow ? ( + + ) : ( +
+ {hits.map((data, index) => { + return + })} +
+ ) + ) + }; + + const SearchBox = ({ refine, searchQuery, setSearchQuery }) => { + + const inputRef = useRef(null); + const [localQuery, setLocalQuery] = useState(searchQuery); + const location = useLocation(); + + + useEffect(() => { + if (searchQuery) { + setLocalQuery(searchQuery); + refine(searchQuery); + } + }, [searchQuery, refine]); + + // Debounced function to refine search + const debouncedRefine = useRef( + debounce((value) => { + setSearchQuery(value); + removeQuery("q"); + refine(value); + }, 300) + ).current; + + useEffect(() => { + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + const foundQuery = params["q"]; + if (foundQuery) { + setLocalQuery(foundQuery); + debouncedRefine(foundQuery); + } + }, [debouncedRefine]); + + useEffect(() => { + if (searchQuery === "") { + return; + } + if (inputRef.current) { + inputRef.current.focus(); + } + }, [searchQuery]); + + const handleChange = (event) => { + const value = event.target.value; + setLocalQuery(value); + debouncedRefine(value); + }; + + return ( + { + if (event.key === "Enter") { + event.preventDefault(); + } + }} + limit={5} + variant="outlined" + placeholder="Search Workflows" + /> + ) + } + + const CustomSearchBox = connectSearchBox(SearchBox) + const CustomHits = connectHits(Hits) + + const CategoryDropdown = ({ refine, currentRefinement, items }) => { + + const handleChange = (event) => { + // Get the selected values array from the event + const selectedValues = event.target.value; + refine(selectedValues); + }; + + return ( + + ); + }; + const CustomCategoryDropdown = connectRefinementList(CategoryDropdown) + + const tabStyle = { + textTransform: 'none', + marginRight: 20, + fontFamily: theme?.typography?.fontFamily, + fontSize: 16, + borderBottom: "5px solid transparent", + minHeight: "48px", + padding: "12px 16px", + } + + const tabActive = { + borderBottom: "5px solid #FF8544", + borderRadius: "2px", + color: "#FF8544" + } + const WorkflowView = memo(() => { if (workflows.length === 0) { @@ -3990,363 +4240,443 @@ const Workflows2 = (props) => { const foundPriority = userdata === undefined || userdata === null || userdata.priorities === undefined || userdata.priorities === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) return ( <> -
- - Workflows - + +
+ + {currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Workflows + -
- - - - - -
- - -
- - - - // - // - // ), - onKeyDown: (e) => { - // Prevent default behavior for Enter and Backspace - if (e.key === 'Enter' || e.key === 'Backspace') { - e.preventDefault(); - e.stopPropagation(); - e.target.focus(); - } - }, - }} - clearInputOnBlur={false} - sx={{ - // Container styling - '& .MuiOutlinedInput-root': { - height: "fit-content", - borderRadius: '4px', - backgroundColor: '#212121', - '& fieldset': { - borderColor: 'rgba(255, 255, 255, 0.23)', - }, - '&:hover fieldset': { - borderColor: 'rgba(255, 255, 255, 0.4)', - }, - }, - - // Adjust chip container to center vertically - '& .MuiInputBase-root': { - display: 'flex', - flexWrap: 'wrap', - gap: '4px', - padding: '4px 8px', - alignItems: 'center', - height: "fit-content", // Match height - }, - - // Rest of the styling remains the same... - }} - value={filters} - onChange={(chips) => { - setFilters(chips); - findWorkflow(chips); - }} - //onAdd={(chip) => { - // console.log("ADd: ", chip); - // addFilter(chip); - //}} - //onDelete={(_, index) => { - // console.log("Remove: ", index); - // removeFilter(index); - //}} - /> - - - - -
-
- - navigate("/workflows/debug")} - disabled={currTab === 2} - > - - - - - - { - const newView = view === "grid" ? "list" : "grid"; - localStorage.setItem("workflowView", newView); - setView(newView); - }} - disabled={currTab === 2} - > - {view === "grid" ? : } - - - - - upload.click()} - disabled={currTab === 2} - > - {submitLoading ? : } - - - - (upload = ref)} - onChange={importFiles} - /> - - - exportAllWorkflows(workflows)} - > - - - -
- + + + +
-
-
- { - isLoadingWorkflow ? ( - +
+ + {currTab === 2 ? ( + ) : ( - view === "grid" && currTab !== 2 ? ( - <> -
+ + // + // + // ), + onKeyDown: (e) => { + // Prevent default behavior for Enter and Backspace + if (e.key === 'Enter' || e.key === 'Backspace') { + e.preventDefault(); + e.stopPropagation(); + e.target.focus(); + } + }, + }} + clearInputOnBlur={false} + sx={{ + // Container styling + '& .MuiOutlinedInput-root': { + height: "fit-content", + borderRadius: '4px', + backgroundColor: '#212121', + '& fieldset': { + borderColor: 'rgba(255, 255, 255, 0.23)', + }, + '&:hover fieldset': { + borderColor: 'rgba(255, 255, 255, 0.4)', + }, + }, + // Adjust chip container to center vertically + '& .MuiInputBase-root': { + display: 'flex', + flexWrap: 'wrap', + gap: '4px', + padding: '4px 8px', + alignItems: 'center', + height: "fit-content", // Match height + backgroundColor: "#212121", + }, + // Rest of the styling remains the same... + }} + value={filters} + onChange={(chips) => { + setFilters(chips); + const remainingCategories = chips.map(chip => { + const match = chip.match(/\d+\.\s+(\w+)/i); + return match ? match[1] : chip; + }).filter(category => { + return usecases.some(usecase => + usecase.name.toLowerCase().includes(category.toLowerCase()) + ); + }); - {filteredWorkflows.map((data, index) => { - // Shouldn't be a part of this list - if (data.public === true) { - return null - } + setSelectedCategory(remainingCategories); + findWorkflow(chips); - if (firstLoad) { - workflowDelay += 75 - } else { - return - } + }} + //onAdd={(chip) => { + // console.log("ADd: ", chip); + // addFilter(chip); + //}} + //onDelete={(_, index) => { + // console.log("Remove: ", index); + // removeFilter(index); + //}} + /> + )} - return ( - - {/**/} - - {/**/} - - ) - })} -
- - ) : ( - currTab !== 2 && + { + currTab !== 2 && ( + ) - ) - } + } + { + currTab === 2 && ( + + ) + } - { - currTab === 2 && - ( - - - - - ) - } +
+
+ + navigate("/workflows/debug")} + disabled={currTab === 2} + > + + + + + { + const newView = view === "grid" ? "list" : "grid"; + localStorage.setItem("workflowView", newView); + setView(newView); + }} + disabled={currTab === 2} + > + {view === "grid" ? + : + + } + + + + + upload.click()} + disabled={currTab === 2} + > + {submitLoading ? + : + + } + + + + (upload = ref)} + onChange={importFiles} + /> + + + exportAllWorkflows(workflows)} + > + + + +
+ +
+ + +
+
+ { + (isLoadingWorkflow && currTab !== 2) ? ( + + ) : ( + view === "grid" && currTab !== 2 ? ( + <> +
+ + + + {currTab === 0 && orgWorkflows.map((data, index) => { + // Shouldn't be a part of this list + if (data.public === true) { + return null + } + + // if (firstLoad) { + // workflowDelay += 75 + // } else { + // return + // } + + return ( + + {/**/} + + {/**/} + + ) + })} + + { + currTab === 1 && myWorkflows.map((data, index) => { + if (data.public === true) { + return null + } + + // if (firstLoad) { + // workflowDelay += 75 + // } else { + // return + // } + + return ( + + {/**/} + + {/**/} + + ) + }) + } +
+ + ) : ( + currTab !== 2 && + ) + ) + } + + { + currTab === 2 && + ( + + ) + } + +
-
- + +
); @@ -4756,7 +5086,7 @@ const Workflows2 = (props) => { ); // Maybe use gridview or something, idk - return
{loadedCheck}
; + return
{loadedCheck}
; };