import React, { useEffect, useState, useContext } from "react"; import { FormControl, Card, Tooltip, Typography, TextField, Button, Grid, ListItem, ListItemText, ListItemAvatar, IconButton, Avatar, Zoom, InputAdornment, Switch, Skeleton } from "@mui/material"; import { ToastContainer, toast } from "react-toastify"; import { Edit as EditIcon, Polyline as PolylineIcon, CheckCircle as CheckCircleIcon, Close as CloseIcon, Visibility as VisibilityIcon, VisibilityOff as VisibilityOffIcon, } from "@mui/icons-material"; import theme from "../theme.jsx"; import { styled } from '@mui/styles'; import { Context } from "../context/ContextApi.jsx"; const CloudSyncTab = (props) => { const { userdata, globalUrl, serverside } = props; const [cloudSyncApikey, setCloudSyncApikey] = useState(""); const [loading, setLoading] = useState(false); const [showApiKey, setShowApiKey] = useState(false); const [orgSyncResponse, setOrgSyncResponse] = React.useState(""); const [organizationFeatures, setOrganizationFeatures] = React.useState({}); const [selectedOrganization, setSelectedOrganization] = React.useState({}); const [selectedStatus, setSelectedStatus] = React.useState([]); const [orgRequest, setOrgRequest] = React.useState(true); const [userSettings, setUserSettings] = React.useState({}); const [, forceUpdate] = React.useState(); const itemColor = "white"; const isCloud = window?.location?.host === "localhost:3002" || window?.location?.host === "shuffler.io"; useEffect(() => { getSettings(); }, []); const GridItem = (props) => { const [expanded, setExpanded] = React.useState(false); const [showEdit, setShowEdit] = React.useState(false); const [newValue, setNewValue] = React.useState(-100); const primary = props.data.primary; const secondary = props.data.secondary; const primaryIcon = props.data.icon; const secondaryIcon = props.data.active ? : const submitFeatureEdit = (sync_features) => { if (!userdata.support) { console.log("User does not have support access and can't edit features"); return } sync_features.editing = true const data = { org_id: selectedOrganization.id, sync_features: sync_features, }; console.log("sync_features: ", sync_features); const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; 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((response) => response.json().then((responseJson) => { if (responseJson["success"] === false) { toast("Failed updating org: ", responseJson.reason); } else { toast("Successfully edited org!"); } }) ) .catch((error) => { toast("Err: " + error.toString()); }); } const enableFeature = () => { console.log("Enabling " + primary) console.log(selectedOrganization.sync_features) // Check if primary is in sync_features var tmpprimary = primary.replaceAll(" ", "_") if (!(tmpprimary in selectedOrganization.sync_features)) { console.log("Primary not in sync_features: " + tmpprimary) return } if (props.data.active) { selectedOrganization.sync_features[tmpprimary].active = false } else { selectedOrganization.sync_features[tmpprimary].active = true } setSelectedOrganization(selectedOrganization) forceUpdate(Math.random()) submitFeatureEdit(selectedOrganization.sync_features) } const submitEdit = (e) => { e.preventDefault(); e.stopPropagation(); // Check if primary is in sync_features var tmpprimary = primary.replaceAll(" ", "_") if (!(tmpprimary in selectedOrganization.sync_features)) { console.log("Primary not in sync_features: " + tmpprimary) return } // Make it into a number var tmp = parseInt(newValue) if (isNaN(tmp)) { console.log("Not a number: " + newValue) return } selectedOrganization.sync_features[tmpprimary].limit = tmp setSelectedOrganization(selectedOrganization) forceUpdate(Math.random()) submitFeatureEdit(selectedOrganization.sync_features) } const handleToggleFeature = (e) => { // Your logic for toggling the feature's active state console.log(`Toggling ${primary}`); if (!isCloud || userdata.support !== true) { return } e.preventDefault(); e.stopPropagation(); enableFeature() }; return (
{ setExpanded(prev => !prev); if(showEdit){ setShowEdit(false) } }} > {primaryIcon} {isCloud && userdata.support === true ? { e.preventDefault(); e.stopPropagation(); console.log('expanded', expanded) if (expanded){ setExpanded(false) } if (showEdit) { setShowEdit(false) return } console.log("Edit") setShowEdit(true) }} /> : null} {userdata.support === true ?( ):( { if (!isCloud || userdata.support !== true) { return } e.preventDefault(); e.stopPropagation(); enableFeature() }} > {secondaryIcon} )} {expanded ?
Usage:  {props.data.limit === 0 ? ( "Unlimited" ) : ( {props.data.usage} / {props.data.limit === "" ? "Unlimited" : props.data.limit} )} {/* Data sharing: {props.data.data_collection} */} Description: {secondary}
: null} {showEdit ? { console.log("Submit") submitEdit(e) }}> { setNewValue(event.target.value) }} /> : null}
); }; const handleGetOrg = (orgId) => { if (serverside !== true && window.location.search !== undefined && window.location.search !== null) { const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); const foundorgid = params["org_id"]; if (foundorgid !== undefined && foundorgid !== null) { orgId = foundorgid; } } if (orgId.length === 0) { toast("Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."); return; } // Just use this one? fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => { if (response.status === 401) { } return response.json(); }) .then((responseJson) => { if (responseJson["success"] === false) { toast("Failed getting your org. If this persists, please contact support."); } else { if ( responseJson.sync_features === undefined || responseJson.sync_features === null ) { responseJson.sync_features = {}; } setSelectedOrganization(responseJson) var lists = { active: { triggers: [], features: [], sync: [], }, inactive: { triggers: [], features: [], sync: [], }, }; setOrganizationFeatures(lists); } }) .catch((error) => { console.log("Error getting org: ", error); toast("Error getting current organization"); }); }; const handleStopOrgSync = (org_id) => { if (org_id === undefined || org_id === null) { toast("Couldn't get org " + org_id); return; } const data = {}; const url = globalUrl + "/api/v1/orgs/" + org_id + "/stop_sync"; 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((response) => { if (response.status === 200) { console.log("Cloud sync success?"); toast("Successfully stopped cloud sync"); } else { console.log("Cloud sync fail?"); toast( "Failed stopping sync. Try again, and contact support if this persists." ); } return response.json(); }) .then((responseJson) => { setTimeout(() => { handleGetOrg(org_id); }, 1000); }) .catch((error) => { toast("Err: " + error.toString()); }); }; const enableCloudSync = (apikey, organization, disableSync) => { setOrgSyncResponse(""); const data = { apikey: apikey, organization: organization, disable: disableSync, }; const url = globalUrl + "/api/v1/cloud/setup"; 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((response) => { setLoading(false); if (response.status === 200) { console.log("Cloud sync success?"); } else { console.log("Cloud sync fail?"); } return response.json(); //setTimeout(() => { //}, 1000) }) .then((responseJson) => { console.log("RESP: ", responseJson); if ( responseJson.success === false && responseJson.reason !== undefined ) { setOrgSyncResponse(responseJson.reason); toast("Failed to handle sync: " + responseJson.reason); } else if (!responseJson.success) { toast("Failed to handle sync."); } else { //getOrgs(); API no longer in use, as it's in handleInfo request if (disableSync) { toast("Successfully disabled sync!"); setOrgSyncResponse("Successfully disabled syncronization"); } else { toast("Cloud Syncronization successfully set up!"); setOrgSyncResponse( "Successfully started syncronization. Cloud features you now have access to can be seen below." ); } selectedOrganization.cloud_sync = !selectedOrganization.cloud_sync; setSelectedOrganization(selectedOrganization); setCloudSyncApikey(""); handleGetOrg(userdata.active_org.id); } }) .catch((error) => { setLoading(false); toast("Err: " + error.toString()); }); }; const getSettings = () => { fetch(globalUrl + "/api/v1/getsettings", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 when getting settings :O!"); } return response.json(); }) .then((responseJson) => { setUserSettings(responseJson); }) .catch((error) => { console.log(error); }); }; if ( selectedOrganization.id === undefined && userdata !== undefined && userdata.active_org !== undefined && orgRequest === true ) { setOrgRequest(false); handleGetOrg(userdata.active_org.id); } return (

