import React, { useState, useEffect } from "react"; import ReactGA from 'react-ga4'; import theme from "../theme.jsx"; import countries from "../components/Countries.jsx"; import { Box, Paper, Typography, Divider, Button, Grid, Card, Dialog, DialogTitle, DialogContent, TextField, InputAdornment, IconButton, Chip, Checkbox, Tooltip, Slider, DialogActions, CardContent, ButtonGroup, DialogContentText, ToggleButton, ToggleButtonGroup, } from "@mui/material"; import { useNavigate, Link } from "react-router-dom"; import { Autocomplete } from "@mui/material"; import { toast } from "react-toastify" import { Cached as CachedIcon, ContentCopy as ContentCopyIcon, Draw as DrawIcon, Close as CloseIcon, Done as DoneIcon, Clear as ClearIcon, AddTask as AddTaskIcon, } from "@mui/icons-material"; 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, isScale, isLoggedIn, isMobile, selectedOrganization, isCloud } = 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 [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) const [currentPrice, setCurrentPrice] = useState(129) const [isLoaded, setIsLoaded] = useState(false) const [errorMessage, setErrorMessage] = useState("") const [highlight, setHighlight] = useState(false) // Cloud const [calculatedApps, setCalculatedApps] = useState(600) 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) 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": 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": [ "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": userdata?.app_execution_limit === 10000 ? 10000 : 2000, } const sendSignatureRequest = (subscription) => { const url = `${globalUrl}/api/v1/orgs/${selectedOrganization.id}`; fetch(url, { body: JSON.stringify({ org_id: selectedOrganization.id, subscription: subscription, }), mode: "cors", method: "POST", credentials: "include", crossDomain: true, withCredentials: true, headers: { "Content-Type": "application/json; charset=utf-8", }, }) .then((response) => { if (response.status !== 200) { console.log("Error in response"); } return response.json(); }) .then((responseJson) => { console.log("Response from signature request: ", responseJson); }) .catch((error) => { console.log("Error: ", error); }) } 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 = "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", // ] // } var newPaperstyle = JSON.parse(JSON.stringify(paperStyle)) if (subscription.name === "Enterprise" && subscription.active === true) { top_text = "Enterprise 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 = "Scale Plan" 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" } 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 (
setHovered(true)} // onMouseLeave={() => setHovered(false)} > { e.preventDefault(); setSignatureOpen(false); setTosChecked(false) }} > Read and Accept the EULA { setTosChecked(e.target.checked) }} inputProps={{ 'aria-label': 'primary checkbox' }} /> { setTosChecked(!tosChecked) }}> Accept By clicking the “accept” button, you are signing the document, electronically agreeing that it has the same legal validity and effects as a handwritten signature, and that you have the competent authority to represent and sign on behalf an entity. Need support or have questions? Contact us at support@shuffler.io.
{subscription.active === true && !isScale && }
{top_text === "Base Cloud Access" && userdata.has_card_available === true && !isScale ? { console.log("Clicked chip") }} variant="outlined" color="primary" /> : null} {top_text} {top_text === "Base Cloud Access" && userdata.has_card_available === false ? : null} {isCloud && highlight === true && top_text !== "Starter Plan" ? { setSignatureOpen(true) }} > : null}
{subscription.name} {subscription.currency_text !== undefined ?
{subscription.currency_text}{subscription.price} {subscription.interval.length > 0 ? `/ ${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("Worker License: ")) { const fieldId = "webhook_uri_field_" + index parsedFeature = Use the {feature.split("Worker License: ")[0]} Worker { }} InputProps={{ endAdornment: { var copyText = document.getElementById(fieldId); if (copyText !== undefined && copyText !== null) { console.log("NAVIGATOR: ", navigator); const clipboard = navigator.clipboard; if (clipboard === undefined) { toast("Can only copy over HTTPS (port 3443)"); return; } navigator.clipboard.writeText(copyText.value); copyText.select(); copyText.setSelectionRange( 0, 99999 ); /* For mobile devices */ /* Copy the text inside the text field */ document.execCommand("copy"); toast("Copied Webhook URL"); } else { console.log("Couldn't find webhook URI field: ", copyText); } }} edge="end" > }} fullWidth /> } return (
  • {parsedFeature}
  • ) }) : null}
{ isCloud ? 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.` } {/* {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}
{/*
Schedule Call Now
*/}
) } // 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 >= 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 } const setMonthlyCost = (variant, paymentType) => { setErrorMessage("") if (variant === 0 && paymentType === 0) { setCurrentPrice(129) } else if (variant === 0 && paymentType === 1) { setCurrentPrice(155) } else if (variant === 1 && paymentType === 0) { setCurrentPrice(999) } else if (variant === 1 && paymentType === 1) { setCurrentPrice(1199) } else if (variant === 2 && paymentType === 0) { setCurrentPrice(15) } else if (variant === 2 && paymentType === 1) { setCurrentPrice(18) } } const handleChange = (event, newValue) => { if (shuffleVariant === 1) { setSelectedValue(newValue) if (newValue === 32) { setCalculatedCost(`Get A Quote`) } else { setCalculatedCost(`$${newValue * 120}`) } } else { setSelectedValue(newValue) if (newValue < 300) { setCalculatedCost(`Pay as you go`) } else if (newValue === 1000) { setCalculatedCost(`Get A Quote`) } else { setCalculatedCost(`$${newValue * 1000 * typecost}`) } } } if (!isLoaded) { setIsLoaded(true) const tmpsearch = typeof window === 'undefined' || window.location === undefined ? "" : window.location.search const tmpVar = new URLSearchParams(tmpsearch).get("variant") if (tmpVar !== undefined && tmpVar !== null && tmpVar < 3) { setVariant(parseInt(tmpVar)) } const tmpType = new URLSearchParams(tmpsearch).get("payment_type") if (tmpType !== undefined && tmpType !== null && tmpType < 2) { setPaymentType(parseInt(tmpType)) } const modal = new URLSearchParams(tmpsearch).get("payment_modal") if (modal !== undefined && modal !== null && modal === "open") { setModalOpen(true) } const tmpView = new URLSearchParams(tmpsearch).get("view") if (tmpView !== undefined && tmpView !== null && tmpView === "failure") { setErrorMessage("Something went wrong with your payment. Please try again.") } const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); const foundTab = params["tab"]; if (foundTab !== null && foundTab !== undefined) { if (foundTab === "onprem") { setShuffleVariant(1); } else if (foundTab === "cloud") { setShuffleVariant(0); } } const foundHighlight = params["highlight"]; if (foundHighlight !== null && foundHighlight !== undefined) { setHighlight(true) } } //const skipFreemode = window.location.pathname.startsWith("/admin") const skipFreemode = false const maxwidth = isMobile ? "91%" : skipFreemode ? 1100 : 1200 const activeIcon = const inActiveIcon = const defaultTaskIcon = const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)" const level1Button = const level2Button = const level3Button = skipFreemode ? null : const cardStyle = { // height: "100%", // width: "100%", // textAlign: "center", color: "white", } 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 = () => { 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); }); }; console.log("Selected Organization: ", selectedOrganization.subscriptions) return (
{selectedOrganization.subscriptions === undefined || selectedOrganization.subscriptions === null || selectedOrganization.subscriptions.length === 0 ? : selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 ? selectedOrganization.subscriptions .reverse() .map((sub, index) => { return ( ) }) : null} {!isCloud ? {/* */} : null} {/* {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 ? selectedOrganization.subscriptions .reverse() .map((sub, index) => { return ( ) }) : null} */} {errorMessage.length > 0 ? Error: {errorMessage} : null}
{ billingCycle === "annual" && ( 10% OFF ) }
{scaleValue > 300 ? "Enterprise Plan" : "Scale Plan"} Monthly Annual
App Runs Units
{scaleValue > 300 ? "Let's Talk" : `$${getPrice(32) * (scaleValue / 10)}`} 300 ? 1 : 0, }} > {scaleValue > 300 ? `for ${scaleValue > 500 ? "500k+" : `${scaleValue}k`} App Runs` : `/month for ${scaleValue}k App Runs`}
{ 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, }, }} />
{defaultTaskIcon} Standard Email Support
{defaultTaskIcon} {shuffleVariant === 0 ? "Multi-Tenant" : "Lightning-Fast Workflows"}
{defaultTaskIcon} {shuffleVariant === 0 ? "Multi-Region Tenants" : "High Availability"}
{defaultTaskIcon} 30 Days workflow run history
) } export default LicencePopup;