import React, { useState, useEffect } from 'react'; import ReactGA from 'react-ga4'; import { useNavigate, Link } from "react-router-dom"; import {isMobile} from "react-device-detect"; import { Done as DoneIcon, Clear as ClearIcon, AddTask as AddTaskIcon, } from '@mui/icons-material'; import { Slider, Divider, List, ListItem, ListItemText, Card, CardContent, Grid, Typography, Button, ButtonGroup, FormControl, Dialog, DialogTitle, DialogActions, DialogContent, Tooltip } from '@mui/material'; import FAQ from "./Faq.jsx"; import Newsletter from "../components/Newsletter.jsx"; import Services from "./Services.jsx"; export const typecost = 0.0018 export const typecost_single = (typecost * 1.33).toFixed(4) // 1. Create 2-3 payment tiers (slider?) // 2. Create a way to show them anywhere // // Site references: // https://logz.io/pricing/ // https://www.avanan.com/pricing const PaymentField = (props) => { const { maxFields, theme, removeAdditions, isLoggedIn } = props const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; // Multiple unused variables here let navigate = useNavigate(); const parsedFields = maxFields === undefined ? 300 : maxFields const [variant, setVariant] = useState(0) const [shuffleVariant, setShuffleVariant] = useState(isCloud ? 0 : 1) const [paymentType, setPaymentType] = useState(0) const [modalOpen, setModalOpen] = useState(false) const [showPricing, ] = useState(true) const [currentPrice, setCurrentPrice] = useState(129) const [isLoaded, setIsLoaded] = useState(false) const [errorMessage, setErrorMessage] = useState("") // Cloud const [calculatedApps, setCalculatedApps] = useState(600) const [calculatedCost, setCalculatedCost] = useState("$600") const [selectedValue, setSelectedValue] = useState(100) // Onprem const [calculatedCores, setCalculatedCores] = useState(600) const [onpremSelectedValue, setOnpremSelectedValue] = useState(8) useEffect(() => { console.log("New variant: ", shuffleVariant) if (shuffleVariant === 1) { setCalculatedCost("$600") setSelectedValue(8) } else { setCalculatedCost("$180") setSelectedValue(100) } }, [shuffleVariant]) if (typeof window === 'undefined' || window.location === undefined) { return null } /* const valuetext = (value, variant) => { console.log("Valuetext: ", value, variant) if (value === 32 || value === 1000) { if (variant === 1) { setCalculatedCores("Get A Quote") setOnpremSelectedValue(value) } else { setCalculatedApps(`Get A Quote`) setSelectedValue(value) } } else { if (variant === 1) { setCalculatedCores(`$${value*75}`) setOnpremSelectedValue(value) } else { setCalculatedApps(`$${value*1000*0.0018}`) setSelectedValue(value) } } } */ const handleChange = (event, newValue) => { console.log("Event, value: ", event.target, newValue) if (shuffleVariant === 1) { setSelectedValue(newValue) if (newValue === 32) { setCalculatedCost(`Get A Quote`) } else { setCalculatedCost(`$${newValue*75}`) } } else { setSelectedValue(newValue) if (newValue === 1000) { setCalculatedCost(`Get A Quote`) } else { setCalculatedCost(`$${newValue*1000*typecost}`) } } } // return `${value}`; //} 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") { //valuetext(8, 1) setShuffleVariant(1) //setCalculatedCores(`$${8*75}`) } } } const billingInfo = Billed anually or monthly at 1.2x the cost //const skipFreemode = window.location.pathname.startsWith("/admin") const skipFreemode = false const maxwidth = isMobile ? "91%" : skipFreemode ? 1100 : 1200 const activeIcon = const inActiveIcon = // All triggers const features = [ { "name": "Users", "basic": "No limit", "community": "No limit", "pro": "No limit", "enterprise": "", "active": true, }, { "name": "Apps", "basic": "No limit", "community": "No limit", "pro": "No limit", "enterprise": "", "active": true, }, { "name": "Workflows", "basic": "No limit", "community": "No limit", "pro": "No limit", "enterprise": "", "active": true, }, { "name": "Workflow App Runs", "basic": "10.000 / month", "community": "10.000 / month", "pro": "Pay as you go", "enterprise": "", "active": true, "onprem": false, }, { "name": "Workflow App Runs", "basic": "No limit", "community": "No limit", "pro": "No limit", "enterprise": "", "active": true, "cloud": false, }, { "name": "Shuffle Datastore (cache)", "basic": "Max 1GB", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "onprem": false, }, { "name": "Shuffle Datastore (cache)", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "cloud": false, }, { "name": "File Storage", "basic": "Max 1GB", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "onprem": false, }, { "name": "File Storage", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "cloud": false, }, { "name": "Multi-Tenant", "basic": "No", "community": "Yes", "pro": "Add-on", "enterprise": "", "active": true, "onprem": false, }, { "name": "Multi-Tenant", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "cloud": false, }, { "name": "Region Control", "basic": "No", "community": "Yes", "pro": "Add-on", "enterprise": "", "active": true, "onprem": false, }, { "name": "Per-CPU-core support", "basic": "0 / month", "community": "0 / month", "pro": "Pay as you go", "enterprise": "8 / month", "active": true, "cloud": false, }, { "name": "Shuffle SMS alerting", "basic": "30 / month", "community": "Yes", "pro": "300 / month", "enterprise": "300 / month", "active": true, }, { "name": "Shuffle Email alerting", "basic": "100 / month", "community": "Yes", "pro": "10.000 / month", "enterprise": "", "active": true, }, { "name": "Priority Support", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": true, "title": "Support & Success", }, { "name": "Maintenance & Updates", "basic": "No", "community": "", "pro": "Yes", "enterprise": "", "active": true, "cloud": false, }, { "name": "Documentation & Community Support", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Email & Chat Support", "basic": "support@shuffler.io", "community": "No", "pro": "Prioritized + Critical issue SLA", "enterprise": "", "active": true, }, { "name": "Personal onboarding", "basic": "No", "community": "", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Shuffle Academy", "basic": "Yes", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Workflow editor", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "title": "Basic features", }, { "name": "App editor", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Private Apps", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Default & Shared playbooks", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Organization control", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Autocomplete features", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Hybrid Webhook trigger", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Hybrid User Input trigger", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Hybrid Email trigger", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Hybrid Schedule", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Failure Notifications", "basic": "Yes", "community": "No", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Hybrid Executions", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Use of Public Workflows", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Multiple Environments", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Shuffle creates integration", "basic": "No", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Uptime SLA", "basic": "No", "community": "No", "pro": "99.9%", "enterprise": "", "active": true, "onprem": false, }, { "name": "Automated backups", "basic": "No", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "onprem": false, }, { "name": "MSSP org overview", "basic": "No", "community": "No", "pro": "Add-on", "enterprise": "", "active": true, }, { "name": "MSSP org control", "basic": "No", "community": "No", "pro": "Add-on", "enterprise": "", "active": true, }, { "name": "Automatic Platform updates", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "onprem": false, }, { "name": "Audit logging", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "2-factor authentication", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "title": "Security & Development", }, { "name": "SAML / SSO", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "API-key management", "basic": "yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Role-based access control (RBAC)", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Workflow recommendations", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, "title": "Additional Features", }, { "name": "Standardized app categories", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "App Framework", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Hybrid search engine", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Hybrid App syncronization", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": true, }, { "name": "Execution Retention", "basic": "1 Month", "community": "Yes", "pro": "1 Year default", "enterprise": "", "active": true, "onprem": false, }, { "name": "Detection Management", "basic": "Yes", "community": "Yes", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Mitre Att&ck integrations", "basic": "No", "community": "Yes", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Open Source account rollback", "basic": "No", "community": "Yes", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Data LOCATION control", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Data RETENTION control", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Shuffle IoC search", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Controllable Reporting", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Management dashboard", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Risk based overview", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "Compliance dashboard", "basic": "No", "community": "No", "pro": "Yes", "enterprise": "", "active": false, }, { "name": "App & Workflow Training", "basic": "$4999 / 5 people", "community": "No", "pro": "$1499 / 5 people", "enterprise": "", "active": true, "title": "Professional Services", }, { "name": "Developer Training", "basic": "$4999 / 5 people", "community": "No", "pro": "$1499 / 5 people", "enterprise": "", "active": true, }, { "name": "Custom App Development", "basic": "Contact us", "community": "No", "pro": "Contact us", "enterprise": "", "active": true, }, { "name": "Custom Workflow Development", "basic": "Contact us", "community": "No", "pro": "Contact us", "enterprise": "", "active": true, }, { "name": "Shuffle Custom Modifications", "basic": "Contact us", "community": "No", "pro": "Contact us", "enterprise": "", "active": true, }, ] const defaultTaskIcon = const stripe = typeof window === 'undefined' || window.location === undefined ? "" : props.stripeKey === undefined ? "" : window.Stripe ? window.Stripe(props.stripeKey) : "" const handleStripeRedirect = (payment_type, recurrence) => { console.log("REDIRECT: ", payment_type, recurrence) // FIXME: Proper redirect cycle here if (props.userdata === undefined || props.userdata.username === undefined || props.userdata.active_org === undefined || props.userdata.active_org.id === undefined) { console.log("User must sign in and have an organization first. Current: ", props.userdata) // 1. Add query parameters: Yearly / monthly, Community / pro navigate(`/register?view=pricing&variant=${variant}&payment_type=${paymentType}&payment_modal=open&message=You need to create a user to continue`) return } else { console.log("Username is ", props.userdata.username) } //payment_type = community(0), pro(1) //recurrence = yearly(0), monthly(1) var priceItem = "price_1Hh8ecDzMUgUjxHSPEdeueyu" var text = "enterprise_yearly_pay_click" // recurrence = 0 = yearly // recurrence = 1 = monthly // if (payment_type === 0) { console.log("Handling payment type 0: hybrid") priceItem = recurrence === 0 ? isCloud ? "price_1HhAOgDzMUgUjxHSmesUZkNU" : "price_1HhAOgDzMUgUjxHSmesUZkNU" : isCloud ? "price_1HhAOgDzMUgUjxHSfU8XzQ84" : "price_1HhAOgDzMUgUjxHSfU8XzQ84" ReactGA.event({ category: "pricing", action: `hybrid_pay_click`, label: "", }) } else if (payment_type === 1) { console.log("Handling payment type 1: enterprise") priceItem = recurrence === 0 ? isCloud ? "price_1HhAdrDzMUgUjxHSsIDOCYgm" : "price_1HhAdrDzMUgUjxHSsIDOCYgm" : isCloud ? "price_1HhAdrDzMUgUjxHS7Cu5vF95" : "price_1HhAdrDzMUgUjxHS7Cu5vF95" if (recurrence === 1) { text = "enterprise_monthly_pay_click" } ReactGA.event({ category: "pricing", action: text, label: "", }) } else if (payment_type === 2) { console.log("Handling payment type 2: basic") priceItem = recurrence === 0 ? isCloud ? "price_1HnPmWDzMUgUjxHSzEHV5e6t" : "price_1HlvuPDzMUgUjxHS1pvtPONJ" : isCloud ? "price_1HnPmWDzMUgUjxHSGC3Yiact" : "price_1HlvuPDzMUgUjxHSrp7Ws8iu" ReactGA.event({ category: "pricing", action: `mssp_pay_click`, label: "", }) } else { console.log(`No handler for redirect ${payment_type} yet`) return } // Current URL + status = Success/fail const successUrl = `${window.location.origin}/admin?payment=success` const failUrl = `${window.location.origin}/pricing?view=failure&variant=${variant}&payment_type=${paymentType}` var checkoutObject = { lineItems: [ {price: priceItem, quantity: 1}, ], mode: "subscription", billingAddressCollection: "auto", successUrl: successUrl, cancelUrl: failUrl, submitType: "donate", clientReferenceId: props.userdata.active_org.id, } stripe.redirectToCheckout(checkoutObject) .then(function (result) { console.log("SUCCESS STRIPE?: ", result) text += "_success" ReactGA.event({ category: "pricing", action: text, label: "", }) }) .catch(function(error) { console.error("STRIPE ERROR: ", error) text += "_fail" ReactGA.event({ category: "pricing", action: text, label: "", }) }); console.log("Done with payment!") } const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)" const level1Button = const level2Button = const level3Button = skipFreemode ? null : const cardStyle = { height: "100%", width: "100%", textAlign: "center", backgroundColor: theme.palette.surfaceColor, color: "white", } var indexskip = 0 const topRet =
Pricing {/*Find pricing, focused on shuffler.io and self-hosted*/} {/*These prices are likely to change*/}
{errorMessage.length > 0 ? Error: {errorMessage} : null}
{skipFreemode ? null : Free {shuffleVariant === 0 ? "shuffler.io / Cloud" : Open Source } {paymentType === 0 ? "Free" : "Free"} {shuffleVariant === 0 ? "Includes 10k App Executions. Refreshes every month." : "Unlimited use, self-hosted." } {defaultTaskIcon} Use any app {defaultTaskIcon} Unlimited users {defaultTaskIcon} Unlimited workflows {defaultTaskIcon} { if (window.drift !== undefined) { window.drift.api.startInteraction({ interactionId: 340043, }) } else { console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) } }}>Free Support & Discord access
{level3Button} } {shuffleVariant === 1 ? "Scale" : "Enterprise"} {shuffleVariant === 0 ? "SaaS / Cloud" : "Open Source + Scale License" } { if (calculatedCores === "Get A Quote") { console.log("Clicked on get a quote") if (window.drift !== undefined) { window.drift.api.startInteraction({ interactionId: 340785 }) } } }}>{calculatedCost} Per month for {shuffleVariant === 1 ? `${selectedValue} CPU cores` : `${selectedValue}k App Executions`}:
{ handleChange(event, newValue) }} marks value={selectedValue} step={shuffleVariant === 0 ? 100 : 4} min={shuffleVariant === 0 ? 100 : 8} max={shuffleVariant === 0 ? 1000 : 32} valueLabelDisplay="auto" />
{defaultTaskIcon} Priority Support {defaultTaskIcon} {shuffleVariant === 0 ? "Multi-Tenant" : "Scalable Orborus" } {shuffleVariant === 0 ? {defaultTaskIcon} Multi-Region Tenants : {defaultTaskIcon} High Availability } {defaultTaskIcon} Help with Workflow and App development
{/*billingInfo*/} {level2Button} {shuffleVariant === 0 ? : null} {/* { if (window.drift !== undefined) { window.drift.api.startInteraction({ interactionId: 340785 }) } else { console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) } }}> Or get a quote */} {/*shuffleVariant === 1 ? null : MSSP Cross-Customer automation ${paymentType === 0 ? 1999 : 2399} Per month{paymentType === 0 ? ", billed yearly" : null} - All previous tiers - Extra Customer control - Sub-organization access - Build Shuffle into your product
*/}
{shuffleVariant === 0 ? "- 100k Executions per month can handle about 500 Assets, and scales linearly." : "- 8 CPU-cores (default) can handle about 1500 Assets and scales linearly." }
- All prices are in USD and exclude VAT {/* Shuffle is an Open Source project. Gives access to support, development and features not otherwise available. This applies to both Open Source & Cloud/SaaS. After the transaction is finished, you will immediately have full access to our support team, and you organization will automatically get upgraded resources assigned. */} Pay-as-you-go-pricing Simple usage based pricing with no long-term commitments Volume Discounts Discounts trigger as your usage grows, so you always get a fair price. Committed-use discounts Get additional discounts for annual or multi-year commitments {!showPricing ? null :
Features ({shuffleVariant === 0 ? "Cloud" : "Self-Hosted"}) {isMobile ? "F" : "Free"} style={{ textAlign: "left", flex: 2}} /> {/*{isMobile ? "H" : "Hybrid"} style={{ textAlign: "left", flex: 2}} />*/} {isMobile ? "E" : "Enterprise / Scale"} style={{ textAlign: "left", flex: 2}} /> {features.slice(0, parsedFields).map((data, index) => { //const activeData = data.active ? activeIcon : data.basic === "No" || data.basic === false ? inActiveIcon : data.basic const basicData = data.basic === "Yes" || data.basic === true ? activeIcon : data.basic === "No" || data.basic === false ? inActiveIcon : data.basic const communityData = data.community === "Yes" || data.community === true ? activeIcon : data.community === "No" || data.community === false ? inActiveIcon : data.community const proData = data.pro === "Yes" || data.pro === true ? activeIcon : data.pro === "No" || data.pro === false ? inActiveIcon : data.pro if (shuffleVariant === 0 && data.cloud === false) { indexskip += 1 return null } if (shuffleVariant === 1 && data.onprem === false) { indexskip += 1 return null } const newindex = index-indexskip return ( {data.title !== undefined ? {data.title} : null} {data.active === true ? : } {/* */} ) })} {features.length > parsedFields ? : null } {/*isMobile ? null :
{level3Button} {level1Button} {level2Button}
*/} {/* */}
}
Scalable models for MSSPs Need support and automation help for diverse and scalable environments? Our Enterprise and MSSP offerings can help you whether Onprem or in our cloud. Open Source CPU-core-based 8 cores included in sub $75 /  CPU-core Per month Cloud App-Execution based Pay-as-you-go ${typecost_single} /  app-runs Per month Cloud, Hybrid & Onprem Our support model is built for both the Cloud and Onpremises version of Shuffle, and can be managed between both. Contact us for more info, or to get a quote from one of our verified resellers. Shuffle MSSP and Open Source
{removeAdditions === true ? null :
Got other questions? If you got more questions about our pricing and plans, please contact us so we can help
}
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 modalView = modalOpen ? { setModalOpen(false) ReactGA.event({ category: "pricing", action: `close_window_outside_click`, label: "", }) }} PaperProps={{ style: { backgroundColor: "#1f2023", color: "white", minWidth: isMobile ? "100%" :500, padding: 30, }, }} >
Shuffle payments
Choose recurrence
Your plan: Shuffle {variant === 0 ? "Community" : variant === 2 ? "Free" : "Pro"} Edition
Monthly subtotal: ${currentPrice}
Discount: {paymentType === 0 ? "20%" : "0%"}
Beta opt-in: Extra features
What you'll pay now: ${paymentType === 0 ? currentPrice*12 : currentPrice}
Your plan will renew each {paymentType === 0 ? "year" : "month"}. {/**/}
: null return (
{topRet} {modalView}
) } export default PaymentField;