diff --git a/frontend/src/components/AuthenticationItem.jsx b/frontend/src/components/AuthenticationItem.jsx
index 593eacb4..eae08e69 100644
--- a/frontend/src/components/AuthenticationItem.jsx
+++ b/frontend/src/components/AuthenticationItem.jsx
@@ -18,7 +18,6 @@ import {
Grid,
Paper,
Typography,
- TextField,
Zoom,
} from "@mui/material";
diff --git a/frontend/src/components/AuthenticationWindow.jsx b/frontend/src/components/AuthenticationWindow.jsx
index d7bcc55a..7d32f597 100755
--- a/frontend/src/components/AuthenticationWindow.jsx
+++ b/frontend/src/components/AuthenticationWindow.jsx
@@ -294,9 +294,6 @@ const AuthenticationData = (props) => {
InputProps={{
style: {
color: "white",
- marginLeft: "5px",
- maxWidth: "95%",
- height: 50,
fontSize: "1em",
},
disableUnderline: true,
@@ -386,7 +383,6 @@ const AuthenticationData = (props) => {
PaperProps={{
style: {
pointerEvents: "auto",
- backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: 600,
minHeight: 600,
@@ -441,9 +437,6 @@ const AuthenticationData = (props) => {
InputProps={{
style: {
color: "white",
- marginLeft: "5px",
- maxWidth: "95%",
- height: 50,
fontSize: "1em",
},
}}
diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx
index 71d01789..0b9c484d 100644
--- a/frontend/src/components/Billing.jsx
+++ b/frontend/src/components/Billing.jsx
@@ -1,316 +1,1010 @@
-import React, { useState, useEffect } from "react";
-import theme from "../theme.jsx";
-import ReactGA from 'react-ga4';
-
-import {
- Paper,
- Typography,
- Divider,
- Button,
- Grid,
- Card,
-} from "@mui/material";
-
-import { useAlert } from "react-alert";
-import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
-
-const Billing = (props) => {
- const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props;
- console.log("Billing: ", billingInfo);
- const alert = useAlert();
-
- const stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : ""
- console.log("Stripe: ", stripe)
-
- const paperStyle = {
- padding: 20,
- height: "100%",
- width: "100%",
- backgroundColor: theme.palette.surfaceColor,
- border: "1px solid rgba(255,255,255,0.3)",
- marginRight: 10,
- }
-
- const isCloud =
- window.location.host === "localhost:3002" ||
- window.location.host === "shuffler.io";
-
- billingInfo.subscription = {
- "active": true,
- "name": "Pay as you go",
- "price": typecost_single,
- "currency": "USD",
- "currency_text": "$",
- "interval": "app run / month",
- "description": "Pay as you go",
- "features": [
- "Includes 10.000 app run/month for free. ",
- "Pay for what you use with no minimum commitment and cancel anytime.",
- ],
- "limit": 10000,
- }
-
-
- const handleStripeRedirect = () => {
- //var priceItem = "price_1MRNF1DzMUgUjxHSfFTUb2Xh"
- if (stripe == "") {
- console.log("Stripe not loaded")
- return
- }
-
- var priceItem = "price_1MROFrDzMUgUjxHShcSxgHO1"
-
- const successUrl = `${window.location.origin}/admin?admin_tab=billing&payment=success`
- const failUrl = `${window.location.origin}/admin?admin_tab=billing&payment=failure`
- var checkoutObject = {
- lineItems: [
- {
- price: priceItem,
- quantity: 1
- },
- ],
- mode: "subscription",
- billingAddressCollection: "auto",
- successUrl: successUrl,
- cancelUrl: failUrl,
- clientReferenceId: props.userdata.active_org.id,
- }
- //submitType: "donate",
-
- stripe.redirectToCheckout(checkoutObject)
- .then(function (result) {
- console.log("SUCCESS STRIPE?: ", result)
-
- ReactGA.event({
- category: "pricing",
- action: "add_card_success",
- label: "",
- })
- })
- .catch(function(error) {
- console.error("STRIPE ERROR: ", error)
-
- ReactGA.event({
- category: "pricing",
- action: "add_card_error",
- label: "",
- })
- });
- }
-
- const cancelSubscriptions = (subscription_id) => {
- const orgId = selectedOrganization.id;
- const data = {
- subscription_id: subscription_id,
- action: "cancel",
- org_id: selectedOrganization.id,
- };
-
- const url = globalUrl + `/api/v1/orgs/${orgId}/cancel`;
- fetch(url, {
- mode: "cors",
- method: "POST",
- body: JSON.stringify(data),
- credentials: "include",
- crossDomain: true,
- withCredentials: true,
- headers: {
- "Content-Type": "application/json; charset=utf-8",
- },
- })
- .then(function (response) {
- if (response.status !== 200) {
- console.log("Error in response");
- }
-
- if (handleGetOrg != undefined) {
- handleGetOrg(selectedOrganization.id);
- }
-
- return response.json();
- })
- .then(function (responseJson) {
- if (responseJson.success !== undefined && responseJson.success) {
- alert.success("Successfully stopped subscription!");
- } else {
- alert.error("Failed stopping subscription. Please contact us.");
- }
- })
- .catch(function (error) {
- console.log("Error: ", error);
- alert.error("Failed stopping subscription. Please contact us.");
- });
- };
-
- const SubscriptionObject = (props) => {
- const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, } = props;
-
- console.log("Sub: ", subscription)
- var top_text = "Base Access"
- if (subscription.limit === undefined && subscription.level !== undefined) {
-
- subscription.name = "Enterprise"
- subscription.currency_text = "$"
- subscription.price = subscription.level*180
- subscription.limit = subscription.level*100000
- subscription.interval = subscription.recurrence
- subscription.features = [
- "Includes " + subscription.limit + " app runs/month. ",
- "Multi-Tenancy and Region-Selection",
- "And all other features from /pricing",
- ]
- }
-
- if (subscription.name === "Enterprise" && subscription.active === true) {
- top_text = "Current Plan"
- }
-
- return (
-
-
-
- {top_text}
-
-
-
-
-
- {subscription.name}
-
-
-
- {subscription.currency_text}{subscription.price}
-
-
- / {subscription.interval}
-
-
-
- Features
-
-
- {subscription.features !== undefined && subscription.features !== null ?
- subscription.features.map((feature, index) => {
- return (
-
-
- {feature}
-
-
- )
- })
- : null}
-
-
- {/*subscription.name === "Pay as you go" && subscription.limit <= 10000 ?
-
-
- You are not subscribed to any plan and are using the free plan with max 10,000 apps per month. Activate billing to de-activate this limit.
-
- {
- handleStripeRedirect()
- }}
- >
- Activate Billing
-
-
- : null*/}
-
- )
- }
-
-
- return (
-
-
- Billing
-
-
- We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below.
-
-
- {billingInfo.subscription !== undefined && billingInfo.subscription !== null ?
-
- : null}
- {isCloud &&
- selectedOrganization.subscriptions !== undefined &&
- selectedOrganization.subscriptions !== null &&
- selectedOrganization.subscriptions.length > 0 ?
-
- selectedOrganization.subscriptions
- .reverse()
- .map((sub, index) => {
- return (
-
- )
- })
- : null}
- {/*
-
-
- Quantity : {sub.level}
-
- Recurrence : {sub.recurrence}
-
- {sub.active ? (
-
-
Started :{" "}
- {new Date(sub.startdate * 1000).toISOString()}
-
-
{
- cancelSubscriptions(sub.reference);
- }}
- >
- Cancel subscription
-
-
- ) : (
-
-
Cancelled :{" "}
- {new Date(
- sub.cancellationdate * 1000
- ).toISOString()}
-
-
- Status : Deactivated
-
-
- )}
-
-
- */}
-
-
- )
-}
-
-export default Billing;
+import React, { useState, useEffect } from "react";
+import ReactGA from 'react-ga4';
+
+import theme from "../theme.jsx";
+import { useTheme } from "@mui/styles";
+import countries from "../components/Countries.jsx";
+import {
+ Box,
+ Paper,
+ Typography,
+ Divider,
+ Button,
+ Grid,
+ Card,
+ List,
+ ListItemText,
+ ListItem,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ TextField,
+} from "@mui/material";
+
+import { useNavigate, Link } from "react-router-dom";
+import { Autocomplete } from "@mui/material";
+import { toast } from "react-toastify"
+
+import {
+ Cached as CachedIcon,
+} from "@mui/icons-material";
+
+//import { useAlert
+import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
+import BillingStats from "../components/BillingStats.jsx";
+
+const Billing = (props) => {
+ const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props;
+ //const alert = useAlert();
+ let navigate = useNavigate();
+
+ 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 [dealCountry, setDealCountry] = React.useState("United States");
+ const [dealCurrency, setDealCurrency] = React.useState("USD");
+ const [dealStatus, setDealStatus] = React.useState("initiated");
+ const [dealValue, setDealValue] = React.useState("");
+ const [dealDiscount, setDealDiscount] = React.useState("");
+ const [dealerror, setDealerror] = React.useState("");
+
+ const stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : ""
+ const products = [
+ { code: "", label: "MSSP", phone: "" },
+ { code: "", label: "Enterprise", phone: "" },
+ { code: "", label: "Consultancy", phone: "" },
+ { code: "", label: "Support", phone: "" },
+ ];
+
+ const handleGetDeals = (orgId) => {
+ console.log("Get deals!");
+
+ if (orgId.length === 0) {
+ toast(
+ "Organization ID not defined (get deals). Please contact us on https://shuffler.io if this persists logout."
+ );
+ return;
+ }
+
+ const url = `${globalUrl}/api/v1/orgs/${orgId}/deals`;
+ fetch(url, {
+ method: "GET",
+ credentials: "include",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Bad status code in get deals: ", response.status);
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ console.log("Got deals: ", responseJson);
+ if (responseJson.success === false) {
+ toast("Failed loading deals. Contact support if this persists");
+ } else {
+ setDealList(responseJson);
+ }
+ })
+ .catch((error) => {
+ console.log("Error getting org deals: ", error);
+ toast(
+ "Failed getting deals for your org. Contact support if this persists."
+ );
+ });
+ };
+
+ useEffect(() => {
+ if (isCloud && selectedOrganization.partner_info !== undefined && selectedOrganization.partner_info.reseller === true) {
+ handleGetDeals(selectedOrganization.id);
+ }
+ }, [])
+
+ const paperStyle = {
+ padding: 20,
+ height: "100%",
+ minHeight: 280,
+ maxWidth: 400,
+ width: "100%",
+ backgroundColor: theme.palette.surfaceColor,
+ borderRadius: theme.palette.borderRadius,
+ border: "1px solid rgba(255,255,255,0.3)",
+ marginRight: 10,
+ }
+
+ const isCloud =
+ window.location.host === "localhost:3002" ||
+ window.location.host === "shuffler.io";
+
+ billingInfo.subscription = {
+ "active": true,
+ "name": "Pay as you go",
+ "price": typecost_single,
+ "currency": "USD",
+ "currency_text": "$",
+ "interval": "app run / month",
+ "description": "Pay as you go",
+ "features": [
+ "Includes 10.000 app run/month for free. ",
+ "Pay for what you use with no minimum commitment and cancel anytime.",
+ ],
+ "limit": 10000,
+ }
+
+
+ const handleStripeRedirect = () => {
+ //var priceItem = "price_1MRNF1DzMUgUjxHSfFTUb2Xh"
+ if (stripe == "") {
+ console.log("Stripe not loaded")
+ return
+ }
+
+ var priceItem = "price_1MROFrDzMUgUjxHShcSxgHO1"
+
+ const successUrl = `${window.location.origin}/admin?admin_tab=billing&payment=success`
+ const failUrl = `${window.location.origin}/admin?admin_tab=billing&payment=failure`
+ var checkoutObject = {
+ lineItems: [
+ {
+ price: priceItem,
+ quantity: 1
+ },
+ ],
+ mode: "subscription",
+ billingAddressCollection: "auto",
+ successUrl: successUrl,
+ cancelUrl: failUrl,
+ clientReferenceId: props.userdata.active_org.id,
+ }
+ //submitType: "donate",
+
+ stripe.redirectToCheckout(checkoutObject)
+ .then(function (result) {
+ console.log("SUCCESS STRIPE?: ", result)
+
+ ReactGA.event({
+ category: "pricing",
+ action: "add_card_success",
+ label: "",
+ })
+ })
+ .catch(function(error) {
+ console.error("STRIPE ERROR: ", error)
+
+ ReactGA.event({
+ category: "pricing",
+ action: "add_card_error",
+ label: "",
+ })
+ });
+ }
+
+ const cancelSubscriptions = (subscription_id) => {
+ const orgId = selectedOrganization.id;
+ const data = {
+ subscription_id: subscription_id,
+ action: "cancel",
+ org_id: selectedOrganization.id,
+ };
+
+ const url = globalUrl + `/api/v1/orgs/${orgId}/cancel`;
+ fetch(url, {
+ mode: "cors",
+ method: "POST",
+ body: JSON.stringify(data),
+ credentials: "include",
+ crossDomain: true,
+ withCredentials: true,
+ headers: {
+ "Content-Type": "application/json; charset=utf-8",
+ },
+ })
+ .then(function (response) {
+ if (response.status !== 200) {
+ console.log("Error in response");
+ }
+
+ if (handleGetOrg != undefined) {
+ handleGetOrg(selectedOrganization.id);
+ }
+
+ return response.json();
+ })
+ .then(function (responseJson) {
+ if (responseJson.success !== undefined && responseJson.success) {
+ toast("Successfully stopped subscription!");
+ } else {
+ toast("Failed stopping subscription. Please contact us.");
+ }
+ })
+ .catch(function (error) {
+ console.log("Error: ", error);
+ toast("Failed stopping subscription. Please contact us.");
+ });
+ };
+
+ const SubscriptionObject = (props) => {
+ const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, highlight, } = props;
+
+ var top_text = "Base Access"
+ if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) {
+ subscription.name = "Enterprise"
+ subscription.currency_text = "$"
+ subscription.price = subscription.level*180
+ subscription.limit = subscription.level*100000
+ subscription.interval = subscription.recurrence
+ subscription.features = [
+ "Includes " + subscription.limit + " app runs/month. ",
+ "Multi-Tenancy and Region-Selection",
+ "And all other features from /pricing",
+ ]
+ }
+
+ var newPaperstyle = JSON.parse(JSON.stringify(paperStyle))
+ if (subscription.name === "Enterprise" && subscription.active === true) {
+ top_text = "Current Plan"
+
+ newPaperstyle.border = "1px solid #f85a3e"
+ }
+
+ var showSupport = false
+ if (subscription.name.includes("default")) {
+ top_text = "Custom Contract"
+ newPaperstyle.border = "1px solid #f85a3e"
+ showSupport = true
+ }
+
+ if (subscription.name.includes("App Run Units")) {
+ top_text = "Cloud Access"
+ showSupport = true
+ }
+
+ if (subscription.name.includes("Open Source")) {
+ top_text = "Open Source"
+ showSupport = true
+ }
+
+ if (subscription.name.includes("Scale")) {
+ top_text = "Scale access"
+ }
+
+ if (highlight === true) {
+ // Add an "Upgrade now" button
+ newPaperstyle.border = "1px solid #f85a3e"
+ }
+
+ return (
+
+
+
+ {top_text}
+
+
+
+
+
+ {subscription.name}
+
+
+ {subscription.currency_text !== undefined ?
+
+
+ {subscription.currency_text}{subscription.price}
+
+
+ / {subscription.interval}
+
+
+ : null}
+
+
+ Features
+
+
+ {subscription.features !== undefined && subscription.features !== null ?
+ subscription.features.map((feature, index) => {
+ var parsedFeature = feature
+ if (feature.includes("Documentation: ")) {
+ parsedFeature =
+
+ Documentation to get started
+
+ }
+
+ if (feature.includes("Licensed Worker: ")) {
+ parsedFeature =
+
+ Download the licensed worker
+
+ }
+
+ return (
+
+
+ {parsedFeature}
+
+
+ )
+ })
+ : null}
+
+
+ {(highlight === true && subscription.name === "Pay as you go" && subscription.limit <= 10000) || subscription.name.includes("Scale") ?
+
+
+ {subscription.name.includes("Scale") ?
+ ""
+ :
+ "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."
+ }
+
+ {
+ if (isCloud) {
+ navigate("/pricing?tab=cloud&highlight=true")
+ } else {
+ window.open("https://shuffler.io/pricing?tab=onprem&highlight=true", "_blank")
+ }
+ }}
+ >
+ Upgrade Now
+
+
+ : null}
+ {showSupport ?
+ {
+ console.log("Support click")
+ if (window.drift !== undefined) {
+ //window.drift.api.startInteraction({ interactionId: 340045 })
+ window.drift.api.startInteraction({ interactionId: 340043 })
+ } else {
+ navigate("/contact")
+ }
+ }}>
+ Get Support
+
+ : null }
+
+ )
+ }
+
+ const addDealModal = (
+
{
+ setSelectedDealModalOpen(false);
+ }}
+ PaperProps={{
+ style: {
+ backgroundColor: theme.palette.surfaceColor,
+ color: "white",
+ minWidth: "800px",
+ minHeight: "320px",
+ },
+ }}
+ >
+
+ Register new deal
+
+
+
+ {
+ setDealName(e.target.value);
+ }}
+ />
+ {
+ setDealAddress(e.target.value);
+ }}
+ />
+
+
+
{
+ setDealValue(e.target.value);
+ }}
+ />
+ option.label}
+ onChange={(event, newValue) => {
+ setDealCountry(newValue.label);
+ }}
+ renderOption={(props, option) => (
+ img": { mr: 2, flexShrink: 0 } }}
+ {...props}
+ >
+
+ {option.label} ({option.code}) +{option.phone}
+
+ )}
+ renderInput={(params) => (
+
+ )}
+ />
+ {
+ setDealType(newValue);
+ }}
+ getOptionLabel={(option) => option.label}
+ renderOption={(props, option) => (
+ img": { mr: 2, flexShrink: 0 } }}
+ {...props}
+ >
+ {option.label}
+
+ )}
+ renderInput={(params) => (
+
+ )}
+ />
+
+ {dealerror.length > 0 ? (
+
+ error registering: {dealerror}
+
+ ) : null}
+
+ {
+ setSelectedDealModalOpen(false);
+
+ //setDealName("")
+ //setDealAddress("")
+ //setDealCountry("")
+ //setDealValue("")
+ }}
+ >
+ Cancel
+
+ {
+ submitDeal(dealName, dealAddress, dealCountry, dealValue);
+ }}
+ >
+ Submit
+
+
+
+
+ );
+
+ const submitDeal = (dealName, dealAddress, dealCountry, dealValue) => {
+ if (dealerror.length > 0) {
+ setDealerror("");
+ }
+
+ const orgId = selectedOrganization.id;
+ const data = {
+ reseller_org: orgId,
+ name: dealName,
+ address: dealAddress,
+ country: dealCountry,
+ value: dealValue,
+ };
+
+ const url = `${globalUrl}/api/v1/orgs/${orgId}/deals`;
+ fetch(url, {
+ mode: "cors",
+ method: "POST",
+ body: JSON.stringify(data),
+ credentials: "include",
+ crossDomain: true,
+ withCredentials: true,
+ headers: {
+ "Content-Type": "application/json; charset=utf-8",
+ },
+ })
+ .then(function (response) {
+ if (response.status !== 200) {
+ console.log("Error in response");
+ }
+
+ return response.json();
+ })
+ .then(function (responseJson) {
+ if (responseJson.success === true) {
+ setSelectedDealModalOpen(false);
+ toast(
+ "Added new deal! We will be in touch shortly with an update."
+ );
+
+ setDealName("");
+ setDealAddress("");
+ setDealValue("");
+ setDealCountry("United States");
+ setDealType("MSSP");
+ } else {
+ setDealerror(responseJson.reason);
+ }
+ })
+ .catch(function (error) {
+ //console.log("Error: ", error);
+ setDealerror(error.toString());
+ toast("Failed adding deal reg: ", error);
+ });
+ };
+
+ return (
+
+ {addDealModal}
+
+ Billing
+
+
+ {isCloud ?
+ "We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below."
+ :
+ "Shuffle is an Open Source automation platform, and no license is required to use it. You may however activate Cloud Sync, get our Scale license, get help with Kubernetes, or talk to Shuffle's Support team to get automation help."
+ }
+
+
+ {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ?
+
+ : !isCloud ?
+
+
+
+
+ : null}
+ {isCloud &&
+ selectedOrganization.subscriptions !== undefined &&
+ selectedOrganization.subscriptions !== null &&
+ selectedOrganization.subscriptions.length > 0 ?
+
+ selectedOrganization.subscriptions
+ .reverse()
+ .map((sub, index) => {
+ return (
+
+ )
+ })
+ : null}
+ {/*
+
+
+ Quantity : {sub.level}
+
+ Recurrence : {sub.recurrence}
+
+ {sub.active ? (
+
+
Started :{" "}
+ {new Date(sub.startdate * 1000).toISOString()}
+
+
{
+ cancelSubscriptions(sub.reference);
+ }}
+ >
+ Cancel subscription
+
+
+ ) : (
+
+
Cancelled :{" "}
+ {new Date(
+ sub.cancellationdate * 1000
+ ).toISOString()}
+
+
+ Status : Deactivated
+
+
+ )}
+
+
+ */}
+
+ {isCloud &&
+ selectedOrganization.partner_info !== undefined &&
+ selectedOrganization.partner_info.reseller === true ? (
+
+
+ Reseller dashboard
+
+
{
+ setSelectedDealModalOpen(true);
+ }}
+ >
+ Add deal
+
+
{
+ handleGetDeals(userdata.active_org.id);
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {dealList.length === 0 ? (
+
+ No deals registered yet. Click "Add deal" to register one
+
+ ) : (
+ dealList.map((deal, index) => {
+ var bgColor = "#27292d";
+ if (index % 2 === 0) {
+ bgColor = "#1f2023";
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ })
+ )}
+
+
+
+
+
+ ) : null}
+
+
+ Billing Usage Overview
+
+
+
+
+ )
+}
+
+export default Billing;
diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx
new file mode 100644
index 00000000..3f7a7516
--- /dev/null
+++ b/frontend/src/components/BillingStats.jsx
@@ -0,0 +1,280 @@
+import React, { useState, useEffect } from 'react';
+
+import classNames from "classnames";
+import theme from '../theme.jsx';
+
+import {
+ Tooltip,
+ TextField,
+ IconButton,
+ Button,
+ Typography,
+ Grid,
+ Paper,
+ Chip,
+ Checkbox,
+} from "@mui/material";
+
+import {
+ BarChart,
+ RadialBarChart,
+ RadialAreaChart,
+ RadialAxis,
+ StackedBarSeries,
+ TooltipArea,
+ ChartTooltip,
+ TooltipTemplate,
+ RadialAreaSeries,
+ RadialPointSeries,
+ RadialArea,
+ RadialLine,
+ TreeMap,
+ TreeMapSeries,
+ TreeMapLabel,
+ TreeMapRect,
+ Line,
+ LineChart,
+ LineSeries,
+ LinearYAxis,
+ LinearXAxis,
+ LinearYAxisTickSeries,
+ LinearXAxisTickSeries,
+ Area,
+ AreaChart,
+ AreaSeries,
+ AreaSparklineChart,
+ PointSeries,
+ GridlineSeries,
+ Gridline,
+ Stripes,
+ Gradient,
+ GradientStop,
+ LinearXAxisTickLabel,
+} from 'reaviz';
+
+const LineChartWrapper = ({keys, inputname, height, width}) => {
+ const [hovered, setHovered] = useState("");
+ const inputdata = keys.data === undefined ? keys : keys.data
+
+ return (
+
+
+ {inputname}
+
+ } />
+ }
+ />
+
+ )
+}
+
+
+const AppStats = (defaultprops) => {
+ const { globalUrl, selectedOrganization, userdata, } = defaultprops;
+ const [keys, setKeys] = useState([])
+ const [searches, setSearches] = useState([]);
+ const [clickData, setClickData] = useState(undefined);
+ const [conversionData, setConversionData] = useState(undefined);
+ const [statistics, setStatistics] = useState(undefined);
+ const [appRuns, setAppruns] = useState(undefined);
+ const [workflowRuns, setWorkflowRuns] = useState(undefined);
+ const [subflowRuns, setSubflowRuns] = useState(undefined);
+
+ const handleDataSetting = (inputdata, grouping) => {
+ if (inputdata === undefined || inputdata === null) {
+ return
+ }
+
+ const dailyStats = inputdata.daily_statistics
+ if (dailyStats === undefined || dailyStats === null) {
+ return
+ }
+
+ console.log("Looking at daily data: ", inputdata)
+
+ var appRuns = {
+ "key": "App Runs",
+ "data": []
+ }
+
+ var workflowRuns = {
+ "key": "Workflow Runs (includes subflows)",
+ "data": []
+ }
+
+ var subflowRuns = {
+ "key": "Subflow Runs",
+ "data": []
+ }
+
+ for (let key in dailyStats) {
+ // Always skips first one as it has accumulated data in it
+ if (key === 0) {
+ continue
+ }
+
+ const item = dailyStats[key]
+
+ if (item["date"] === undefined) {
+ console.log("No date: ", item)
+ continue
+ }
+
+ // Check if app_executions key in item
+ if (item["app_executions"] !== undefined && item["app_executions"] !== null) {
+ appRuns["data"].push({
+ key: new Date(item["date"]),
+ data: item["app_executions"]
+ })
+ }
+
+ // Check if workflow_executions key in item
+ if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) {
+ workflowRuns["data"].push({
+ key: new Date(item["date"]),
+ data: item["workflow_executions"]
+ })
+ }
+
+ if (item["subflow_executions"] !== undefined && item["subflow_executions"] !== null) {
+ subflowRuns["data"].push({
+ key: new Date(item["date"]),
+ data: item["subflow_executions"]
+ })
+ }
+ }
+
+ // Adds data for today
+ console.log("Inputdata: ", inputdata)
+ if (inputdata["daily_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) {
+ appRuns["data"].push({
+ key: new Date(),
+ data: inputdata["daily_app_executions"]
+ })
+ }
+
+ if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) {
+ workflowRuns["data"].push({
+ key: new Date(),
+ data: inputdata["daily_workflow_executions"]
+ })
+ }
+
+ if (inputdata["daily_subflow_executions"] !== undefined && inputdata["daily_subflow_executions"] !== null) {
+ subflowRuns["data"].push({
+ key: new Date(),
+ data: inputdata["daily_subflow_executions"]
+ })
+ }
+
+ setSubflowRuns(subflowRuns)
+ setWorkflowRuns(workflowRuns)
+ setAppruns(appRuns)
+ }
+
+ const getStats = () => {
+ fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for workflows :O!: ", response.status);
+ return;
+ }
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson["success"] === false) {
+ return
+ }
+
+ setStatistics(responseJson)
+ handleDataSetting(responseJson, "day")
+ })
+ .catch((error) => {
+ console.log("error: ", error)
+ });
+ }
+
+ useEffect(() => {
+ getStats()
+ }, [])
+
+ const paperStyle = {
+ textAlign: "center",
+ padding: 40,
+ margin: 5,
+ backgroundColor: theme.palette.surfaceColor,
+ maxWidth: 300,
+ }
+
+ const data = (
+
+
+ All Stat widgets are monthly and gathered from Your Organization Statistics.
+ This is a feature to help give you more insight into Shuffle, and will be populating over time.
+
+ {statistics !== undefined ?
+
+
+
+ {statistics.monthly_workflow_executions}
+
+
+ Workflow Runs
+
+
+
+
+ {statistics.monthly_app_executions}
+
+
+ App Runs
+
+
+
+ : null}
+
+ {appRuns === undefined ?
+ null
+ :
+
+ }
+
+ {workflowRuns === undefined ?
+ null
+ :
+
+ }
+
+ {subflowRuns === undefined ?
+ null
+ :
+
+ }
+
+ )
+
+ const dataWrapper = (
+
{data}
+ );
+
+ return dataWrapper;
+}
+
+export default AppStats;
diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx
index 0e80540c..52a3ac1b 100755
--- a/frontend/src/components/ConfigureWorkflow.jsx
+++ b/frontend/src/components/ConfigureWorkflow.jsx
@@ -16,11 +16,17 @@ import {
ListItem,
ListItemText,
Collapse,
+ IconButton,
} from "@mui/material";
+
import {
FavoriteBorder as FavoriteBorderIcon,
Error as ErrorIcon,
CheckCircleRounded as CheckCircleRoundedIcon,
+ ExpandMore as ExpandMoreIcon,
+ ExpandLess as ExpandLessIcon,
+ Check as CheckIcon,
+ Visibility as VisibilityIcon,
} from "@mui/icons-material";
import { FixName } from "../views/Apps.jsx";
import aa from 'search-insights'
@@ -34,27 +40,26 @@ import aa from 'search-insights'
// Specifically used for UNSAVED workflows only?
const ConfigureWorkflow = (props) => {
const {
- userdata,
- globalUrl,
+ apps,
theme,
+ isCloud,
workflow,
+ userdata,
+ globalUrl,
+ newWebhook,
+ referenceUrl,
+ saveWorkflow,
+ showTriggers,
+ submitSchedule,
+ setSelectedApp,
+ selectedAction,
appAuthentication,
setSelectedAction,
- setAuthenticationModalOpen,
- setSelectedApp,
- apps,
- selectedAction,
- setConfigureWorkflowModalOpen,
- saveWorkflow,
- newWebhook,
- submitSchedule,
- referenceUrl,
- isCloud,
+ workflowExecutions,
+ getWorkflowExecution,
setAuthenticationType,
- alert,
- showTriggers,
- workflowExecutions,
- getWorkflowExecution,
+ setAuthenticationModalOpen,
+ setConfigureWorkflowModalOpen,
} = props;
const [requiredActions, setRequiredActions] = React.useState([]);
@@ -64,9 +69,39 @@ const ConfigureWorkflow = (props) => {
const [itemChanged, setItemChanged] = React.useState(false);
const [firstLoad, setFirstLoad] = React.useState("");
const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false);
+ const [loopRunning, setLoopRunning] = useState(false)
- const [checkStarted, setCheckStarted] = React.useState(false);
+ const [checkStarted, setCheckStarted] = React.useState(false);
+ const stop = () => {
+ setLoopRunning(false)
+ }
+
+ const start = () => {
+ setLoopRunning(true)
+ }
+
+ useEffect(() => {
+ if (loopRunning) {
+ const intervalId = setInterval(() => {
+ if (!loopRunning) {
+ clearInterval(intervalId);
+ }
+
+
+ if (getWorkflowExecution !== undefined && workflowExecutions !== undefined) {
+ const paramkey = workflow.id
+ getWorkflowExecution(paramkey)
+ } else {
+ console.log("Executions or getWorkflowExecutions not defined")
+ }
+ }, 3000)
+
+ return () => clearInterval(intervalId);
+ }
+ }, [loopRunning])
+
+ /*
const { start, stop } = useInterval({
duration: 3000,
startImmediate: false,
@@ -79,6 +114,7 @@ const ConfigureWorkflow = (props) => {
}
},
});
+ */
// ONLY when component is being unloaded, run stop() function
// This is to prevent the interval from running when the component is not being used
@@ -92,16 +128,18 @@ const ConfigureWorkflow = (props) => {
*/
// Where is this from?
- if (workflow === undefined || workflow === null) {
+ if (workflow === undefined || workflow === null || workflow.id === undefined) {
return null;
}
if (apps === undefined || apps === null) {
- return null;
+ console.log("Apps is undefined or null: ", apps)
+ return null;
}
if (appAuthentication === undefined || appAuthentication === null) {
- return null;
+ console.log("App authentication is undefined or null: ", appAuthentication)
+ return null;
}
const getApp = (actionId, appId) => {
@@ -136,13 +174,19 @@ const ConfigureWorkflow = (props) => {
if (firstLoad.length === 0 || firstLoad !== workflow.id) {
if (apps === undefined || apps === null || apps.length === 0) {
console.log("No apps loaded: ", apps);
- setConfigureWorkflowModalOpen(false);
+
+ if (setConfigureWorkflowModalOpen !== undefined) {
+ setConfigureWorkflowModalOpen(false);
+ }
+
return null;
}
setFirstLoad(workflow.id)
+
const newactions = [];
for (let [key, keyval] in Object.entries(workflow.actions)) {
+
const action = workflow.actions[key];
var newaction = {
large_image: action.large_image,
@@ -154,34 +198,36 @@ const ConfigureWorkflow = (props) => {
auth_done: false,
action_ids: [],
action: action,
- update_version: action.app_version,
+ update_version: action.app_version,
app: {},
- steps: [],
- show_steps: false,
+ steps: [],
+ show_steps: false,
}
- //console.log("Action: ", key, keyval)
+ if (action.app_name.toLowerCase().endsWith("_api")) {
+ action.app_name = action.app_name.slice(0, -4)
+ }
- const app = apps.find((app) =>
- app.id === action.app_id ||
- (app.name === action.app_name &&
- (app.app_version === action.app_version || (app.loop_versions !== null && app.loop_versions.includes(action.app_version))))
- )
-
- //console.log("FOUND APP: ", app)
+ // ID match OR name match + version match
+ //const app = apps.find((app) => app.id === action.app_id || (app.name === action.app_name && (app.app_version === action.app_version || (app.loop_versions !== null && app.loop_versions.includes(action.app_version)))))
+ //
+ // without version match
+ const newappname = action.app_name.toLowerCase().replaceAll(" ", "_")
+ const app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname)
if (app === undefined || app === null) {
+
const subapp = apps.find(app => app.name === action.app_name)
- if (subapp !== undefined && subapp !== null) {
- newaction.update_version = "1.1.0"
- }
+ if (subapp !== undefined && subapp !== null) {
+ newaction.update_version = "1.1.0"
+ }
newaction.must_activate = true;
- newaction.steps.push({
- "title": "Activate app",
- "type": "activate",
- "required": true,
- })
+ newaction.steps.push({
+ "title": "Activate app",
+ "type": "activate",
+ "required": true,
+ })
} else {
if (action.authentication_id === "" && app.authentication.required === true && action.parameters !== undefined && action.parameters !== null) {
// Check if configuration is filled or not
@@ -199,20 +245,19 @@ const ConfigureWorkflow = (props) => {
}
}
- newaction.steps.push({
- "title": "Authenticate app",
- "type": "authenticate",
- "required": true,
- })
+ newaction.steps.push({
+ "title": "Authenticate app",
+ "type": "authenticate",
+ "required": true,
+ })
if (!filled) {
newaction.must_authenticate = true;
newaction.action_ids.push(action.id);
}
} else if (action.authentication_id !== "" && app.authentication.required === true) {
- console.log("Should verify authentication ID ", action.authentication_id)
-
- }
+ console.log("Should verify authentication ID ", action.authentication_id)
+ }
newaction.app = app;
}
@@ -276,80 +321,78 @@ const ConfigureWorkflow = (props) => {
}
if (workflow.workflow_variables !== undefined && workflow.workflow_variables !== null && workflow.workflow_variables.length !== 0) {
- for (let [key,keyval] in Object.entries(workflow.workflow_variables)) {
- const variable = workflow.workflow_variables[key];
- if (variable.value === undefined || variable.value === undefined || variable.value.length === 0) {
- variable.value = "";
- requiredVariables.push(variable);
- }
+ for (let [key,keyval] in Object.entries(workflow.workflow_variables)) {
+ const variable = workflow.workflow_variables[key];
- variable.index = key;
- }
+ if (variable.value === undefined || variable.value === undefined || variable.value.length === 0) {
+ variable.value = "";
+ requiredVariables.push(variable);
+ }
+
+ variable.index = key;
+ }
}
if (workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers.length !== 0) {
- for (let [key,keyval] in Object.entries(workflow.triggers)) {
- var trigger = workflow.triggers[key];
- trigger.index = key;
+ for (let [key,keyval] in Object.entries(workflow.triggers)) {
+ var trigger = workflow.triggers[key];
+ trigger.index = key;
- if (trigger.trigger_type === "WEBHOOK") {
- console.log("Found webhook: ", trigger)
- if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
- console.log("Actions: ", newactions)
- const findapp = trigger.app_association.name.toLowerCase()
- const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp)
+ if (trigger.trigger_type === "WEBHOOK") {
+ console.log("Found webhook: ", trigger)
- // Adding webhook to start of it
- if (foundindex >= 0) {
- const tmpsteps = newactions[foundindex].steps
- newactions[foundindex].steps = [
- {
- "title": "Configure Webhook",
- "type": "webhook",
- "required": true,
- }
- ]
+ if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
+ console.log("Actions: ", newactions)
+ const findapp = trigger.app_association.name.toLowerCase()
+ const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp)
- for (let [subkey,subkeyval] in Object.entries(tmpsteps)) {
- newactions[foundindex].steps.push(tmpsteps[subkey])
- }
-
- newactions[foundindex].show_steps = true
+ // Adding webhook to start of it
+ if (foundindex >= 0) {
+ const tmpsteps = newactions[foundindex].steps
+ newactions[foundindex].steps = [
+ {
+ "title": "Configure Webhook",
+ "type": "webhook",
+ "required": true,
+ }
+ ]
- console.log("CHANGED ACTION: ", newactions[foundindex])
- //console.log("Index: ", newactions[foundindex])
+ for (let [subkey,subkeyval] in Object.entries(tmpsteps)) {
+ newactions[foundindex].steps.push(tmpsteps[subkey])
+ }
+
+ newactions[foundindex].show_steps = true
- continue
- }
- }
+ console.log("CHANGED ACTION: ", newactions[foundindex])
+ //console.log("Index: ", newactions[foundindex])
+
+ continue
+ }
+ }
+ }
+
+ if (trigger.status === "running") {
+ continue;
+ }
+
+ if (
+ trigger.trigger_type === "SUBFLOW" ||
+ trigger.trigger_type === "USERINPUT"
+ ) {
+ continue;
+ }
+
+ requiredTriggers.push(trigger);
}
+ }
- if (trigger.status === "running") {
- continue;
- }
+ if (requiredTriggers.length === 0 && requiredVariables.length === 0 && newactions.length === 0 && setConfigureWorkflowModalOpen !== undefined) {
+ setConfigureWorkflowModalOpen(false);
+ }
- if (
- trigger.trigger_type === "SUBFLOW" ||
- trigger.trigger_type === "USERINPUT"
- ) {
- continue;
- }
-
- requiredTriggers.push(trigger);
- }
-}
-
- if (
- requiredTriggers.length === 0 &&
- requiredVariables.length === 0 &&
- newactions.length === 0
- ) {
- setConfigureWorkflowModalOpen(false);
- }
-
- setRequiredTriggers(requiredTriggers);
- setRequiredVariables(requiredVariables);
- setRequiredActions(newactions);
+ setRequiredTriggers(requiredTriggers);
+ setRequiredVariables(requiredVariables);
+ setRequiredActions(newactions);
}
if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) {
@@ -381,7 +424,7 @@ const ConfigureWorkflow = (props) => {
const { trigger } = props
return (
-
+
{
{trigger.status !== "running" ? "Start" : "Running"}
) : null}
- {/*
-
-
- )
- }}
- fullWidth
- color="primary"
- type={"text"}
- placeholder={`New value for ${trigger.name}`}
- onChange={(event) => {
- console.log("NEW VALUE ON INDEX", trigger.value)
- }}
- onBlur={(event) => {
- //workflow.variables[variable.index] = event.target.value
- }}
- />
- }
- style={{}}
- />
- */}
);
};
@@ -488,7 +498,7 @@ const ConfigureWorkflow = (props) => {
//Name: {variable.name} - {variable.value}.
return (
-
+
@@ -589,88 +599,312 @@ const ConfigureWorkflow = (props) => {
};
+ const AppSectionSelfcontained = (props) => {
+ const { action } = props;
+
+ const [opened, setOpened] = useState(false);
+ const [filled, setFilled] = useState(false);
+ const [submitted, setSubmitted] = useState(false);
+ const [finalized, setFinalized] = useState(false);
+
+ const [authFields, setAuthFields] = useState([])
+ const [sensitiveFields, setSensitiveFields] = useState([])
+
+ if (authFields.length === 0 && opened === true) {
+ // Loop through fields of the action
+
+ var newfields = []
+ const params = action.action.parameters
+
+ var sensitiveIndexes = []
+ var index = 0
+ for (let key in params) {
+ const param = params[key]
+
+ if (param.configuration === true) {
+ if (param.name.toLowerCase().includes("key") || param.name.toLowerCase().includes("token") || param.name.toLowerCase().includes("password")) {
+ sensitiveIndexes.push(index)
+ }
+
+ newfields.push({
+ "key": param.name,
+ "example": param.example === undefined ? "" : param.example,
+ "value": param.name === "url" ? param.example : "",
+ })
+
+ index += 1
+ }
+ }
+
+ if (newfields.length > 0) {
+ setSensitiveFields(sensitiveIndexes)
+ setAuthFields(newfields)
+ }
+ }
+
+
+ const submitLocalAuth = (app, fields) => {
+ const appAuthData = {
+ active: true,
+ app: app,
+ fields: fields,
+ label: "Authentication for " + app.name,
+ usage: [{"workflow_id": workflow.id}],
+ auto_distribute: true,
+ }
+
+
+ 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!");
+ }
+
+ setSubmitted(false)
+
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (!responseJson.success) {
+ toast("Failed to set app auth: " + responseJson.reason);
+ } else {
+ toast("App auth set for app " + app.name.replace("_", " "));
+ setFinalized(true)
+ setOpened(false)
+ }
+ })
+ .catch((error) => {
+ setSubmitted(false)
+ //toast(error.toString());
+ console.log("New auth error: ", error.toString());
+ });
+ }
+
+ var parsedName = action.app_name.replaceAll("_", " ");
+ if (action.app_name.toLowerCase().endsWith("_api")) {
+ parsedName = parsedName.substring(0, parsedName.length - 4);
+ }
+
+ // Remove _basic at the end if it exists
+ if (parsedName.toLowerCase().endsWith("_basic")) {
+ parsedName = parsedName.substring(0, parsedName.length - 6);
+ }
+
+ parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " ");
+
+ return (
+
+
+
{
+ setOpened(!opened);
+ }}
+ >
+
+
+ {!opened ? : }
+
+
+
+ {finalized ? "Authenticated" : `Configure ${parsedName}`}
+
+
+ {filled ?
+
+ : null}
+
+ {opened ?
+
+ {authFields.map((field, index) => {
+ var parsedName = field.key
+ // Remove _basic at the end if it exists
+ if (parsedName.toLowerCase().endsWith("_basic")) {
+ parsedName = parsedName.substring(0, parsedName.length - 6);
+ }
+
+ parsedName = (parsedName.charAt(0).toUpperCase() + parsedName.slice(1)).replaceAll("_", " ");
+
+ return (
+
+
+ {parsedName}
+
+ {
+ event.preventDefault();
+ authFields[index].value = event.target.value;
+ setAuthFields(authFields);
+
+ var allFilled = true;
+ authFields.forEach((field) => {
+ if (field.value.length === 0) {
+ allFilled = false;
+ } else {
+ //console.log("Field is not filled: "+field.key)
+ }
+ })
+
+ if (allFilled) {
+ console.log("Should test the fields, and submit them")
+ setFilled(true);
+ } else {
+ if (filled) {
+ setFilled(false);
+ }
+ }
+ }}
+
+ endAdornment={
+ // Show item that can show field value if password
+ //field.name.toLowerCase().includes("key") || field.name.toLowerCase().includes("token") || field.name.toLowerCase().includes("password") ?
+ field.key.toLowerCase().includes("key") || field.key.toLowerCase().includes("token") || field.key.toLowerCase().includes("password") ?
+
+ {
+ setSensitiveFields(sensitiveFields.filter((item) => item !== index))
+ }}
+ onMouseDown={(event) => {
+ event.preventDefault();
+ }}
+ >
+
+
+
+ : null
+ }
+ fullWidth
+ color="primary"
+ type={sensitiveFields.includes(index) ? "password" : "text"}
+ placeholder={field.example ? field.example : `Enter your ${field.key}`}
+ data-lpignore="true"
+ dataLPIgnore="true"
+ autocomplete="off"
+ />
+
+ )
+ })}
+
{
+ setSubmitted(true);
+
+ // const submitLocalAuth = (app, fields) => {
+ submitLocalAuth({"id": action.app.id, "name": action.app.name, "version": action.app.version, "large_image": action.large_image, }, authFields);
+
+ }}
+ >
+ {submitted ? : "Submit"}
+
+
+ : null}
+
+
+ )
+ }
const AppSection = (props) => {
const { action } = props;
+ var parsedName = action.app_name.replaceAll("_", " ");
+ if (action.app_name.toLowerCase().endsWith("_api")) {
+ parsedName = parsedName.substring(0, parsedName.length - 4);
+ }
+
return (
- {/*
-
-
-
-
-
-
- */}
{action.must_authenticate ?
- {
- setAuthenticationType(
- action.app.authentication.type === "oauth2" &&
- action.app.authentication.redirect_uri !== undefined &&
- action.app.authentication.redirect_uri !== null
- ? {
- type: "oauth2",
- redirect_uri: action.app.authentication.redirect_uri,
- token_uri: action.app.authentication.token_uri,
- scope: action.app.authentication.scope,
- }
- : {
- type: "",
- }
- )
+ {
+ if (setAuthenticationType !== undefined) {
+ setAuthenticationType(action.app.authentication.type === "oauth2" && action.app.authentication.redirect_uri !== undefined && action.app.authentication.redirect_uri !== null
+ ?
+ {
+ type: "oauth2",
+ redirect_uri: action.app.authentication.redirect_uri,
+ token_uri: action.app.authentication.token_uri,
+ scope: action.app.authentication.scope,
+ }
+ :
+ {
+ type: "",
+ }
+ )
+ }
- setItemChanged(true);
+ setItemChanged(true);
+ if (setSelectedAction !== undefined) {
+ setSelectedAction(action.action);
+ }
- if (setSelectedAction !== undefined) {
- setSelectedAction(action.action);
- }
+ if (setSelectedApp !== undefined) {
+ setSelectedApp(action.app);
+ }
- if (setSelectedApp !== undefined) {
- setSelectedApp(action.app);
- }
-
- setAuthenticationModalOpen(true);
- }}
- >
-
-
- {action.auth_done ? "Authenticated" : `Authenticate ${action.app_name.replaceAll("_", " ")}`}
-
-
+ if (setAuthenticationModalOpen !== undefined) {
+ setAuthenticationModalOpen(true);
+ }
+ }}
+ >
+
+
+ {action.auth_done ? "Authenticated" : `Authenticate ${action.app_name.replaceAll("_", " ")}`}
+
+
: null}
{action.update_version !== action.app_version ?
- {
:
action.must_activate ?
{
- console.log("ACTION: ", action)
- activateApp(action.action.app_id, action.app_name, action.app_version);
- setItemChanged(true);
- }}
+ fullWidth
+ variant="contained"
+ disabled={action.auth_done}
+ style={{
+ flex: 1,
+ textTransform: "none",
+ textAlign: "left",
+ justifyContent: "flex-start",
+ backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
+ color: action.auth_done ? "#686a6c" : "#ffffff",
+ borderRadius: theme.palette.borderRadius,
+ minWidth: 350,
+ maxHeight: 50,
+ overflow: "hidden",
+ border: `1px solid ${theme.palette.inputColor}`,
+ }}
+ color="primary"
+ onClick={() => {
+ console.log("ACTION: ", action)
+ activateApp(action.action.app_id, action.app_name, action.app_version);
+ setItemChanged(true);
+ }}
>
{
//backgroundColor: selectedUsecaseCategory === usecase.name ? usecase.color : theme.palette.surfaceColor,
const BoxHighlight = (props) => {
- const {data, appname, appinfo, index, activeStep, setActiveStep, finished, } = props
+ const {data, appname, appinfo, index, activeStep, setActiveStep, filled, } = props
const [hovered, setHovered] = useState(false)
const [isOpen, setIsOpen] = useState(false)
@@ -773,7 +1007,6 @@ const ConfigureWorkflow = (props) => {
// This kind of just works for new workflows..
// What if we try many times?
-
var webhook = {
"name": "Testhook",
"description": `A Webhook Trigger has been started and is ready to receive events from ${appname}. Click to copy the URL to send events to.`,
@@ -781,10 +1014,10 @@ const ConfigureWorkflow = (props) => {
}
useEffect(() => {
- if (data.type === "webhook" && !finished) {
+ if (data.type === "webhook" && !filled) {
if (!checkStarted) {
setCheckStarted(true)
- start()
+ start()
}
}
}, [])
@@ -832,8 +1065,8 @@ const ConfigureWorkflow = (props) => {
>
-
{data.title}
- {finished ?
+
{data.title}
+ {filled ?
:
@@ -870,7 +1103,7 @@ const ConfigureWorkflow = (props) => {
}}>
{webhook.description}
{/*
{webhook.url} */}
- {isLoading && finished === false ?
+ {isLoading && filled === false ?
@@ -879,7 +1112,10 @@ const ConfigureWorkflow = (props) => {
}
:
-
+ setConfigureWorkflowModalOpen !== undefined ?
+
+ :
+
}
: null}
@@ -923,10 +1159,10 @@ const ConfigureWorkflow = (props) => {