Tons of frontend bugfixes from cloud sync

This commit is contained in:
Frikky
2025-03-30 18:44:09 +02:00
parent a3b0093cf5
commit d8cd75879e
16 changed files with 742 additions and 431 deletions
+4 -4
View File
@@ -891,7 +891,7 @@ const Billing = memo((props) => {
</span>
: null}
{showSupport ?
{/* {showSupport ?
<Button variant="outlined" color="primary" style={{ marginTop: 20, marginBottom: 10, }} onClick={() => {
if (window.drift !== undefined) {
//window.drift.api.startInteraction({ interactionId: 340045 })
@@ -902,7 +902,7 @@ const Billing = memo((props) => {
}}>
Get Support
</Button>
: null}
: null} */}
</div>
)
}
@@ -2021,7 +2021,7 @@ const Billing = memo((props) => {
<div style={{ display: "flex", width: clickedFromOrgTab ? "100%" : "auto", overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: '#494949 #2f2f2f', height: isChildOrg ? 0 : "100%", marginTop: 20}} >
<div style={{ display: "flex", flexDirection: "column", width: "100%", }}>
{isCloud &&
{/* {isCloud &&
selectedOrganization.subscriptions !== undefined &&
selectedOrganization.subscriptions !== null &&
selectedOrganization.subscriptions.length > 0 &&
@@ -2043,7 +2043,7 @@ const Billing = memo((props) => {
/>
)
})
: null}
: null} */}
<div style={{ display: "flex", flexDirection: "row", width: "100%", marginTop: 20, marginBottom: 20, maxWidth: 860, }}>
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null :
+10 -6
View File
@@ -151,13 +151,16 @@ const CacheView = memo((props) => {
if (fileCategories.length === 1 && fileCategories[0] === "default") {
var newcategories = ["default"]
for (var key in responseJson.keys) {
if (responseJson.keys[key].category !== undefined && responseJson.keys[key].category !== null && responseJson.keys[key].category !== "" && !fileCategories.includes(responseJson.keys[key].category)) {
newcategories.push(responseJson.keys[key].category);
var category = responseJson.keys[key].category
if (category !== undefined && category !== null && category !== ""){
category = category.replaceAll(" ", "_")
if (!newcategories.includes(category)) {
newcategories.push(category)
}
}
}
console.log("CATEGORIES: ", newcategories)
setFileCategories(newcategories)
}
}
@@ -357,6 +360,7 @@ const CacheView = memo((props) => {
<span style={{ color: "white" }}>
{ editCache ? "Edit Key" : "Add Key"}{selectedCategory === "" || selectedCategory === "default" ? "" : ` in category '${selectedCategory}'`}
</span>
</DialogTitle>
<div style={{ paddingLeft: "30px", paddingRight: '30px', backgroundColor: "#212121", }}>
Key
@@ -1041,13 +1045,13 @@ const CacheView = memo((props) => {
</span>
</Tooltip>
<Tooltip
title={data?.org_id !== selectedOrganization.id ? "You can not delete this key as it is controlled by parent organization." : "Delete this key" }
title={selectedOrganization?.id !== undefined && data?.org_id !== selectedOrganization.id ? "You can not delete this key as it is controlled by parent organization." : "Delete this key" }
aria-label={"Delete"}
>
<span>
<IconButton
style={{ padding: "6px" }}
disabled={data.org_id !== selectedOrganization.id ? true : false}
disabled={selectedOrganization?.id === undefined ? false : data.org_id !== selectedOrganization.id ? true : false}
onClick={() => {
deleteCache(orgId, data.key);
//deleteFile(orgId);
+8 -6
View File
@@ -774,8 +774,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}, [window?.location?.pathname]);
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
const showPartnerLogo = userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.image !== undefined && userdata?.active_org?.image !== null && userdata?.active_org?.image.length > 0
return (
<div
style={{
@@ -836,11 +836,13 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}
}}
>
<Link to="/">
<Link to={isCloud && !showPartnerLogo ? "/" : "/workflows"}>
<img
src={ShuffleLogo}
src={
showPartnerLogo ? userdata?.active_org?.image : ShuffleLogo
}
alt="Shuffle Logo"
style={{ width: 24, height: 24 }}
style={{ width: showPartnerLogo ? 30 : 24, height: showPartnerLogo ? 30 : 24 }}
/>
</Link>
</Tooltip>
@@ -1611,7 +1613,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
}}
>
{expandLeftNav &&
{userdata?.licensed !== true && !userdata?.org_status?.includes("integration_partner") && expandLeftNav &&
<Button
variant="outlined"
style={{marginBottom: 15, borderWidth: 2, }}
+447 -203
View File
@@ -25,6 +25,8 @@ import {
CardContent,
ButtonGroup,
DialogContentText,
ToggleButton,
ToggleButtonGroup,
} from "@mui/material";
import { useNavigate, Link } from "react-router-dom";
@@ -87,29 +89,80 @@ const LicencePopup = (props) => {
const [calculatedCores, setCalculatedCores] = useState('600')
const [onpremSelectedValue, setOnpremSelectedValue] = useState(8)
const payasyougo = "Pay as you go"
const [billingCycle, setBillingCycle] = useState("annual")
const [scaleValue, setScaleValue] = useState(
new URLSearchParams(window.location.search).get("app_runs") ||
(userdata?.app_execution_limit / 1000) + 50 || 10
);
useEffect(() => {
setScaleValue((userdata?.app_execution_limit / 1000) + 50 || 10)
}, [userdata])
const getPrice = (basePrice) => {
return Math.round(billingCycle === "annual" ? basePrice * 0.9 : basePrice); // 10% discount for annual
};
const stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : ""
// Handle slider change for Scale plan
const handleScaleChange = (event, newValue) => {
setScaleValue(newValue);
// Add app runs to URL query params
const urlSearchParams = new URLSearchParams(window.location.search);
urlSearchParams.set("app_runs", newValue); // Convert to actual app runs (k to actual number)
const newUrl = `${window.location.pathname}?${urlSearchParams.toString()}`;
window.history.replaceState({}, "", newUrl);
};
// Handle billing cycle change
const handleBillingCycleChange = (event, newValue) => {
if (newValue !== null) {
setBillingCycle(newValue);
if(isCloud){
ReactGA.event({
category: 'Billingpage',
action: 'Billing Cycle Changed',
label: `${billingCycle} -> ${newValue}`,
});
}
// Add billing cycle to URL query params
const urlSearchParams = new URLSearchParams(window.location.search);
urlSearchParams.set("billing_cycle", newValue);
const newUrl = `${
window.location.pathname
}?${urlSearchParams.toString()}`;
window.history.replaceState({}, "", newUrl);
}
};
const payasyougo = "Pay as you go"
const paperStyle = {
padding: 20,
paddingBottom: 30,
borderRadius: theme.palette?.borderRadius,
height: "100%"
}
billingInfo.subscription = {
"active": true,
"name": "Pay as you go",
"price": typecost_single,
"currency": "USD",
"currency_text": "$",
"interval": "app run / month",
"name": userdata?.app_execution_limit === 10000 ? "10,000 App Runs" : "2,000 App Runs",
"price": "Free",
"currency": "Free",
"currency_text": "",
"interval": "",
"description": "Pay as you go",
"features": [
"Basic Support",
"Includes 10.000 app run/month for free. ",
"Pay for what you use with no minimum commitment and cancel anytime."
"Community Support",
`Includes ${userdata?.app_execution_limit === 10000 ? "10,000" : "2,000"} app run/month for free. `,
"Get all 2500+ Apps and 10 Workflows",
"Invite up to 5 users"
],
"limit": 10000,
"limit": userdata?.app_execution_limit === 10000 ? 10000 : 2000,
}
const sendSignatureRequest = (subscription) => {
@@ -151,32 +204,32 @@ const LicencePopup = (props) => {
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"
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 top_text = "Starter Plan"
// 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",
// ]
// }
if (userdata?.app_execution_limit >= 300000) {
subscription.name = "Enterprise"
subscription.currency_text = "$"
subscription.price = typecost_single
subscription.limit = userdata?.app_execution_limit
subscription.interval = "app run / month"
subscription.features = [
"Includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. ",
"Multi-Tenancy and Region-Selection",
"And all other features from /pricing",
]
}
// if (userdata?.app_execution_limit >= 300000) {
// subscription.name = "Enterprise"
// subscription.currency_text = "$"
// subscription.price = typecost_single
// subscription.limit = userdata?.app_execution_limit
// subscription.interval = "app run / month"
// subscription.features = [
// "Includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. ",
// "Multi-Tenancy and Region-Selection",
// "And all other features from /pricing",
// ]
// }
var newPaperstyle = JSON.parse(JSON.stringify(paperStyle))
@@ -189,12 +242,12 @@ const LicencePopup = (props) => {
var showSupport = false
if (subscription.name.includes("default")) {
top_text = "Custom Contract"
newPaperstyle.border = "1px solid #f85a3e"
// newPaperstyle.border = "1px solid #f85a3e"
showSupport = true
}
if (subscription.name.includes("App Run Units")) {
top_text = "Cloud Access"
top_text = "Scale Plan"
showSupport = true
}
@@ -357,7 +410,11 @@ const LicencePopup = (props) => {
</div>
</DialogContent>
</Dialog>
{subscription.active === true && !isScale && <Button style={{ backgroundColor: '#2F2F2F', color: "white", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', width: 144, height: 40 }}>Current Plan</Button> }
{subscription.active === true && !isScale && <Button style={{ backgroundColor: '#2f2f2f', color: "#ffffff", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', fontSize: 13 }}
variant="contained"
color="primary">
Current Plan
</Button>}
<div style={{ display: "flex" }}>
{top_text === "Base Cloud Access" && userdata.has_card_available === true && !isScale ?
<Chip
@@ -381,7 +438,7 @@ const LicencePopup = (props) => {
color="primary"
/>
: null}
<Typography variant="h6" style={{ marginTop: 10, marginBottom: 10, flex: 5, whiteSpace: 'nowrap' }}>
<Typography variant="h6" style={{ marginTop: 25, marginBottom: 10, flex: 5, whiteSpace: 'nowrap' }}>
{top_text}
</Typography>
@@ -396,7 +453,7 @@ const LicencePopup = (props) => {
}}
/>
: null}
{isCloud && highlight === true && top_text !== "Base Cloud Access" ?
{isCloud && highlight === true && top_text !== "Starter Plan" ?
<Tooltip
title="Sign EULA"
placement="top"
@@ -426,7 +483,7 @@ const LicencePopup = (props) => {
{subscription.currency_text}{subscription.price}
</Typography>
<Typography variant="body1" color="textSecondary" style={{ marginLeft: 10, marginTop: 15, marginBottom: 10 }}>
/ {subscription.interval}
{subscription.interval.length > 0 ? `/ ${subscription.interval}` : ""}
</Typography>
</div>
: null}
@@ -517,23 +574,17 @@ const LicencePopup = (props) => {
: null}
</ul>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
{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 ?
userdata?.app_execution_limit && userdata?.app_execution_limit >= 300000 ?
"You have subscribed to the Enterprise plan, which includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. You can increase the limit by upgrading current plan. Contact support@shuffler.io for more information." :
`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.`
userdata?.app_execution_limit && userdata?.app_execution_limit !== 10000 ?
"You have already subscribed to the Scale plan, which includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. You can increase the limit by upgrading current plan. Contact support@shuffler.io for more information." :
`You are using free Starter plan with max ${userdata?.app_execution_limit === 10000 ? "10,000" : "2,000"} runs per month. Upgrade to increase 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.`
}
</Typography>
{isCloud && (userdata.has_card_available === true || selectedOrganization?.Billing?.Email?.length > 0 )?
{/* {isCloud && (userdata.has_card_available === true || selectedOrganization?.Billing?.Email?.length > 0 )?
<div>
<span>Billing email: {BillingEmail}</span>
<Button
@@ -611,15 +662,14 @@ const LicencePopup = (props) => {
</DialogActions>
</Dialog>
</div>
: null}
: null} */}
</div>
{isCloud ? (
<Button
fullWidth
disabled={false}
color="primary"
style={{
marginTop: !userdata.has_card_available ? 20 : 10,
marginTop: !userdata.has_card_available ? 25 : 10,
borderRadius: 4,
height: 40,
fontSize: 16,
@@ -630,20 +680,15 @@ const LicencePopup = (props) => {
}}
onClick={() => {
if (isCloud) {
handlePayasyougo(userdata, selectedOrganization, BillingEmail)
//navigate("/pricing?tab=cloud&highlight=true")
} else {
//window.open("https://shuffler.io/pricing?tab=onprem&highlight=true", "_blank")
handlePayasyougo()
}
console.log("Subscription: ", subscription.name)
if(!subscription.name.includes("App Run Units")) {
window.open("https://discord.gg/B2CBzUm", "_blank")
} else {
window.open("mailto:support@shuffler.io", "_blank")
}
}}
>
{userdata.has_card_available === true ?
"Manage Card Details"
:
"Add Card Details"
}
Get Support
</Button> ) : null}
<Button
variant="outlined"
@@ -709,42 +754,42 @@ const LicencePopup = (props) => {
)
}
useEffect(() => {
console.log("New variant: ", shuffleVariant)
// useEffect(() => {
// console.log("New variant: ", shuffleVariant)
if (shuffleVariant === 1) {
setCalculatedCost("$960")
setSelectedValue(8)
} else {
if (userdata && userdata?.app_execution_limit) {
if (userdata.app_execution_limit >= 300000 && userdata.app_execution_limit < 400000) {
setSelectedValue(400)
setCalculatedCost("$1280")
}else if (userdata?.app_execution_limit >= 400000 && userdata?.app_execution_limit < 500000) {
setSelectedValue(500)
setCalculatedCost("$1600")
} else if (userdata?.app_execution_limit >= 500000 && userdata?.app_execution_limit < 600000) {
setSelectedValue(600)
setCalculatedCost("$1920")
} else if (userdata?.app_execution_limit >= 600000 && userdata?.app_execution_limit < 700000) {
setSelectedValue(700)
setCalculatedCost("$2240")
} else if (userdata?.app_execution_limit >= 700000 && userdata?.app_execution_limit < 800000) {
setSelectedValue(800)
setCalculatedCost("$2560")
} else if (userdata?.app_execution_limit >= 800000 && userdata?.app_execution_limit < 900000) {
setSelectedValue(900)
setCalculatedCost("$2880")
}else {
setCalculatedCost("$960")
setSelectedValue(300)
}
}else {
setCalculatedCost("$960")
setSelectedValue(300)
}
}
}, [shuffleVariant])
// if (shuffleVariant === 1) {
// setCalculatedCost("$960")
// setSelectedValue(8)
// } else {
// if (userdata && userdata?.app_execution_limit) {
// if (userdata.app_execution_limit >= 30000 && userdata.app_execution_limit < 40000) {
// setSelectedValue(400)
// setCalculatedCost("$1280")
// }else if (userdata?.app_execution_limit >= 40000 && userdata?.app_execution_limit < 50000) {
// setSelectedValue(500)
// setCalculatedCost("$1600")
// } else if (userdata?.app_execution_limit >= 500000 && userdata?.app_execution_limit < 600000) {
// setSelectedValue(600)
// setCalculatedCost("$1920")
// } else if (userdata?.app_execution_limit >= 60000 && userdata?.app_execution_limit < 70000) {
// setSelectedValue(700)
// setCalculatedCost("$2240")
// } else if (userdata?.app_execution_limit >= 70000 && userdata?.app_execution_limit < 80000) {
// setSelectedValue(800)
// setCalculatedCost("$2560")
// } else if (userdata?.app_execution_limit >= 80000 && userdata?.app_execution_limit < 90000) {
// setSelectedValue(900)
// setCalculatedCost("$2880")
// }else {
// setCalculatedCost("$960")
// setSelectedValue(300)
// }
// }else {
// setCalculatedCost("$960")
// setSelectedValue(300)
// }
// }
// }, [userdata])
if (typeof window === 'undefined' || window.location === undefined) {
return null
@@ -900,71 +945,121 @@ const LicencePopup = (props) => {
}
console.log("Priceitem: ", shuffleVariant)
// const isLoggedInHandler = () => {
// if (calculatedCost === payasyougo) {
// handlePayasyougo(props.userdata)
// return
// }
// const priceItem =
// window.location.origin === "https://shuffler.io/" || "https://sandbox.shuffler.io/"
// ? shuffleVariant === 0
// ? "price_1PWI3uDzMUgUjxHSffUBwWCy"
// : "price_1PWI8EDzMUgUjxHSfEhUB7oL"
// : shuffleVariant === 0
// ? "price_1PZPSSEJjT17t98NLJoTMYja"
// : "price_1PZPQuEJjT17t98N3yORUtd9";
// const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`
// const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure`
// const quantity = shuffleVariant === 0 ? selectedValue / 100 : selectedValue
// console.log("Priceitem: ", priceItem, quantity, shuffleVariant)
// var checkoutObject = {
// lineItems: [
// {
// price: priceItem,
// quantity: quantity,
// },
// ],
// mode: "subscription",
// billingAddressCollection: "auto",
// successUrl: successUrl,
// cancelUrl: failUrl,
// clientReferenceId: props.userdata.active_org.id,
// }
// if (stripe === undefined || stripe === null || stripe.redirectToCheckout === undefined) {
// window.open("https://shuffler.io/admin?admin_tab=billingstats&payment=stripe_error", "_self")
// }
// 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 isLoggedInHandler = () => {
if (calculatedCost === payasyougo) {
handlePayasyougo(props.userdata)
return
var priceItem;
if (window.location.origin === "https://shuffler.io" || window.location.origin === "https://sandbox.shuffler.io") {
priceItem = billingCycle === "monthly" ? "price_1R66rbEJjT17t98NHIQ78nrz" : "price_1R671UEJjT17t98NzfqWvSG7"
} else if (window.location.origin === "http://localhost:3002") {
priceItem = billingCycle === "monthly" ? "price_1R678hEJjT17t98Nai5J50gs" : "price_1R6c84EJjT17t98NR68gUfT7"
}
const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`;
const failUrl = `${window.location.origin}/pricing?admin_tab=billingstats&payment=failure`;
let quantity;
if (billingCycle === "monthly") {
quantity = scaleValue / 10
} else {
quantity = (scaleValue / 10) * 12
}
redirectToCheckout(priceItem, quantity, successUrl, failUrl);
};
const redirectToCheckout = (priceItem, quantity, successUrl, failUrl) => {
const checkoutObject = {
lineItems: [
{
price: priceItem,
quantity: quantity,
},
],
mode: "subscription",
billingAddressCollection: "auto",
successUrl: successUrl,
cancelUrl: failUrl,
clientReferenceId: userdata.active_org.id,
};
console.log("OBJECT: ", priceItem, checkoutObject);
stripe
.redirectToCheckout(checkoutObject)
.then(function (result) {
console.log("SUCCESS STRIPE?: ", result);
})
.catch(function (error) {
console.error("STRIPE ERROR: ", error);
});
};
const priceItem =
window.location.origin === "https://shuffler.io/" || "https://sandbox.shuffler.io/"
? shuffleVariant === 0
? "price_1PWI3uDzMUgUjxHSffUBwWCy"
: "price_1PWI8EDzMUgUjxHSfEhUB7oL"
: shuffleVariant === 0
? "price_1PZPSSEJjT17t98NLJoTMYja"
: "price_1PZPQuEJjT17t98N3yORUtd9";
const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`
const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure`
const quantity = shuffleVariant === 0 ? selectedValue / 100 : selectedValue
console.log("Priceitem: ", priceItem, quantity, shuffleVariant)
var checkoutObject = {
lineItems: [
{
price: priceItem,
quantity: quantity,
},
],
mode: "subscription",
billingAddressCollection: "auto",
successUrl: successUrl,
cancelUrl: failUrl,
clientReferenceId: props.userdata.active_org.id,
}
if (stripe === undefined || stripe === null || stripe.redirectToCheckout === undefined) {
window.open("https://shuffler.io/admin?admin_tab=billingstats&payment=stripe_error", "_self")
}
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: "",
})
})
}
console.log("Selected Organization: ", selectedOrganization.subscriptions)
return (
<div>
<Grid container spacing={2} columns={16} style={{ flexDirection: "row", flexWrap: "nowrap", borderRadius: '16px', display: "flex", }}>
<Grid container spacing={2} columns={16} style={{ flexDirection: "row", flexWrap: "nowrap", borderRadius: '16px', display: "flex"}}>
<Grid item xs={8}>
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ?
{selectedOrganization.subscriptions === undefined || selectedOrganization.subscriptions === null || selectedOrganization.subscriptions.length === 0 ?
<SubscriptionObject
index={0}
globalUrl={globalUrl}
@@ -976,7 +1071,29 @@ const LicencePopup = (props) => {
subscription={billingInfo.subscription}
highlight={selectedOrganization.subscriptions === undefined || selectedOrganization.subscriptions === null || selectedOrganization.subscriptions.length === 0}
/>
: !isCloud ?
:
selectedOrganization.subscriptions !== undefined &&
selectedOrganization.subscriptions !== null &&
selectedOrganization.subscriptions.length > 0 ?
selectedOrganization.subscriptions
.reverse()
.map((sub, index) => {
return (
<SubscriptionObject
index={index + 1}
globalUrl={globalUrl}
userdata={userdata}
serverside={serverside}
billingInfo={billingInfo}
stripeKey={stripeKey}
selectedOrganization={selectedOrganization}
subscription={sub}
highlight={true}
/>
)
})
: null}
{!isCloud ?
<span style={{ display: "flex", }}>
<SubscriptionObject
index={0}
@@ -1044,61 +1161,184 @@ const LicencePopup = (props) => {
})
: null} */}
</Grid>
<Grid item xs={8}>
<Grid item xs={8} sx={{ height: "100%" }}>
<Grid style={{}}>
{errorMessage.length > 0 ? <Typography variant="h4">Error: {errorMessage}</Typography> : null}
<Card style={{
padding: 20,
borderRadius: theme.palette?.borderRadius,
border: !isScale ? "1px solid #f85a3e" : 'none',
background:
"linear-gradient(to right, #212121, #212121) padding-box, linear-gradient(90deg, #F86744 0%, #F34475 100%) border-box",
borderWidth: "2px",
borderStyle: "solid",
borderColor: "transparent",
}}>
<div>
{ !isScale && <Button style={{ backgroundColor: 'rgba(255, 132, 68, 0.2)', color: "#FF8444", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', }}
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 25,
marginLeft: -2,
}}
>
<Button style={{ backgroundColor: 'rgba(255, 132, 68, 0.2)', color: "#FF8444", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', fontSize: 13 }}
variant="contained"
color="primary">Recommended </Button> }
color="primary">Recommended
</Button>
{
billingCycle === "annual" &&
(
<Box
sx={{
background: "rgba(248, 103, 68, 0.1)",
py: 0.5,
px: 1.5,
borderRadius: "8px",
}}
>
<Typography
sx={{
fontWeight: "bold",
background:
"linear-gradient(90deg, #FF8544 0%, #FB47A0 100%)",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
backgroundClip: "text",
fontSize: {
xs: "12px",
md: "14px",
},
}}
>
10% OFF
</Typography>
</Box>
)
}
</div>
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 10,
marginTop: 10,
}}
>
<Typography variant="h6" style={{ }}>
{scaleValue > 300 ? "Enterprise Plan" : "Scale Plan"}
</Typography>
<Box sx={{ display: "flex", justifyContent: "center" }}>
<ToggleButtonGroup
value={billingCycle}
exclusive
onChange={handleBillingCycleChange}
aria-label="billing cycle"
sx={{
backgroundColor: "rgba(255, 255, 255, 0.1)",
fontFamily: theme.typography.fontFamily,
borderRadius: "30px",
marginTop: -1,
padding: "3px",
"& .MuiToggleButton-root": {
border: "none",
borderRadius: "30px",
color: "#fff",
padding: "6px 22px",
textTransform: "none",
fontSize: {
xs: "12px",
},
"&.Mui-selected": {
backgroundColor: "#fff",
fontFamily: theme.typography.fontFamily,
color: "#1A1A1A",
fontWeight: "bold",
"&:hover": {
backgroundColor: "#fff",
},
},
"&:hover": {
backgroundColor: "rgba(255, 255, 255, 0.2)",
},
},
}}
>
<ToggleButton value="monthly" aria-label="monthly">
Monthly
</ToggleButton>
<ToggleButton value="annual" aria-label="annual">
Annual
</ToggleButton>
</ToggleButtonGroup>
</Box>
</div>
<Typography variant="h6" style={{ marginTop: 10, marginBottom: 10, flex: 5, }}>{shuffleVariant === 1 ? "Scale" : "Enterprise"}</Typography>
<Divider />
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20 }}>
{shuffleVariant === 0 ?
"SaaS / Cloud - Per Month"
:
"Open Source + Scale License"
}
App Runs Units
</Typography>
<Typography style={{ minHeight: 46, cursor: calculatedCores === "Get A Quote" ? "pointer" : "inherit", }} onClick={() => {
if (calculatedCores === "Get A Quote") {
console.log("Clicked on get a quote")
if (window.drift !== undefined) {
window.drift.api.startInteraction({ interactionId: 340785 })
}
}
}}>{calculatedCost}</Typography>
<Typography variant="body1" color="textSecondary" style={{}}>For {shuffleVariant === 1 ? `${selectedValue} CPU cores` : `${selectedValue}k App Runs`}: </Typography>
<div style={{ textAlign: "center" }}>
<Slider
aria-label="Small steps"
style={{ width: "80%", margin: "auto" }}
onChange={(event, newValue) => {
handleChange(event, newValue)
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", gap: 10 , marginTop: 10 }}>
<Typography style={{
fontSize: 24,
marginTop: 7,
marginBottom: 10,
fontWeight: "500",
}}
>
{scaleValue > 300 ? "Let's Talk" : `$${getPrice(32) * (scaleValue / 10)}`}
</Typography>
<Typography
color="text.secondary"
sx={{
fontSize: "14px",
marginBottom: "-2px",
marginLeft: scaleValue > 300 ? 1 : 0,
}}
marks
value={selectedValue}
step={shuffleVariant === 0 ? 100 : 4}
min={shuffleVariant === 0 ? 100 : 8}
max={shuffleVariant === 0 ? 1000 : 32}
valueLabelDisplay="auto"
/>
>
{scaleValue > 300 ? `for ${scaleValue > 500 ? "500k+" : `${scaleValue}k`} App Runs` : `/month for ${scaleValue}k App Runs`}
</Typography>
</div>
<Box sx={{ px: 1 }}>
<Slider
value={scaleValue}
onChange={handleScaleChange}
aria-labelledby="scale-slider"
valueLabelDisplay="auto"
valueLabelFormat={(value) => {
if(value === 510){
return "500k+"
}
return `${value}k`
}}
step={10}
min={10}
max={510}
marks
sx={{
color: "#ff8544",
"& .MuiSlider-thumb": {
width: 15,
height: 15,
},
"& .MuiSlider-valueLabel": {
backgroundColor: "rgba(33, 33, 33, 1)",
color: "rgba(241, 241, 241, 1)",
fontSize: 14,
borderRadius: "4px",
border: "1px solid rgba(73, 73, 73, 1)",
fontFamily: theme?.typography?.fontFamily,
},
}}
/>
</Box>
<div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<span>{defaultTaskIcon}</span>
<Typography style={{ fontSize: 14, marginLeft: 8 }}>Priority Support</Typography>
<Typography style={{ fontSize: 14, marginLeft: 8 }}>Standard Email Support</Typography>
</div>
<Divider />
<div style={{ display: 'flex', alignItems: 'center' }}>
@@ -1117,7 +1357,7 @@ const LicencePopup = (props) => {
<Divider />
<div style={{ display: 'flex', alignItems: 'center' }}>
<span>{defaultTaskIcon}</span>
<Typography style={{ fontSize: 14, marginLeft: 8 }}>Help with Workflow and App development</Typography>
<Typography style={{ fontSize: 14, marginLeft: 8 }}>30 Days workflow run history</Typography>
</div>
</div>
<div style={{ marginTop: 20, }} />
@@ -1136,7 +1376,7 @@ const LicencePopup = (props) => {
navigate("/pricing")
} else {
window.open("https://shuffler.io/pricing?tab=onprem", "_blank")
window.open("https://shuffler.io/pricing?tab=Self-Hosted", "_blank")
}
}}
color="primary"
@@ -1149,6 +1389,10 @@ const LicencePopup = (props) => {
style={{ borderRadius: 4, textTransform: "capitalize", color: "#1a1a1a", backgroundColor: "#ff8544", width: "100%", fontSize: 16}}
onClick={() => {
if (isCloud) {
if(scaleValue > 300){
navigate("/contact?category=cloud_enterprise_plan")
return;
}
ReactGA.event({
category: "header",
action: "upgread_clicks_popup",
@@ -1170,7 +1414,7 @@ const LicencePopup = (props) => {
}}
color="primary"
>
Upgrade
{scaleValue > 300 ? "Let's Talk" : "Upgrade"}
</Button>
</div>
</DialogActions>
+1 -2
View File
@@ -1363,7 +1363,6 @@ const Navbar = (props) => {
sx={buttonStyles}
onClick={() => {
if(isCloud) {
// navigate("/new-pricing");
navigate("/pricing");
ReactGA.event({
category: "navbar",
@@ -1371,7 +1370,7 @@ const Navbar = (props) => {
label: "go_to_pricing",
})
} else {
window.open("https://shuffler.io/pricing?env=Self-hosted", '_blank');
window.open("https://shuffler.io/pricing?env=Self-Hosted", '_blank');
return;
}
}}
@@ -538,7 +538,7 @@ const OrgHeaderexpandedNew = (props) => {
renderValue={(selected) => selected.join(', ')}
MenuProps={MenuProps}
>
{["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "creator", "tech partner", "old customer", "old lead"].map((name) => (
{["contacted", "lead", "demo done", "pov", "customer", "open source", "student", "internal", "creator", "tech partner", "integration partner", "distribution partner", "service partner", "old customer", "old lead"].map((name) => (
<MenuItem key={name} value={name}>
<Checkbox checked={selectedStatus.indexOf(name) > -1} />
<ListItemText primary={name} />
+64 -25
View File
@@ -682,11 +682,14 @@ const ParsedAction = (props) => {
// Process workflowExecutions
if (workflowExecutions.length > 0) {
var appended = false
var foundvalue = ""
for (let execution of workflowExecutions) {
const execArg = execution.execution_argument;
if (execArg && execArg.length > 0) {
const valid = validateJson(execArg);
if (valid.valid) {
appended = true
newActionList.push({
type: "Runtime Argument",
name: "Runtime Argument",
@@ -697,9 +700,23 @@ const ParsedAction = (props) => {
})
break
} else {
foundvalue = execArg
}
}
}
if (!appended && foundvalue !== undefined && foundvalue !== "") {
newActionList.push({
type: "Runtime Argument",
name: "Runtime Argument",
highlight: "exec",
autocomplete: "exec",
value: foundvalue,
example: foundvalue,
})
}
}
// Add default Runtime Argument if none were added
@@ -788,6 +805,8 @@ const ParsedAction = (props) => {
}
labels.push(parentNode.label);
var secondaryExample = ""
let exampleData = parentNode.example ?? "";
if (parentNode?.app_name === "http") {
exampleData = ""
@@ -798,9 +817,13 @@ const ParsedAction = (props) => {
const foundResult = exec.results?.find(result => result?.action?.id === parentNode?.id);
if (foundResult) {
const valid = validateJson(foundResult.result);
if (valid.valid && valid.result.success !== false) {
exampleData = valid.result
break
if (valid.valid) {
if (valid.result.success !== false) {
exampleData = valid.result
break
}
} else {
secondaryExample = foundResult.result
}
}
}
@@ -834,6 +857,10 @@ const ParsedAction = (props) => {
}
}
}
}
if (exampleData === "" && secondaryExample !== "") {
exampleData = secondaryExample
}
if (parentNode.label === undefined) {
@@ -1178,7 +1205,9 @@ const ParsedAction = (props) => {
selectedAction.parameters[1].value = splitparsed[1]
if (splitparsed.length > 2) {
toast.warn("Filter list only supports filtering at the first level. If you want multi-level filtering, please use the 'execute python' action with the 'filter a list' function in the code editor.")
toast.warn("Filter list only supports filtering on the first list. If you want multi-level filtering, please use the 'execute python' action with the 'filter a list' function in the code editor.", {
autoClose: 10000,
})
} else if (selectedAction.parameters[1].value.includes(".#")) {
toast.warn("This filter may not work due to using .# indexing. Please use the 'execute python' action and try the 'filter a list' function in the code editor.")
}
@@ -1830,28 +1859,37 @@ const ParsedAction = (props) => {
</Tooltip>
</IconButton>
<Button
color="secondary"
variant="outlined"
style={{
marginTop: "auto",
marginBottom: "auto",
height: 30,
marginLeft: 115,
textTransform: "none",
}}
disabled={autoCompleting}
onClick={() => {
if (runFromHere !== undefined) {
runFromHere(selectedAction)
} else {
toast.error("Function not available. Please contact support@shuffler.io")
}
}}
<Tooltip
title={
<Typography variant="body2" style={{margin: 3, }}>
Rerun this action with results from previous executions. Built for testing individual actions in the middle of workflows.
</Typography>
}
placement="top"
>
<PlayArrowIcon style={{marginRight: 5, }}/>
Rerun
</Button>
<Button
color="secondary"
variant="outlined"
style={{
marginTop: "auto",
marginBottom: "auto",
height: 30,
marginLeft: 115,
textTransform: "none",
}}
disabled={autoCompleting}
onClick={() => {
if (runFromHere !== undefined) {
runFromHere(selectedAction)
} else {
toast.error("Function not available. Please contact support@shuffler.io")
}
}}
>
<PlayArrowIcon style={{marginRight: 5, }}/>
Rerun
</Button>
</Tooltip>
{(selectedAction?.generated === true && selectedAction?.app_version === "1.0.0") || (selectedAction?.app_name === "Shuffle Tools" && selectedAction?.app_version !== "1.2.0") ?
<Button
@@ -4561,6 +4599,7 @@ const ParsedAction = (props) => {
minWidth: 250,
maxWidth: 250,
marginRight: 0,
paddingLeft: 12,
}}
value={innerdata}
onMouseOver={() => handleMouseover()}
+7 -4
View File
@@ -54,7 +54,8 @@ const TenantsTab = memo((props) => {
const [cloudSyncModalOpen, setCloudSyncModalOpen] = React.useState(false);
const [modalOpen, setModalOpen] = React.useState(false);
const [parentOrg, setParentOrg] = React.useState(null);
const [parentOrgFlag, setParentOrgFlag] = React.useState(null);
const [parentOrgFlag, setParentOrgFlag] = React.useState("gb");
const [parentOrgRegionName, setParentOrgRegionName] = React.useState("UK");
const [loadOrgs, setLoadOrgs] = React.useState(true);
const [, forceUpdate] = React.useState();
const itemColor = "black";
@@ -82,9 +83,10 @@ const TenantsTab = memo((props) => {
}
}
setParentOrgFlag(regionCode);
setParentOrgRegionName(regiontag);
}
}
}, [parentOrg]);
}, [parentOrg, parentOrgFlag]);
var syncList = [
{
@@ -164,7 +166,8 @@ const TenantsTab = memo((props) => {
regionCode = "ca";
}
}
setParentOrgFlag(regionCode);
setParentOrgFlag(regionCode);
setParentOrgRegionName(regiontag);
}
}
})
@@ -1026,7 +1029,7 @@ const TenantsTab = memo((props) => {
src={`https://flagcdn.com/w20/${parentOrgFlag}.png`}
style={{ width: "30px", height: "20px", marginRight: "5px" }}
/>
<ListItemText primary={parentOrgFlag?.toUpperCase()} />
<ListItemText primary={parentOrgRegionName?.toUpperCase()} />
</div>
}
style={{ display: "table-cell", padding: 8, verticalAlign: "middle" }}
@@ -248,6 +248,14 @@ const UserManagmentTab = memo((props) => {
return;
}
if (event.target.value.includes("ALL")) {
toast.info("Adding to available all sub-organizations. This may take a minute.")
event.target.value = selectedOrganization.child_orgs.map((org) => org.id)
} else if (event.target.value.includes("None")) {
toast.info("Removing from all sub-organizations. This may take a minute")
event.target.value = []
}
console.log("event: ", event.target.value);
setMatchingOrganizations(event.target.value);
// Workaround for empty orgs
@@ -286,6 +294,14 @@ const UserManagmentTab = memo((props) => {
}}
MenuProps={MenuProps}
>
<MenuItem key={-2} value={"None"}>
<Checkbox checked={false} />
<ListItemText primary={"None"} />
</MenuItem>
<MenuItem key={-1} value={"ALL"}>
<Checkbox checked={false} />
<ListItemText primary={"ALL"} />
</MenuItem>
{selectedOrganization.child_orgs.map((org, index) => (
<MenuItem key={index} value={org.id}>
<Checkbox checked={matchingOrganizations.indexOf(org.id) > -1} />
@@ -608,8 +608,6 @@ const WorkflowValidationTimeline = (props) => {
const ballsize = 8
const topMargin = 20
console.log("CHIP: ", index, chipColor, chipBackground)
const chipStyle = {
height: 40,
minWidth: 125,