sync onprem features/pages with cloud
This commit is contained in:
@@ -0,0 +1,780 @@
|
||||
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 ?
|
||||
<CheckCircleIcon style={{ color: "green" }} />
|
||||
:
|
||||
<CloseIcon style={{ color: "red" }} />
|
||||
|
||||
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 (
|
||||
<Grid
|
||||
item
|
||||
xs={4}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
margin: 4,
|
||||
backgroundColor: "#1a1a1a",
|
||||
borderRadius: 8,
|
||||
color: "white",
|
||||
minHeight: expanded ? 250 : "inherit",
|
||||
maxHeight: expanded ? 300 : "inherit",
|
||||
boxShadow: "none",
|
||||
}}
|
||||
>
|
||||
<ListItem
|
||||
style={{ cursor: "pointer", }}
|
||||
onClick={() => {
|
||||
setExpanded(prev => !prev);
|
||||
if(showEdit){
|
||||
setShowEdit(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar>{primaryIcon}</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
style={{ textTransform: "capitalize", color: "#F1F1F1", fontSize: 14, fontWeight: 400, }}
|
||||
primary={primary}
|
||||
/>
|
||||
{isCloud && userdata.support === true ?
|
||||
<Tooltip title="Edit features (support users only)">
|
||||
<EditIcon
|
||||
color="secondary"
|
||||
style={{ marginRight: 10, cursor: "pointer", }}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
console.log('expanded', expanded)
|
||||
if (expanded){
|
||||
setExpanded(false)
|
||||
}
|
||||
if (showEdit) {
|
||||
setShowEdit(false)
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Edit")
|
||||
|
||||
setShowEdit(true)
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
: null}
|
||||
{userdata.support === true ?(
|
||||
<Tooltip title={props.data.active ? 'Disable feature' : 'Enable feature'}>
|
||||
<Switch
|
||||
checked={props.data.active}
|
||||
onChange={handleToggleFeature}
|
||||
color="primary"
|
||||
inputProps={{ 'aria-label': 'feature toggle' }}
|
||||
sx={{
|
||||
'& .MuiSwitch-switchBase.Mui-checked': {
|
||||
color: '#FFFFFF',
|
||||
},
|
||||
'& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': {
|
||||
backgroundColor: props.data.active ? '#2BC07E' : "#9e9e9e",
|
||||
},
|
||||
"& .MuiSwitch-track": {
|
||||
backgroundColor: props.data.active ? '#2BC07E' : "#9e9e9e"
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
):(
|
||||
<Tooltip title={props.data.active ? "Disable feature" : "Enable feature"}>
|
||||
<span
|
||||
style={{ cursor: "pointer", marginTop: 5, }}
|
||||
onClick={(e) => {
|
||||
if (!isCloud || userdata.support !== true) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
enableFeature()
|
||||
}}
|
||||
>
|
||||
{secondaryIcon}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
</ListItem>
|
||||
{expanded ?
|
||||
<div style={{ padding: 15 }}>
|
||||
<Typography>
|
||||
<b>Usage: </b>
|
||||
{props.data.limit === 0 ? (
|
||||
"Unlimited"
|
||||
) : (
|
||||
<span>
|
||||
{props.data.usage} / {props.data.limit === "" ? "Unlimited" : props.data.limit}
|
||||
</span>
|
||||
)}
|
||||
</Typography>
|
||||
{/*<Typography>
|
||||
Data sharing: {props.data.data_collection}
|
||||
</Typography>*/}
|
||||
<Typography style={{ maxHeight: 150, overflowX: "hidden", overflowY: "auto" }}><b>Description:</b> {secondary}</Typography>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
|
||||
{showEdit ?
|
||||
<FormControl fullWidth onSubmit={(e) => {
|
||||
console.log("Submit")
|
||||
submitEdit(e)
|
||||
}}>
|
||||
<span style={{ display: "flex",}}>
|
||||
|
||||
<TextField
|
||||
style={{ flex: 3, }}
|
||||
color="primary"
|
||||
label={"Edit value"}
|
||||
defaultValue={props.data.limit}
|
||||
sx={{
|
||||
marginTop: 0.5,
|
||||
marginBottom: 0.5
|
||||
}}
|
||||
onChange={(event) => {
|
||||
setNewValue(event.target.value)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
style={{ flex: 1, }}
|
||||
variant="contained"
|
||||
disabled={newValue < -1}
|
||||
onClick={(e) => {
|
||||
console.log("Submit 2")
|
||||
submitEdit(e)
|
||||
}}
|
||||
sx={{
|
||||
marginTop: 0.5,
|
||||
maarginBottom: 0.5
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</span>
|
||||
</FormControl>
|
||||
: null}
|
||||
</div>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
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 (
|
||||
<div style={{padding: "27px 10px 19px 27px",}}>
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
<h2
|
||||
style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}
|
||||
>
|
||||
Cloud syncronization
|
||||
</h2>
|
||||
<span style={{ color: "#C8C8C8", fontSize: 16, fontWeight: 400, }}>
|
||||
What does <a href="/docs/organizations#cloud_sync" target="_blank" rel="noopener noreferrer" style={{ color: "rgba(255, 132, 68, 1)", fontSize: 16, textDecoration: 'none', }}>cloud sync</a> 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.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{isCloud ? (
|
||||
<div style={{ marginTop: 15, display: "flex" }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Typography style={{fontWeight: 400, fontSize: 16, color: "#F1F1F1"}}>
|
||||
Currently syncronizing:{" "}
|
||||
{selectedOrganization.cloud_sync_active === true
|
||||
? <span style={{ color: "#4CFD72", fontSize: 16, marginLeft: 16}}>True</span>
|
||||
: <span style={{ color: "#FD4C62", fontSize: 16, marginLeft: 16 }}>False</span>}
|
||||
</Typography>
|
||||
{selectedOrganization.cloud_sync_active ? (
|
||||
<Typography style={{}}>
|
||||
Syncronization interval:{" "}
|
||||
{selectedOrganization.sync_config.interval === 0
|
||||
? "60"
|
||||
: selectedOrganization.sync_config.interval}
|
||||
</Typography>
|
||||
) : null}
|
||||
<Typography
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
marginTop: 25,
|
||||
marginRight: 10,
|
||||
fontSize: 16,
|
||||
fontWeight: 400,
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
}}
|
||||
>
|
||||
Your Api key
|
||||
</Typography>
|
||||
{userSettings?.apikey === undefined || userSettings?.apikey === null || userSettings?.apikey?.length <=0 ? (
|
||||
<Skeleton variant="rectangular" animation="wave" sx={{backgroundColor: '#212121', border: '1px solid #646464', width: 500, height: 50, marginTop: 2 }}/>
|
||||
):
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
maxWidth: 500,
|
||||
height: 35
|
||||
}}
|
||||
InputProps={{
|
||||
sx: {
|
||||
height: "35px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
backgroundColor: '#212121',
|
||||
},
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle password visibility"
|
||||
onClick={() => {
|
||||
setShowApiKey(!showApiKey)
|
||||
}}
|
||||
>
|
||||
{showApiKey ? <VisibilityIcon /> : <VisibilityOffIcon />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
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 ? (
|
||||
<Button
|
||||
style={{
|
||||
width: 150,
|
||||
height: 50,
|
||||
marginLeft: 10,
|
||||
marginTop: 17,
|
||||
}}
|
||||
variant={
|
||||
selectedOrganization.cloud_sync_active === true
|
||||
? "outlined"
|
||||
: "contained"
|
||||
}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
handleStopOrgSync(selectedOrganization.id);
|
||||
}}
|
||||
>
|
||||
Stop Sync
|
||||
</Button>
|
||||
) : null}
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div style={{ display: "flex", marginBottom: 20 }}>
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{
|
||||
backgroundColor: "#1a1a1a",
|
||||
marginRight: 10,
|
||||
height: 35,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
height: "35px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
required
|
||||
fullWidth={true}
|
||||
disabled={selectedOrganization.cloud_sync}
|
||||
autoComplete="cloud apikey"
|
||||
id="apikey_field"
|
||||
margin="normal"
|
||||
placeholder="Cloud Apikey"
|
||||
variant="outlined"
|
||||
onChange={(event) => {
|
||||
setCloudSyncApikey(event.target.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
disabled={
|
||||
(!selectedOrganization.cloud_sync &&
|
||||
cloudSyncApikey.length === 0) ||
|
||||
loading
|
||||
}
|
||||
style={{ marginTop: 15, height: 35, width: 150, textTransform: 'none', fontSize: 16, color: (!selectedOrganization.cloud_sync &&
|
||||
cloudSyncApikey.length === 0) ||
|
||||
loading ? null : "#1a1a1a", backgroundColor: (!selectedOrganization.cloud_sync &&
|
||||
cloudSyncApikey.length === 0) ||
|
||||
loading? null : "#FF8544" }}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
enableCloudSync(
|
||||
cloudSyncApikey,
|
||||
selectedOrganization,
|
||||
selectedOrganization.cloud_sync
|
||||
);
|
||||
}}
|
||||
color="primary"
|
||||
variant={
|
||||
selectedOrganization.cloud_sync === true
|
||||
? "outlined"
|
||||
: "contained"
|
||||
}
|
||||
>
|
||||
{selectedOrganization.cloud_sync
|
||||
? "Stop sync"
|
||||
: "Start sync"}
|
||||
</Button>
|
||||
</div>
|
||||
{orgSyncResponse.length > 0 ? (
|
||||
<Typography style={{ marginTop: 5, marginBottom: 10 }}>
|
||||
Message from Shuffle Cloud: <b>{orgSyncResponse}</b>
|
||||
</Typography>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h2 style={{ marginLeft: 5, marginTop: 40, marginBottom: 5 }}>
|
||||
Features
|
||||
</h2>
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginBottom: 24, fontSize: 16, fontWeight: 400, marginLeft: 5, color: "#C8C8C8" }}>
|
||||
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. </Typography>
|
||||
<Grid container style={{ width: "100%", marginBottom: 15, }}>
|
||||
|
||||
{selectedOrganization.sync_features === undefined ||
|
||||
selectedOrganization.sync_features === null
|
||||
? <Grid container spacing={2} justifyContent="center">
|
||||
{[...Array(18)].map((_, i) => (
|
||||
<Grid item xs={12} sm={6} md={4} key={i}>
|
||||
<div
|
||||
style={{
|
||||
margin: 4,
|
||||
borderRadius: 8,
|
||||
minHeight: "inherit",
|
||||
maxHeight: "inherit",
|
||||
boxShadow: "none",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Skeleton
|
||||
variant="rectangular"
|
||||
height={50}
|
||||
width={343}
|
||||
sx={{ backgroundColor: '#1a1a1a', display: 'flex', borderRadius: 1 }}
|
||||
animation="wave"
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
: 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: <PolylineIcon style={{ color: "#1a1a1a" }} />,
|
||||
};
|
||||
|
||||
return (
|
||||
<Zoom key={index}>
|
||||
<GridItem data={griditem} />
|
||||
</Zoom>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CloudSyncTab;
|
||||
Reference in New Issue
Block a user