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 { getTheme } 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";
const { themeMode, brandColor, setUpdateOrg } = useContext(Context);
const theme = getTheme(themeMode, brandColor);
useEffect(() => { getSettings(); }, []);
const GridItem = (props) => {
const [expanded, setExpanded] = React.useState(false);
const [showEdit, setShowEdit] = React.useState(false);
const [newValue, setNewValue] = React.useState(-100);
var primary = props.data.primary
const shownName = props.data.newname !== undefined && props.data.newname !== null && props.data.newname !== primary ? props.data.newname : 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)
}}
/>
{
console.log("Submit 2")
submitEdit(e)
}}
sx={{
marginTop: 0.5,
maarginBottom: 0.5
}}
>
Submit
: 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 === undefined || orgId === null || (orgId.length !== 36 && orgId.length !== 0)) {
return
}
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.warn("Failed getting your org. If this persists, please contact support (cloud sync)")
} 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?");
handleGetOrg(userdata.active_org.id);
setUpdateOrg(true);
} 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/Hybrid features are available 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. This will by default back up apps and workflows.
{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 ? (
{
handleStopOrgSync(selectedOrganization.id);
}}
>
Stop Sync
) : null}
}
) : (
{
setCloudSyncApikey(event.target.value);
}}
/>
{
setLoading(true);
enableCloudSync(
cloudSyncApikey,
selectedOrganization,
selectedOrganization.cloud_sync
);
}}
color="primary"
variant={
selectedOrganization.cloud_sync === true
? "outlined"
: "contained"
}
>
{selectedOrganization.cloud_sync
? "Stop sync"
: "Start sync"}
{orgSyncResponse.length > 0 ? (
Message from Shuffle Cloud: {orgSyncResponse}
) : null}
)}
{isCloud ? "Cloud" : "Hybrid"} 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("_", " ");
// Rewrites to frontend names
var newname = newkey
if (newkey === "app executions") {
newname = "app runs"
}
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: ,
newname: newname,
};
return (
);
})}
);
};
export default CloudSyncTab;