Cloud syncronization

What does cloud sync do? Cloud synchronization is a way of getting more out of Shuffle. Shuffle will ALWAYS make every option open source, but features relying on other users can't be done without a collaborative approach.
{isCloud ? (
Currently syncronizing:{" "} {selectedOrganization.cloud_sync_active === true ? True : False} {selectedOrganization.cloud_sync_active ? ( Syncronization interval:{" "} {selectedOrganization.sync_config.interval === 0 ? "60" : selectedOrganization.sync_config.interval} ) : null} Your Api key {userSettings?.apikey === undefined || userSettings?.apikey === null || userSettings?.apikey?.length <=0 ? ( ):
{ setShowApiKey(!showApiKey) }} > {showApiKey ? : } ) }} required fullWidth={true} disabled={true} autoComplete="cloud apikey" id="apikey_field" margin="normal" placeholder="Cloud Apikey" variant="outlined" value={userSettings?.apikey} defaultValue={userSettings?.apikey} type={!isCloud || showApiKey ? "text" : "password"} /> {selectedOrganization.cloud_sync_active ? ( ) : null}
}
) : (
{ setCloudSyncApikey(event.target.value); }} />
{orgSyncResponse.length > 0 ? ( Message from Shuffle Cloud: {orgSyncResponse} ) : null}
)}

Features

Features and Limitations that are currently available to you in your Cloud or Hybrid Organization. App Executions (App Runs) reset monthly. If the organization is a customer or in a trial, these features limitations are not always enforced. {selectedOrganization.sync_features === undefined || selectedOrganization.sync_features === null ? {[...Array(18)].map((_, i) => (
))}
: Object.keys(selectedOrganization.sync_features).map(function ( key, index ) { if (key === "schedule" || key === "apps" || key === "updates" || key === "editing") { return null; } const item = selectedOrganization.sync_features[key]; if (item === null) { return null } const newkey = key.replaceAll("_", " "); const griditem = { primary: newkey, secondary: item.description === undefined || item.description === null || item.description.length === 0 ? "Not defined yet" : item.description, limit: item.limit, usage: item.usage === undefined || item.usage === null ? 0 : item.usage, data_collection: "None", active: item.active, icon: , }; return ( ); })}
); }; export default CloudSyncTab;