Fixed hybrid issues
This commit is contained in:
@@ -74,7 +74,8 @@ var baseAppPath = "/home/frikky/git/shaffuru/tmp/apps"
|
|||||||
var baseDockerName = "frikky/shuffle"
|
var baseDockerName = "frikky/shuffle"
|
||||||
|
|
||||||
//var syncUrl = "http://192.168.102.54:5002"
|
//var syncUrl = "http://192.168.102.54:5002"
|
||||||
var syncUrl = "http://localhost:5002"
|
//var syncUrl = "http://localhost:5002"
|
||||||
|
var syncUrl = "https://shuffler.io"
|
||||||
|
|
||||||
var dbclient *datastore.Client
|
var dbclient *datastore.Client
|
||||||
|
|
||||||
|
|||||||
@@ -68,15 +68,19 @@ type ExecutionRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SyncFeatures struct {
|
type SyncFeatures struct {
|
||||||
Webhook SyncData `json:"webhook" datastore:"webhook"`
|
Webhook SyncData `json:"webhook" datastore:"webhook"`
|
||||||
Schedule SyncData `json:"schedule" datastore:"schedule"`
|
Schedules SyncData `json:"schedules" datastore:"schedules"`
|
||||||
UserInput SyncData `json:"user_input" datastore:"user_input"`
|
UserInput SyncData `json:"user_input" datastore:"user_input"`
|
||||||
EmailTrigger SyncData `json:"email_trigger" datastore:"email_trigger"`
|
SendMail SyncData `json:"send_mail" datastore:"send_mail"`
|
||||||
Apps SyncData `json:"apps" datastore:"apps"`
|
SendSms SyncData `json:"send_sms" datastore:"send_sms"`
|
||||||
Workflows SyncData `json:"workflows" datastore:"workflows"`
|
EmailTrigger SyncData `json:"email_trigger" datastore:"email_trigger"`
|
||||||
Schedules SyncData `json:"schedules" datastore:"schedules"`
|
AppExecutions SyncData `json:"app_executions" datastore:"app_executions"`
|
||||||
Autocomplete SyncData `json:"autocomplete" datastore:"autocomplete"`
|
WorkflowExecutions SyncData `json:"workflow_executions" datastore:"workflow_executions"`
|
||||||
Authentication SyncData `json:"authentication" datastore:"authentication"`
|
Apps SyncData `json:"apps" datastore:"apps"`
|
||||||
|
Workflows SyncData `json:"workflows" datastore:"workflows"`
|
||||||
|
Autocomplete SyncData `json:"autocomplete" datastore:"autocomplete"`
|
||||||
|
Authentication SyncData `json:"authentication" datastore:"authentication"`
|
||||||
|
Schedule SyncData `json:"schedule" datastore:"schedule"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncData struct {
|
type SyncData struct {
|
||||||
@@ -1836,6 +1840,7 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This is not the right time to send them, BUT it's well served for testing. Save -> send email / sms
|
// FIXME: This is not the right time to send them, BUT it's well served for testing. Save -> send email / sms
|
||||||
|
_ = triggerInformation
|
||||||
if strings.Contains(triggerType, "email") {
|
if strings.Contains(triggerType, "email") {
|
||||||
if email == "test@test.com" {
|
if email == "test@test.com" {
|
||||||
log.Printf("Email isn't specified during save.")
|
log.Printf("Email isn't specified during save.")
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ const App = (message, props) => {
|
|||||||
//console.log(responseJson.success)
|
//console.log(responseJson.success)
|
||||||
setUserData(responseJson)
|
setUserData(responseJson)
|
||||||
setIsLoggedIn(true)
|
setIsLoggedIn(true)
|
||||||
console.log("Cookies: ", cookies)
|
//console.log("Cookies: ", cookies)
|
||||||
|
|
||||||
// Updating cookie every request
|
// Updating cookie every request
|
||||||
for (var key in responseJson["cookies"]) {
|
for (var key in responseJson["cookies"]) {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { createBrowserHistory } from 'history';
|
||||||
|
|
||||||
|
var localExport
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
localExport = createBrowserHistory({forceRefresh: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default localExport
|
||||||
+285
-105
@@ -75,6 +75,9 @@ const Admin = (props) => {
|
|||||||
const [users, setUsers] = React.useState([]);
|
const [users, setUsers] = React.useState([]);
|
||||||
const [organizations, setOrganizations] = React.useState([]);
|
const [organizations, setOrganizations] = React.useState([]);
|
||||||
const [orgSyncResponse, setOrgSyncResponse] = React.useState("");
|
const [orgSyncResponse, setOrgSyncResponse] = React.useState("");
|
||||||
|
const [userSettings, setUserSettings] = React.useState({});
|
||||||
|
const [orgName, setOrgName] = React.useState("");
|
||||||
|
const [orgDescription, setOrgDescription] = React.useState("");
|
||||||
|
|
||||||
const [environments, setEnvironments] = React.useState([]);
|
const [environments, setEnvironments] = React.useState([]);
|
||||||
const [authentication, setAuthentication] = React.useState([]);
|
const [authentication, setAuthentication] = React.useState([]);
|
||||||
@@ -86,6 +89,7 @@ const Admin = (props) => {
|
|||||||
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false)
|
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false)
|
||||||
const [showArchived, setShowArchived] = React.useState(false)
|
const [showArchived, setShowArchived] = React.useState(false)
|
||||||
|
|
||||||
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
|
||||||
const getApps = () => {
|
const getApps = () => {
|
||||||
fetch(globalUrl+"/api/v1/workflows/apps", {
|
fetch(globalUrl+"/api/v1/workflows/apps", {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -275,7 +279,7 @@ const Admin = (props) => {
|
|||||||
style={{ width: 150, height: 55, flex: 1 }}
|
style={{ width: 150, height: 55, flex: 1 }}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => handleEditOrg(selectedOrganization.name, selectedOrganization.description, selectedOrganization.id, selectedOrganization.image)}
|
onClick={() => handleEditOrg(orgName, orgDescription, selectedOrganization.id, selectedOrganization.image)}
|
||||||
>
|
>
|
||||||
Save Changes
|
Save Changes
|
||||||
</Button>
|
</Button>
|
||||||
@@ -303,7 +307,7 @@ const Admin = (props) => {
|
|||||||
.then(response =>
|
.then(response =>
|
||||||
response.json().then(responseJson => {
|
response.json().then(responseJson => {
|
||||||
if (responseJson["success"] === false) {
|
if (responseJson["success"] === false) {
|
||||||
alert.error("Failed updating org")
|
alert.error("Failed updating org: ", responseJson.reason)
|
||||||
} else {
|
} else {
|
||||||
alert.success("Successfully edited org!")
|
alert.success("Successfully edited org!")
|
||||||
setSelectedUserModalOpen(false)
|
setSelectedUserModalOpen(false)
|
||||||
@@ -386,37 +390,44 @@ const Admin = (props) => {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(response =>
|
.then(response => {
|
||||||
response.json().then(responseJson => {
|
if (response.status === 401) {
|
||||||
if (responseJson["success"] === false) {
|
}
|
||||||
alert.error("Failed getting org: ", responseJson.readon)
|
|
||||||
} else {
|
|
||||||
setSelectedOrganization(responseJson)
|
|
||||||
var lists = {
|
|
||||||
"active": {
|
|
||||||
"triggers": [],
|
|
||||||
"features": [],
|
|
||||||
"sync": [],
|
|
||||||
},
|
|
||||||
"inactive": {
|
|
||||||
"triggers": [],
|
|
||||||
"features": [],
|
|
||||||
"sync": [],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(responseJson.sync_features).map(function(key, index) {
|
return response.json()
|
||||||
//console.log(responseJson.sync_features[key])
|
})
|
||||||
})
|
.then(responseJson => {
|
||||||
|
if (responseJson["success"] === false) {
|
||||||
|
alert.error("Failed getting org: ", responseJson.readon)
|
||||||
if (responseJson.image !== undefined && responseJson.image !== null && responseJson.image.length > 0) {
|
} else {
|
||||||
setFileBase64(responseJson.image)
|
setSelectedOrganization(responseJson)
|
||||||
}
|
var lists = {
|
||||||
setOrganizationFeatures(lists)
|
"active": {
|
||||||
|
"triggers": [],
|
||||||
|
"features": [],
|
||||||
|
"sync": [],
|
||||||
|
},
|
||||||
|
"inactive": {
|
||||||
|
"triggers": [],
|
||||||
|
"features": [],
|
||||||
|
"sync": [],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
// FIXME: Set up features
|
||||||
|
Object.keys(responseJson.sync_features).map(function(key, index) {
|
||||||
|
//console.log(responseJson.sync_features[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
setOrgName(responseJson.name)
|
||||||
|
setOrgDescription(responseJson.description)
|
||||||
|
if (responseJson.image !== undefined && responseJson.image !== null && responseJson.image.length > 0) {
|
||||||
|
setFileBase64(responseJson.image)
|
||||||
|
}
|
||||||
|
setOrganizationFeatures(lists)
|
||||||
|
}
|
||||||
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log("Error getting org: ", error)
|
console.log("Error getting org: ", error)
|
||||||
alert.error("Error getting current organization")
|
alert.error("Error getting current organization")
|
||||||
@@ -629,7 +640,7 @@ const Admin = (props) => {
|
|||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (responseJson.success) {
|
if (responseJson.success) {
|
||||||
console.log(responseJson.data)
|
//console.log(responseJson.data)
|
||||||
setAuthentication(responseJson.data)
|
setAuthentication(responseJson.data)
|
||||||
} else {
|
} else {
|
||||||
alert.error("Failed getting authentications")
|
alert.error("Failed getting authentications")
|
||||||
@@ -715,9 +726,38 @@ const Admin = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (firstRequest) {
|
if (firstRequest) {
|
||||||
setFirstRequest(false)
|
setFirstRequest(false)
|
||||||
getUsers()
|
if (!isCloud) {
|
||||||
|
getUsers()
|
||||||
|
} else {
|
||||||
|
getSettings()
|
||||||
|
}
|
||||||
|
|
||||||
const views = {
|
const views = {
|
||||||
"organization": 0,
|
"organization": 0,
|
||||||
@@ -737,7 +777,7 @@ const Admin = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selectedOrganization.id === undefined && userdata !== undefined && userdata.active_org !== undefined) {
|
if (selectedOrganization.id === undefined && userdata !== undefined && userdata.active_org !== undefined) {
|
||||||
//setSelectedOrganization(userdata.active_org)
|
setSelectedOrganization(userdata.active_org)
|
||||||
handleGetOrg(userdata.active_org.id)
|
handleGetOrg(userdata.active_org.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1120,8 +1160,51 @@ const Admin = (props) => {
|
|||||||
var imageData = file.length > 0 ? file : fileBase64
|
var imageData = file.length > 0 ? file : fileBase64
|
||||||
imageData = imageData === undefined || imageData.length === 0 ? defaultImage : imageData
|
imageData = imageData === undefined || imageData.length === 0 ? defaultImage : imageData
|
||||||
|
|
||||||
|
const cancelSubscriptions = (subscription_id) => {
|
||||||
|
console.log(selectedOrganization)
|
||||||
|
const data = {
|
||||||
|
"subscription_id": subscription_id,
|
||||||
|
"action": "cancel",
|
||||||
|
"org_id": selectedOrganization.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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(function(response) {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Error in response")
|
||||||
|
}
|
||||||
|
|
||||||
|
handleGetOrg(selectedOrganization.id)
|
||||||
|
return response.json();
|
||||||
|
}).then(function(responseJson) {
|
||||||
|
if (responseJson.success !== undefined && responseJson.success) {
|
||||||
|
alert.success("Successfully stopped subscription!")
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert.error("Failed stopping subscription. Please contact us.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.log("Error: ", error)
|
||||||
|
alert.error("Failed stopping subscription. Please contact us.")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const imageInfo = <img src={imageData} alt="Click to upload an image (174x174)" id="logo" style={{maxWidth: 174, maxHeight: 174, minWidth: 174, minHeight: 174, objectFit: "contain",}} />
|
const imageInfo = <img src={imageData} alt="Click to upload an image (174x174)" id="logo" style={{maxWidth: 174, maxHeight: 174, minWidth: 174, minHeight: 174, objectFit: "contain",}} />
|
||||||
const organizationView = curTab === 0 ?
|
const organizationView = curTab === 0 && selectedOrganization.id !== undefined ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{ marginTop: 20, marginBottom: 20, }}>
|
<div style={{ marginTop: 20, marginBottom: 20, }}>
|
||||||
<h2 style={{ display: "inline", }}>Organization overview</h2>
|
<h2 style={{ display: "inline", }}>Organization overview</h2>
|
||||||
@@ -1130,17 +1213,18 @@ const Admin = (props) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{selectedOrganization.id === undefined ?
|
{selectedOrganization.id === undefined ?
|
||||||
null :
|
<div style={{height: 250}}/>
|
||||||
|
:
|
||||||
<div>
|
<div>
|
||||||
<div style={{color: "white", flex: "1", display: "flex", flexDirection: "row"}}>
|
<div style={{color: "white", flex: "1", display: "flex", flexDirection: "row"}}>
|
||||||
<Tooltip title="Click to edit the app's image" placement="bottom">
|
<Tooltip title="Click to edit the app's image - 174x174" placement="bottom">
|
||||||
<div style={{flex: "1", margin: "10px 25px 10px 0px", border: imageData !== undefined && imageData.length > 0 ? null : "1px solid #f85a3e", cursor: "pointer", backgroundColor: imageData !== undefined && imageData.length > 0 ? null : theme.palette.inputColor, maxWidth: 174, maxHeight: 174}} onClick={() => {upload.click()}}>
|
<div style={{flex: "1", margin: "10px 25px 10px 0px", border: imageData !== undefined && imageData.length > 0 ? null : "1px solid #f85a3e", cursor: "pointer", backgroundColor: imageData !== undefined && imageData.length > 0 ? null : theme.palette.inputColor, maxWidth: 174, maxHeight: 174}} onClick={() => {upload.click()}}>
|
||||||
<input hidden type="file" ref={(ref) => upload = ref} onChange={editHeaderImage} />
|
<input hidden type="file" ref={(ref) => upload = ref} onChange={editHeaderImage} />
|
||||||
{imageInfo}
|
{imageInfo}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div style={{flex: "3", color: "white",}}>
|
<div style={{flex: "3", color: "white",}}>
|
||||||
<div style={{marginTop: 8}}/>
|
<div style={{marginTop: 8}}/>
|
||||||
Name
|
Name
|
||||||
<TextField
|
<TextField
|
||||||
required
|
required
|
||||||
@@ -1151,7 +1235,7 @@ const Admin = (props) => {
|
|||||||
id="standard-required"
|
id="standard-required"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
defaultValue={selectedOrganization.name}
|
value={orgName}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
const invalid = ["#", ":", "."]
|
const invalid = ["#", ":", "."]
|
||||||
for (var key in invalid) {
|
for (var key in invalid) {
|
||||||
@@ -1166,8 +1250,7 @@ const Admin = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedOrganization.name = e.target.value
|
setOrgName(e.target.value)
|
||||||
setSelectedOrganization(selectedOrganization)
|
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
InputProps={{
|
InputProps={{
|
||||||
@@ -1193,10 +1276,9 @@ const Admin = (props) => {
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
placeholder="A description for the service"
|
placeholder="A description for the service"
|
||||||
defaultValue={selectedOrganization.description}
|
value={orgDescription}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
selectedOrganization.description = e.target.value
|
setOrgDescription(e.target.value)
|
||||||
setSelectedOrganization(selectedOrganization)
|
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
classes: {
|
classes: {
|
||||||
@@ -1214,65 +1296,110 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Divider style={{ marginTop: 20, marginBottom: 20, backgroundColor: theme.palette.inputColor }} />
|
<Divider style={{ marginTop: 20, marginBottom: 20, backgroundColor: theme.palette.inputColor }} />
|
||||||
<HandlePayment />
|
<Typography variant="h6" style={{marginBottom: "10px", color: "white"}}>Cloud syncronization</Typography>
|
||||||
<Typography variant="h6" style={{marginBottom: "10px", color: "white"}}>Cloud syncronization</Typography>
|
What does <a href="https://shuffler.io/docs/hybrid#cloud_sync" target="_blank" style={{textDecoration: "none", color: "#f85a3e"}}>cloud sync</a> do? Cloud syncronization is a way of getting more out of Shuffle. Shuffle will <b>ALWAYS</b> make every option open source, but features relying on other users can't be done without a collaborative approach.
|
||||||
What does <a href="https://shuffler.io/docs/hybrid#cloud_sync" target="_blank" style={{textDecoration: "none", color: "#f85a3e"}}>cloud sync</a> do? Cloud syncronization is a way of getting more out of Shuffle. Shuffle will <b>ALWAYS</b> make every option open source, but features relying on other users can't be done without a collaborative approach.
|
|
||||||
<div style={{display: "flex", marginBottom: 20, }}>
|
{isCloud ?
|
||||||
<TextField
|
<div style={{marginTop: 15, display: "flex"}}>
|
||||||
color="primary"
|
<div style={{flex: 1}}>
|
||||||
style={{backgroundColor: theme.palette.inputColor, marginRight: 10, }}
|
<Typography style={{}}>
|
||||||
InputProps={{
|
Currently syncronizing: {selectedOrganization.cloud_sync_active === true ? "True" : "False"}
|
||||||
style: {
|
</Typography>
|
||||||
height: "50px",
|
{selectedOrganization.cloud_sync_active ?
|
||||||
color: "white",
|
<Typography style={{}}>
|
||||||
fontSize: "1em",
|
Syncronization interval: {selectedOrganization.sync_config.interval === 0 ? "60" : selectedOrganization.sync_config.interval}
|
||||||
},
|
</Typography>
|
||||||
}}
|
:
|
||||||
required
|
null
|
||||||
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} variant="contained" style={{marginTop: 15, height: 50, width: 150,}} onClick={() => {
|
|
||||||
setLoading(true)
|
|
||||||
enableCloudSync(
|
|
||||||
cloudSyncApikey,
|
|
||||||
selectedOrganization,
|
|
||||||
selectedOrganization.cloud_sync,
|
|
||||||
)
|
|
||||||
}} color="primary">
|
|
||||||
{selectedOrganization.cloud_sync ?
|
|
||||||
"Stop sync"
|
|
||||||
:
|
|
||||||
"Start sync"
|
|
||||||
}
|
}
|
||||||
</Button>
|
<Typography style={{whiteSpace: "nowrap", marginTop: 25, marginRight: 10}}>
|
||||||
|
Your Apikey
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
color="primary"
|
||||||
|
style={{backgroundColor: theme.palette.inputColor, }}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
height: "50px",
|
||||||
|
color: "white",
|
||||||
|
fontSize: "1em",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
fullWidth={true}
|
||||||
|
disabled={true}
|
||||||
|
autoComplete="cloud apikey"
|
||||||
|
id="apikey_field"
|
||||||
|
margin="normal"
|
||||||
|
placeholder="Cloud Apikey"
|
||||||
|
variant="outlined"
|
||||||
|
defaultValue={userSettings.apikey}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{orgSyncResponse.length > 0 ?
|
:
|
||||||
<Typography style={{marginTop: 5, marginBottom: 10}}>
|
<div>
|
||||||
Error from Shuffle: {orgSyncResponse}
|
<div style={{display: "flex", marginBottom: 20, }}>
|
||||||
</Typography>
|
<TextField
|
||||||
: null
|
color="primary"
|
||||||
}
|
style={{backgroundColor: theme.palette.inputColor, marginRight: 10, }}
|
||||||
{/*
|
InputProps={{
|
||||||
<Grid container style={{width: "100%", marginBottom: 15, }}>
|
style: {
|
||||||
{syncList.map((data, index) => {
|
height: "50px",
|
||||||
return (
|
color: "white",
|
||||||
<GridItem key={index} data={data} />
|
fontSize: "1em",
|
||||||
)
|
},
|
||||||
})}
|
}}
|
||||||
</Grid>
|
required
|
||||||
*/}
|
fullWidth={true}
|
||||||
<Typography style={{marginTop: 40}}>Cloud sync features</Typography>
|
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} variant="contained" style={{marginTop: 15, height: 50, width: 150,}} onClick={() => {
|
||||||
|
setLoading(true)
|
||||||
|
enableCloudSync(
|
||||||
|
cloudSyncApikey,
|
||||||
|
selectedOrganization,
|
||||||
|
selectedOrganization.cloud_sync,
|
||||||
|
)
|
||||||
|
}} color="primary">
|
||||||
|
{selectedOrganization.cloud_sync ?
|
||||||
|
"Stop sync"
|
||||||
|
:
|
||||||
|
"Start sync"
|
||||||
|
}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{orgSyncResponse.length > 0 ?
|
||||||
|
<Typography style={{marginTop: 5, marginBottom: 10}}>
|
||||||
|
Error from Shuffle: {orgSyncResponse}
|
||||||
|
</Typography>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
{/*
|
||||||
|
<Grid container style={{width: "100%", marginBottom: 15, }}>
|
||||||
|
{syncList.map((data, index) => {
|
||||||
|
return (
|
||||||
|
<GridItem key={index} data={data} />
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
*/}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<Typography style={{marginTop: 40, marginLeft: 10, marginBottom: 5,}}>Cloud sync features</Typography>
|
||||||
<Grid container style={{width: "100%", marginBottom: 15, }}>
|
<Grid container style={{width: "100%", marginBottom: 15, }}>
|
||||||
{Object.keys(selectedOrganization.sync_features).map(function(key, index) {
|
{Object.keys(selectedOrganization.sync_features).map(function(key, index) {
|
||||||
|
if (key === "schedule") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
//<GridItem key={index} data={data} />
|
//<GridItem key={index} data={data} />
|
||||||
const item = selectedOrganization.sync_features[key]
|
const item = selectedOrganization.sync_features[key]
|
||||||
@@ -1290,8 +1417,51 @@ const Admin = (props) => {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Divider style={{ marginTop: 20, marginBottom: 20, backgroundColor: theme.palette.inputColor }} />
|
||||||
|
{isCloud && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 ?
|
||||||
|
<div style={{marginTop: 30, marginBottom: 20}}>
|
||||||
|
<Typography style={{marginTop: 40, marginLeft: 10, marginBottom: 5,}}>
|
||||||
|
Your subscription{selectedOrganization.subscriptions.length > 1 ? "s" : ""}
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={3} style={{marginTop: 15}}>
|
||||||
|
{selectedOrganization.subscriptions.reverse().map((sub, index) => {
|
||||||
|
return (
|
||||||
|
<Grid item key={index} xs={4}>
|
||||||
|
<Card elevation={6} style={{backgroundColor: theme.palette.inputColor, color: "white", padding: 25, textAlign: "left",}}>
|
||||||
|
<b>Type</b>: {sub.level}<div/>
|
||||||
|
<b>Recurrence</b>: {sub.recurrence}<div/>
|
||||||
|
{sub.active ?
|
||||||
|
<div>
|
||||||
|
<b>Started</b>: {new Date(sub.startdate*1000).toISOString()}<div/>
|
||||||
|
<Button variant="outlined" color="primary" style={{marginTop: 15}} onClick={() => {
|
||||||
|
cancelSubscriptions(sub.reference)
|
||||||
|
}}>
|
||||||
|
Cancel subscription
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
<b>Cancelled</b>: {new Date(sub.cancellationdate*1000).toISOString()}<div/>
|
||||||
|
<Typography color="textSecondary">
|
||||||
|
<b>Status</b>: Deactivated
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
<Divider style={{ marginTop: 20, backgroundColor: theme.palette.inputColor }} />
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div style={{backgroundColor: "#1f2023", paddingTop: 25,}}>
|
||||||
|
<HandlePayment stripeKey={props.stripeKey} userdata={userdata} globalUrl={globalUrl} {...props} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
@@ -1508,7 +1678,11 @@ const Admin = (props) => {
|
|||||||
<List>
|
<List>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Interval (seconds)"
|
primary="Interval"
|
||||||
|
style={{maxWidth: 200}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Environment"
|
||||||
style={{maxWidth: 200}}
|
style={{maxWidth: 200}}
|
||||||
/>
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
@@ -1526,6 +1700,10 @@ const Admin = (props) => {
|
|||||||
style={{maxWidth: 200}}
|
style={{maxWidth: 200}}
|
||||||
primary={schedule.seconds}
|
primary={schedule.seconds}
|
||||||
/>
|
/>
|
||||||
|
<ListItemText
|
||||||
|
style={{maxWidth: 200}}
|
||||||
|
primary={schedule.environment}
|
||||||
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={schedule.argument}
|
primary={schedule.argument}
|
||||||
style={{maxWidth: 400, overflow: "hidden"}}
|
style={{maxWidth: 400, overflow: "hidden"}}
|
||||||
@@ -1933,7 +2111,9 @@ const Admin = (props) => {
|
|||||||
// primary={environment.Registered ? "true" : "false"}
|
// primary={environment.Registered ? "true" : "false"}
|
||||||
|
|
||||||
const setConfig = (event, newValue) => {
|
const setConfig = (event, newValue) => {
|
||||||
if (newValue === 2) {
|
if (newValue === 1) {
|
||||||
|
getUsers()
|
||||||
|
} else if (newValue === 2) {
|
||||||
getAppAuthentication()
|
getAppAuthentication()
|
||||||
} else if (newValue === 3) {
|
} else if (newValue === 3) {
|
||||||
getEnvironments()
|
getEnvironments()
|
||||||
@@ -1980,10 +2160,10 @@ const Admin = (props) => {
|
|||||||
aria-label="disabled tabs example"
|
aria-label="disabled tabs example"
|
||||||
>
|
>
|
||||||
<Tab label=<span><BusinessIcon style={iconStyle} /> Organization</span>/>
|
<Tab label=<span><BusinessIcon style={iconStyle} /> Organization</span>/>
|
||||||
<Tab label=<span><AccessibilityNewIcon style={iconStyle} />Users</span> />
|
{isCloud ? null : <Tab label=<span><AccessibilityNewIcon style={iconStyle} />Users</span> />}
|
||||||
<Tab label=<span><LockIcon style={iconStyle} />App Authentication</span>/>
|
{isCloud ? null : <Tab label=<span><LockIcon style={iconStyle} />App Authentication</span>/>}
|
||||||
<Tab label=<span><EcoIcon style={iconStyle} />Environments</span>/>
|
{isCloud ? null : <Tab label=<span><EcoIcon style={iconStyle} />Environments</span>/>}
|
||||||
<Tab label=<span><ScheduleIcon style={iconStyle} />Schedules</span> />
|
{isCloud ? null : <Tab label=<span><ScheduleIcon style={iconStyle} />Schedules</span> />}
|
||||||
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><CloudIcon style={iconStyle} /> Hybrid</span>/> : null}
|
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><CloudIcon style={iconStyle} /> Hybrid</span>/> : null}
|
||||||
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><BusinessIcon style={iconStyle} /> Organizations</span>/> : null}
|
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><BusinessIcon style={iconStyle} /> Organizations</span>/> : null}
|
||||||
{window.location.protocol === "http:" && window.location.port === "3000" ? <Tab label=<span><LockIcon style={iconStyle} />Categories</span>/> : null}
|
{window.location.protocol === "http:" && window.location.port === "3000" ? <Tab label=<span><LockIcon style={iconStyle} />Categories</span>/> : null}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+36
-31
@@ -137,6 +137,7 @@ const Apps = (props) => {
|
|||||||
|
|
||||||
const [isDropzone, setIsDropzone] = React.useState(false);
|
const [isDropzone, setIsDropzone] = React.useState(false);
|
||||||
const upload = React.useRef(null);
|
const upload = React.useRef(null);
|
||||||
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" ? true : false
|
||||||
|
|
||||||
const { start, stop } = useInterval({
|
const { start, stop } = useInterval({
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
@@ -592,7 +593,7 @@ const Apps = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{activateButton}
|
{activateButton}
|
||||||
{(props.userdata.role === "admin" || props.userdata.id === selectedApp.owner) || !selectedApp.generated ?
|
{(props.userdata !== undefined && (props.userdata.role === "admin" || props.userdata.id === selectedApp.owner) || !selectedApp.generated) ?
|
||||||
<div>
|
<div>
|
||||||
{downloadButton}
|
{downloadButton}
|
||||||
{editButton}
|
{editButton}
|
||||||
@@ -618,7 +619,7 @@ const Apps = (props) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
{props.userdata.id === selectedApp.owner ?
|
{props.userdata !== undefined && props.userdata.id === selectedApp.owner ?
|
||||||
<div style={{marginTop: 15}}>
|
<div style={{marginTop: 15}}>
|
||||||
{/*<p><b>ID:</b> {selectedApp.id}</p>*/}
|
{/*<p><b>ID:</b> {selectedApp.id}</p>*/}
|
||||||
<b style={{marginRight: 15}}>Sharing:</b>
|
<b style={{marginRight: 15}}>Sharing:</b>
|
||||||
@@ -794,7 +795,7 @@ const Apps = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uploadFile = (e) => {
|
const uploadFile = (e) => {
|
||||||
const isDropzone = e.dataTransfer?.files.length > 0;
|
const isDropzone = e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
|
||||||
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
@@ -846,37 +847,41 @@ const Apps = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<Divider style={{marginBottom: 10, marginTop: 10, height: "100%", width: 1, backgroundColor: dividerColor}}/>
|
<Divider style={{marginBottom: 10, marginTop: 10, height: "100%", width: 1, backgroundColor: dividerColor}}/>
|
||||||
<div style={{flex: 1, marginLeft: 10, marginRight: 10}}>
|
<div style={{flex: 1, marginLeft: 10, marginRight: 10}}>
|
||||||
<div style={{display: "flex"}}>
|
<div style={{display: "flex", minHeight: 84.81}}>
|
||||||
<div style={{flex: 1}}>
|
<div style={{flex: 1}}>
|
||||||
<h2>Your apps ({apps.length+searchableApps.length})</h2>
|
<h2>Your apps ({apps.length+searchableApps.length})</h2>
|
||||||
</div>
|
</div>
|
||||||
<Tooltip title={"Reload apps locally"} style={{marginTop: "28px", width: "100%"}} aria-label={"Upload"}>
|
{isCloud ? null :
|
||||||
<Button
|
<span>
|
||||||
variant="outlined"
|
<Tooltip title={"Reload apps locally"} style={{marginTop: "28px", width: "100%"}} aria-label={"Upload"}>
|
||||||
component="label"
|
<Button
|
||||||
color="primary"
|
variant="outlined"
|
||||||
style={{margin: 5, maxHeight: 50, marginTop: 10}}
|
component="label"
|
||||||
onClick={() => {
|
color="primary"
|
||||||
hotloadApps()
|
style={{margin: 5, maxHeight: 50, marginTop: 10}}
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
hotloadApps()
|
||||||
<CachedIcon />
|
}}
|
||||||
</Button>
|
>
|
||||||
</Tooltip>
|
<CachedIcon />
|
||||||
<Tooltip title={"Download from Github"} style={{marginTop: "28px", width: "100%"}} aria-label={"Upload"}>
|
</Button>
|
||||||
<Button
|
</Tooltip>
|
||||||
variant="outlined"
|
<Tooltip title={"Download from Github"} style={{marginTop: "28px", width: "100%"}} aria-label={"Upload"}>
|
||||||
component="label"
|
<Button
|
||||||
color="primary"
|
variant="outlined"
|
||||||
style={{margin: 5, maxHeight: 50, marginTop: 10}}
|
component="label"
|
||||||
onClick={() => {
|
color="primary"
|
||||||
setOpenApi(baseRepository)
|
style={{margin: 5, maxHeight: 50, marginTop: 10}}
|
||||||
setLoadAppsModalOpen(true)
|
onClick={() => {
|
||||||
}}
|
setOpenApi(baseRepository)
|
||||||
>
|
setLoadAppsModalOpen(true)
|
||||||
<CloudDownloadIcon />
|
}}
|
||||||
</Button>
|
>
|
||||||
</Tooltip>
|
<CloudDownloadIcon />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<TextField
|
<TextField
|
||||||
style={{backgroundColor: inputColor}}
|
style={{backgroundColor: inputColor}}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const HandlePayment = () => {
|
||||||
|
return (
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HandlePayment
|
||||||
Reference in New Issue
Block a user