Added basic organization edit page
This commit is contained in:
+71
-5
@@ -2298,6 +2298,70 @@ func handleGetEnvironments(resp http.ResponseWriter, request *http.Request) {
|
|||||||
resp.Write(newjson)
|
resp.Write(newjson)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleGetOrg(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
cors := handleCors(resp, request)
|
||||||
|
if cors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileId string
|
||||||
|
location := strings.Split(request.URL.String(), "/")
|
||||||
|
if location[1] == "api" {
|
||||||
|
if len(location) <= 4 {
|
||||||
|
log.Printf("Path too short: %d", len(location))
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileId = location[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := handleApiAuthentication(resp, request)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Api authentication failed in set new workflowhandler: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
org, err := getOrg(ctx, fileId)
|
||||||
|
if err != nil {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Failed getting org users"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//FIXME : cleanup org before marshal
|
||||||
|
userFound := false
|
||||||
|
for _, foundUser := range org.Users {
|
||||||
|
if foundUser.Id == user.Id {
|
||||||
|
userFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !userFound {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Use doesn't have access to org"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
org.Users = []User{}
|
||||||
|
org.SyncConfig.Apikey = ""
|
||||||
|
newjson, err := json.Marshal(org)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed unmarshal of org: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write(newjson)
|
||||||
|
}
|
||||||
|
|
||||||
func handleGetOrgs(resp http.ResponseWriter, request *http.Request) {
|
func handleGetOrgs(resp http.ResponseWriter, request *http.Request) {
|
||||||
cors := handleCors(resp, request)
|
cors := handleCors(resp, request)
|
||||||
if cors {
|
if cors {
|
||||||
@@ -2312,7 +2376,7 @@ func handleGetOrgs(resp http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.Role != "admin" {
|
if user.Role != "global_admin" {
|
||||||
resp.WriteHeader(401)
|
resp.WriteHeader(401)
|
||||||
resp.Write([]byte(`{"success": false, "reason": "Not admin"}`))
|
resp.Write([]byte(`{"success": false, "reason": "Not admin"}`))
|
||||||
return
|
return
|
||||||
@@ -3507,7 +3571,7 @@ func executeCloudAction(action CloudSyncJob, apikey string) error {
|
|||||||
Reason string `json:"reason"`
|
Reason string `json:"reason"`
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Data: %s", string(respBody))
|
//log.Printf("Data: %s", string(respBody))
|
||||||
responseData := Result{}
|
responseData := Result{}
|
||||||
err = json.Unmarshal(respBody, &responseData)
|
err = json.Unmarshal(respBody, &responseData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -3554,7 +3618,7 @@ func handleNewHook(resp http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Data: %s", string(body))
|
//log.Printf("Data: %s", string(body))
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
var requestdata requestData
|
var requestdata requestData
|
||||||
@@ -6743,7 +6807,7 @@ func remoteOrgJobHandler(org Org, interval int) error {
|
|||||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, org.SyncConfig.Apikey))
|
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, org.SyncConfig.Apikey))
|
||||||
newresp, err := client.Do(req)
|
newresp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed request in org sync: %s", err)
|
//log.Printf("Failed request in org sync: %s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6753,7 +6817,7 @@ func remoteOrgJobHandler(org Org, interval int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Data: %s", respBody)
|
//log.Printf("Data: %s", respBody)
|
||||||
|
|
||||||
err = remoteOrgJobController(org, respBody)
|
err = remoteOrgJobController(org, respBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -7958,6 +8022,8 @@ func initHandlers() {
|
|||||||
// NEW for 0.8.0
|
// NEW for 0.8.0
|
||||||
r.HandleFunc("/api/v1/cloud/setup", handleCloudSetup).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/cloud/setup", handleCloudSetup).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/orgs", handleGetOrgs).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/orgs", handleGetOrgs).Methods("GET", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/orgs/{orgId}", handleGetOrg).Methods("GET", "OPTIONS")
|
||||||
|
//r.HandleFunc("/api/v1/orgs/{orgId}", handleEditOrg).Methods("POST", "OPTIONS")
|
||||||
|
|
||||||
// Important for email, IDS etc. Create this by:
|
// Important for email, IDS etc. Create this by:
|
||||||
// PS: For cloud, this has to use cloud storage.
|
// PS: For cloud, this has to use cloud storage.
|
||||||
|
|||||||
@@ -68,19 +68,22 @@ type ExecutionRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SyncFeatures struct {
|
type SyncFeatures struct {
|
||||||
|
Webhook SyncData `json:"webhook" datastore:"webhook"`
|
||||||
|
Schedule SyncData `json:"schedule" datastore:"schedule"`
|
||||||
|
UserInput SyncData `json:"user_input" datastore:"user_input"`
|
||||||
|
EmailTrigger SyncData `json:"email_trigger" datastore:"email_trigger"`
|
||||||
Apps SyncData `json:"apps" datastore:"apps"`
|
Apps SyncData `json:"apps" datastore:"apps"`
|
||||||
Workflows SyncData `json:"workflows" datastore:"workflows"`
|
Workflows SyncData `json:"workflows" datastore:"workflows"`
|
||||||
Schedules SyncData `json:"schedules" datastore:"schedules"`
|
Schedules SyncData `json:"schedules" datastore:"schedules"`
|
||||||
Autocomplete SyncData `json:"autocomplete" datastore:"autocomplete"`
|
Autocomplete SyncData `json:"autocomplete" datastore:"autocomplete"`
|
||||||
Authentication SyncData `json:"authentication" datastore:"authentication"`
|
Authentication SyncData `json:"authentication" datastore:"authentication"`
|
||||||
Webhook SyncData `json:"webhook" datastore:"webhook"`
|
|
||||||
Schedule SyncData `json:"schedule" datastore:"schedule"`
|
|
||||||
UserInput SyncData `json:"user_input" datastore:"user_input"`
|
|
||||||
EmailTrigger SyncData `json:"email_trigger" datastore:"email_trigger"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncData struct {
|
type SyncData struct {
|
||||||
Active bool `json:"active" datastore:"active"`
|
Active bool `json:"active" datastore:"active"`
|
||||||
|
Name string `json:"name" datastore:"name"`
|
||||||
|
Description string `json:"description" datastore:"description"`
|
||||||
|
Limit int64 `json:"limit" datastore:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncConfig struct {
|
type SyncConfig struct {
|
||||||
@@ -91,6 +94,8 @@ type SyncConfig struct {
|
|||||||
// Role is just used for feedback for a user
|
// Role is just used for feedback for a user
|
||||||
type Org struct {
|
type Org struct {
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
|
Description string `json:"description" datastore:"description"`
|
||||||
|
Image string `json:"image" datastore:"image"`
|
||||||
Id string `json:"id" datastore:"id"`
|
Id string `json:"id" datastore:"id"`
|
||||||
Org string `json:"org" datastore:"org"`
|
Org string `json:"org" datastore:"org"`
|
||||||
Users []User `json:"users" datastore:"users"`
|
Users []User `json:"users" datastore:"users"`
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ const App = (message, props) => {
|
|||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
position: positions.TOP_CENTER,
|
position: positions.BOTTOM_LEFT,
|
||||||
};
|
};
|
||||||
|
|
||||||
const includedData = window.location.pathname === "/home" || window.location.pathname === "/features" ?
|
const includedData = window.location.pathname === "/home" || window.location.pathname === "/features" ?
|
||||||
|
|||||||
+311
-34
@@ -1,7 +1,10 @@
|
|||||||
import React, { useEffect} from 'react';
|
import React, { useEffect} from 'react';
|
||||||
|
|
||||||
|
import { makeStyles } from '@material-ui/styles';
|
||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import Card from '@material-ui/core/Card';
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import Switch from '@material-ui/core/Switch';
|
import Switch from '@material-ui/core/Switch';
|
||||||
@@ -20,6 +23,7 @@ import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
|||||||
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
|
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import Avatar from '@material-ui/core/Avatar';
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
|
import Zoom from '@material-ui/core/Zoom';
|
||||||
|
|
||||||
|
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
@@ -41,19 +45,30 @@ import ScheduleIcon from '@material-ui/icons/Schedule';
|
|||||||
import CloudIcon from '@material-ui/icons/Cloud';
|
import CloudIcon from '@material-ui/icons/Cloud';
|
||||||
import BusinessIcon from '@material-ui/icons/Business';
|
import BusinessIcon from '@material-ui/icons/Business';
|
||||||
|
|
||||||
const Admin = (props) => {
|
const useStyles = makeStyles({
|
||||||
const { globalUrl } = props;
|
notchedOutline: {
|
||||||
|
borderColor: "#f85a3e !important"
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const Admin = (props) => {
|
||||||
|
const { globalUrl, userdata } = props;
|
||||||
|
|
||||||
|
var upload = ""
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const classes = useStyles();
|
||||||
const [firstRequest, setFirstRequest] = React.useState(true);
|
const [firstRequest, setFirstRequest] = React.useState(true);
|
||||||
const [modalUser, setModalUser] = React.useState({});
|
const [modalUser, setModalUser] = React.useState({});
|
||||||
const [modalOpen, setModalOpen] = React.useState(false);
|
const [modalOpen, setModalOpen] = React.useState(false);
|
||||||
|
const [file, setFile] = React.useState("");
|
||||||
|
const [fileBase64, setFileBase64] = React.useState("");
|
||||||
|
|
||||||
const [cloudSyncModalOpen, setCloudSyncModalOpen] = React.useState(false);
|
const [cloudSyncModalOpen, setCloudSyncModalOpen] = React.useState(false);
|
||||||
const [cloudSyncApikey, setCloudSyncApikey] = React.useState("");
|
const [cloudSyncApikey, setCloudSyncApikey] = React.useState("");
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
|
||||||
const [selectedOrganization, setSelectedOrganization] = React.useState({});
|
const [selectedOrganization, setSelectedOrganization] = React.useState({});
|
||||||
|
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
||||||
const [loginInfo, setLoginInfo] = React.useState("");
|
const [loginInfo, setLoginInfo] = React.useState("");
|
||||||
const [curTab, setCurTab] = React.useState(0);
|
const [curTab, setCurTab] = React.useState(0);
|
||||||
const [users, setUsers] = React.useState([]);
|
const [users, setUsers] = React.useState([]);
|
||||||
@@ -244,6 +259,8 @@ const Admin = (props) => {
|
|||||||
selectedOrganization.cloud_sync = !selectedOrganization.cloud_sync
|
selectedOrganization.cloud_sync = !selectedOrganization.cloud_sync
|
||||||
setSelectedOrganization(selectedOrganization)
|
setSelectedOrganization(selectedOrganization)
|
||||||
setCloudSyncApikey("")
|
setCloudSyncApikey("")
|
||||||
|
|
||||||
|
handleGetOrg(userdata.active_org.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -312,6 +329,50 @@ const Admin = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleGetOrg = (orgId) => {
|
||||||
|
// Just use this one?
|
||||||
|
var baseurl = globalUrl
|
||||||
|
const url = baseurl + '/api/v1/orgs/'+orgId
|
||||||
|
fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(response =>
|
||||||
|
response.json().then(responseJson => {
|
||||||
|
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) {
|
||||||
|
console.log(responseJson.sync_features[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
setOrganizationFeatures(lists)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.catch(error => {
|
||||||
|
console.log("Error getting org: ", error)
|
||||||
|
alert.error("Error getting current organization")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const submitUser = (data) => {
|
const submitUser = (data) => {
|
||||||
console.log("INPUT: ", data)
|
console.log("INPUT: ", data)
|
||||||
|
|
||||||
@@ -609,6 +670,11 @@ const Admin = (props) => {
|
|||||||
getUsers()
|
getUsers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedOrganization.id === undefined && userdata !== undefined && userdata.active_org !== undefined) {
|
||||||
|
//setSelectedOrganization(userdata.active_org)
|
||||||
|
handleGetOrg(userdata.active_org.id)
|
||||||
|
}
|
||||||
|
|
||||||
const paperStyle = {
|
const paperStyle = {
|
||||||
maxWidth: 1250,
|
maxWidth: 1250,
|
||||||
margin: "auto",
|
margin: "auto",
|
||||||
@@ -831,19 +897,21 @@ const Admin = (props) => {
|
|||||||
<CloseIcon style={{color: "red"}} />
|
<CloseIcon style={{color: "red"}} />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid item xs={6}>
|
<Grid item xs={4}>
|
||||||
<ListItem>
|
<Card style={{margin: 4, backgroundColor: theme.palette.inputColor, color: "white",}}>
|
||||||
<ListItemAvatar>
|
<ListItem>
|
||||||
<Avatar>
|
<ListItemAvatar>
|
||||||
{primaryIcon}
|
<Avatar>
|
||||||
</Avatar>
|
{primaryIcon}
|
||||||
</ListItemAvatar>
|
</Avatar>
|
||||||
<ListItemText
|
</ListItemAvatar>
|
||||||
primary={primary}
|
<ListItemText
|
||||||
secondary={secondary}
|
primary={primary}
|
||||||
/>
|
secondary={secondary}
|
||||||
{secondaryIcon}
|
/>
|
||||||
</ListItem>
|
{secondaryIcon}
|
||||||
|
</ListItem>
|
||||||
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -853,15 +921,21 @@ const Admin = (props) => {
|
|||||||
{
|
{
|
||||||
"primary": "Workflows",
|
"primary": "Workflows",
|
||||||
"secondary": "",
|
"secondary": "",
|
||||||
"active": false,
|
"active": true,
|
||||||
"icon": <PolymerIcon style={{color: itemColor}}/>,
|
"icon": <PolymerIcon style={{color: itemColor}}/>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"primary": "Apps",
|
"primary": "Apps",
|
||||||
"secondary": "",
|
"secondary": "",
|
||||||
"active": false,
|
"active": true,
|
||||||
"icon": <AppsIcon style={{color: itemColor}}/>,
|
"icon": <AppsIcon style={{color: itemColor}}/>,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"primary": "Organization",
|
||||||
|
"secondary": "",
|
||||||
|
"active": true,
|
||||||
|
"icon": <BusinessIcon style={{color: itemColor}}/>,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const cloudSyncModal =
|
const cloudSyncModal =
|
||||||
@@ -905,7 +979,7 @@ const Admin = (props) => {
|
|||||||
setCloudSyncApikey(event.target.value)
|
setCloudSyncApikey(event.target.value)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button disabled={(!selectedOrganization.cloud_sync && cloudSyncApikey.length === 0) || loading} variant="contained" style={{ marginLeft: 15, height: 60, margin: "auto", borderRadius: "0px" }} onClick={() => {
|
<Button disabled={(!selectedOrganization.cloud_sync && cloudSyncApikey.length === 0) || loading} variant="contained" style={{ marginLeft: 15, height: 50, borderRadius: "0px" }} onClick={() => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
enableCloudSync(
|
enableCloudSync(
|
||||||
cloudSyncApikey,
|
cloudSyncApikey,
|
||||||
@@ -934,6 +1008,7 @@ const Admin = (props) => {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
* New triggers (userinput, hotmail realtime)<div/>
|
* New triggers (userinput, hotmail realtime)<div/>
|
||||||
* Execute in the cloud rather than onprem<div/>
|
* Execute in the cloud rather than onprem<div/>
|
||||||
* Apps can be built in the cloud<div/>
|
* Apps can be built in the cloud<div/>
|
||||||
@@ -942,6 +1017,206 @@ const Admin = (props) => {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
var image = ""
|
||||||
|
const editHeaderImage = (event) => {
|
||||||
|
const file = event.target.value
|
||||||
|
const actualFile = event.target.files[0]
|
||||||
|
const fileObject = URL.createObjectURL(actualFile)
|
||||||
|
setFile(fileObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file !== "") {
|
||||||
|
const img = document.getElementById('logo')
|
||||||
|
var canvas = document.createElement('canvas')
|
||||||
|
canvas.width = 174
|
||||||
|
canvas.height = 174
|
||||||
|
var ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
img.onload = function() {
|
||||||
|
// img, x, y, width, height
|
||||||
|
//ctx.drawImage(img, 174, 174)
|
||||||
|
//console.log("IMG natural: ", img.naturalWidth, img.naturalHeight)
|
||||||
|
//ctx.drawImage(img, 0, 0, 174, 174)
|
||||||
|
ctx.drawImage(img,
|
||||||
|
0, 0, img.width, img.height,
|
||||||
|
0, 0, canvas.width, canvas.height
|
||||||
|
)
|
||||||
|
|
||||||
|
const canvasUrl = canvas.toDataURL()
|
||||||
|
if (canvasUrl !== fileBase64) {
|
||||||
|
//console.log("SET URL TO: ", canvasUrl)
|
||||||
|
setFileBase64(canvasUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const imageData = file.length > 0 ? file : fileBase64
|
||||||
|
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 ?
|
||||||
|
<div>
|
||||||
|
<Typography variant="h6" style={{marginBottom: "10px", color: "white"}}>Organization overview</Typography>
|
||||||
|
<a target="_blank" href="https://shuffler.io/docs/admin#organization" style={{textDecoration: "none", color: "#f85a3e"}}>Click here to learn more about organization editing</a>
|
||||||
|
{selectedOrganization.id === undefined ?
|
||||||
|
null :
|
||||||
|
<div>
|
||||||
|
<div style={{color: "white", flex: "1", display: "flex", flexDirection: "row"}}>
|
||||||
|
<Tooltip title="Click to edit the app's image" placement="bottom">
|
||||||
|
<div style={{flex: "1", margin: 10, border: "1px solid #f85a3e", cursor: "pointer", backgroundColor: theme.palette.inputColor, maxWidth: 174, maxHeight: 174}} onClick={() => {upload.click()}}>
|
||||||
|
<input hidden type="file" ref={(ref) => upload = ref} onChange={editHeaderImage} />
|
||||||
|
{imageInfo}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
<div style={{flex: "3", color: "white",}}>
|
||||||
|
<div style={{marginTop: 8}}/>
|
||||||
|
Name
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{flex: "1", marginTop: "5px", marginRight: "15px", backgroundColor: theme.palette.inputColor}}
|
||||||
|
fullWidth={true}
|
||||||
|
placeholder="Name"
|
||||||
|
type="name"
|
||||||
|
id="standard-required"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
defaultValue={selectedOrganization.name}
|
||||||
|
onChange={e => {
|
||||||
|
const invalid = ["#", ":", "."]
|
||||||
|
for (var key in invalid) {
|
||||||
|
if (e.target.value.includes(invalid[key])) {
|
||||||
|
alert.error("Can't use "+invalid[key]+" in name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.target.value.length > 100) {
|
||||||
|
alert.error("Choose a shorter name.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedOrganization.name = e.target.value
|
||||||
|
setSelectedOrganization(selectedOrganization)
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
InputProps={{
|
||||||
|
style:{
|
||||||
|
color: "white",
|
||||||
|
height: "50px",
|
||||||
|
fontSize: "1em",
|
||||||
|
},
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div style={{marginTop: "10px"}}/>
|
||||||
|
Description
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{flex: "1", marginTop: "5px", marginRight: "15px", backgroundColor: theme.palette.inputColor}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="A description for the service"
|
||||||
|
defaultValue={selectedOrganization.description}
|
||||||
|
onChange={e => {
|
||||||
|
selectedOrganization.description = e.target.value
|
||||||
|
setSelectedOrganization(selectedOrganization)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style:{
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Divider style={{ marginTop: 20, marginBottom: 20, backgroundColor: theme.palette.inputColor }} />
|
||||||
|
<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.
|
||||||
|
<div style={{display: "flex", marginBottom: 20, }}>
|
||||||
|
<TextField
|
||||||
|
color="primary"
|
||||||
|
style={{backgroundColor: theme.palette.inputColor, marginRight: 10, }}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
height: "50px",
|
||||||
|
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} 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>
|
||||||
|
*/}
|
||||||
|
<Typography style={{marginTop: 40}}>Cloud sync features</Typography>
|
||||||
|
<Grid container style={{width: "100%", marginBottom: 15, }}>
|
||||||
|
{Object.keys(selectedOrganization.sync_features).map(function(key, index) {
|
||||||
|
|
||||||
|
//<GridItem key={index} data={data} />
|
||||||
|
const item = selectedOrganization.sync_features[key]
|
||||||
|
const griditem = {
|
||||||
|
"primary": key,
|
||||||
|
"secondary": "",
|
||||||
|
"active": item.active,
|
||||||
|
"icon": <PolymerIcon style={{color: itemColor}}/>,
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Zoom key={index} >
|
||||||
|
<GridItem data={griditem} />
|
||||||
|
</Zoom>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
|
||||||
const modalView =
|
const modalView =
|
||||||
<Dialog
|
<Dialog
|
||||||
open={modalOpen}
|
open={modalOpen}
|
||||||
@@ -956,10 +1231,10 @@ const Admin = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle><span style={{ color: "white" }}>
|
<DialogTitle><span style={{ color: "white" }}>
|
||||||
{curTab === 0 ? "Add user" : "Add environment"}
|
{curTab === 1 ? "Add user" : "Add environment"}
|
||||||
</span></DialogTitle>
|
</span></DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
{curTab === 0 ?
|
{curTab === 1 ?
|
||||||
<div>
|
<div>
|
||||||
Username
|
Username
|
||||||
<TextField
|
<TextField
|
||||||
@@ -1004,7 +1279,7 @@ const Admin = (props) => {
|
|||||||
onChange={(event) => changeModalData("Password", event.target.value)}
|
onChange={(event) => changeModalData("Password", event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
: curTab === 2 ?
|
: curTab === 3 ?
|
||||||
<div>
|
<div>
|
||||||
Environment Name
|
Environment Name
|
||||||
<TextField
|
<TextField
|
||||||
@@ -1035,9 +1310,9 @@ const Admin = (props) => {
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" style={{ borderRadius: "0px" }} onClick={() => {
|
<Button variant="contained" style={{ borderRadius: "0px" }} onClick={() => {
|
||||||
if (curTab === 0) {
|
if (curTab === 1) {
|
||||||
submitUser(modalUser)
|
submitUser(modalUser)
|
||||||
} else if (curTab === 2) {
|
} else if (curTab === 3) {
|
||||||
submitEnvironment(modalUser)
|
submitEnvironment(modalUser)
|
||||||
}
|
}
|
||||||
}} color="primary">
|
}} color="primary">
|
||||||
@@ -1046,7 +1321,7 @@ const Admin = (props) => {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
const usersView = curTab === 0 ?
|
const usersView = curTab === 1 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{ marginTop: 20, marginBottom: 20, }}>
|
<div style={{ marginTop: 20, marginBottom: 20, }}>
|
||||||
<h2 style={{ display: "inline", }}>User management</h2>
|
<h2 style={{ display: "inline", }}>User management</h2>
|
||||||
@@ -1145,7 +1420,7 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const schedulesView = curTab === 3 ?
|
const schedulesView = curTab === 4 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{marginTop: 20, marginBottom: 20,}}>
|
<div style={{marginTop: 20, marginBottom: 20,}}>
|
||||||
<h2 style={{display: "inline",}}>Schedules</h2>
|
<h2 style={{display: "inline",}}>Schedules</h2>
|
||||||
@@ -1194,7 +1469,7 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const appCategoryView = curTab === 6 ?
|
const appCategoryView = curTab === 7 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{marginTop: 20, marginBottom: 20,}}>
|
<div style={{marginTop: 20, marginBottom: 20,}}>
|
||||||
<h2 style={{display: "inline",}}>Categories</h2>
|
<h2 style={{display: "inline",}}>Categories</h2>
|
||||||
@@ -1270,7 +1545,7 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const authenticationView = curTab === 1 ?
|
const authenticationView = curTab === 2 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{marginTop: 20, marginBottom: 20,}}>
|
<div style={{marginTop: 20, marginBottom: 20,}}>
|
||||||
<h2 style={{display: "inline",}}>App Authentication</h2>
|
<h2 style={{display: "inline",}}>App Authentication</h2>
|
||||||
@@ -1355,7 +1630,7 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const environmentView = curTab === 2 ?
|
const environmentView = curTab === 3 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{marginTop: 20, marginBottom: 20,}}>
|
<div style={{marginTop: 20, marginBottom: 20,}}>
|
||||||
<h2 style={{display: "inline",}}>Environments</h2>
|
<h2 style={{display: "inline",}}>Environments</h2>
|
||||||
@@ -1456,7 +1731,7 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const organizationsTab = curTab === 5 ?
|
const organizationsTab = curTab === 6 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{marginTop: 20, marginBottom: 20,}}>
|
<div style={{marginTop: 20, marginBottom: 20,}}>
|
||||||
<h2 style={{display: "inline",}}>Organizations</h2>
|
<h2 style={{display: "inline",}}>Organizations</h2>
|
||||||
@@ -1537,7 +1812,7 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const hybridTab = curTab === 4 ?
|
const hybridTab = curTab === 5 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{marginTop: 20, marginBottom: 20,}}>
|
<div style={{marginTop: 20, marginBottom: 20,}}>
|
||||||
<h2 style={{display: "inline",}}>Hybrid</h2>
|
<h2 style={{display: "inline",}}>Hybrid</h2>
|
||||||
@@ -1580,13 +1855,13 @@ const Admin = (props) => {
|
|||||||
// primary={environment.Registered ? "true" : "false"}
|
// primary={environment.Registered ? "true" : "false"}
|
||||||
|
|
||||||
const setConfig = (event, newValue) => {
|
const setConfig = (event, newValue) => {
|
||||||
if (newValue === 1) {
|
if (newValue === 2) {
|
||||||
getAppAuthentication()
|
getAppAuthentication()
|
||||||
} else if (newValue === 2) {
|
|
||||||
getEnvironments()
|
|
||||||
} else if (newValue === 3) {
|
} else if (newValue === 3) {
|
||||||
|
getEnvironments()
|
||||||
|
} else if (newValue === 4) {
|
||||||
getSchedules()
|
getSchedules()
|
||||||
} else if (newValue === 5) {
|
} else if (newValue === 6) {
|
||||||
getOrgs()
|
getOrgs()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1608,6 +1883,7 @@ const Admin = (props) => {
|
|||||||
onChange={setConfig}
|
onChange={setConfig}
|
||||||
aria-label="disabled tabs example"
|
aria-label="disabled tabs example"
|
||||||
>
|
>
|
||||||
|
<Tab label=<span><BusinessIcon style={iconStyle} /> Organization</span>/>
|
||||||
<Tab label=<span><AccessibilityNewIcon style={iconStyle} />Users</span> />
|
<Tab label=<span><AccessibilityNewIcon style={iconStyle} />Users</span> />
|
||||||
<Tab label=<span><LockIcon style={iconStyle} />App Authentication</span>/>
|
<Tab label=<span><LockIcon style={iconStyle} />App Authentication</span>/>
|
||||||
<Tab label=<span><EcoIcon style={iconStyle} />Environments</span>/>
|
<Tab label=<span><EcoIcon style={iconStyle} />Environments</span>/>
|
||||||
@@ -1618,6 +1894,7 @@ const Admin = (props) => {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
<Divider style={{marginTop: 0, marginBottom: 10, backgroundColor: "rgb(91, 96, 100)"}} />
|
<Divider style={{marginTop: 0, marginBottom: 10, backgroundColor: "rgb(91, 96, 100)"}} />
|
||||||
<div style={{padding: 15}}>
|
<div style={{padding: 15}}>
|
||||||
|
{organizationView}
|
||||||
{authenticationView}
|
{authenticationView}
|
||||||
{appCategoryView}
|
{appCategoryView}
|
||||||
{usersView}
|
{usersView}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
NAME=worker
|
NAME=worker
|
||||||
VERSION=0.6.0
|
VERSION=0.8.0
|
||||||
|
|
||||||
echo "Running docker build with $NAME:$VERSION"
|
echo "Running docker build with $NAME:$VERSION"
|
||||||
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .
|
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .
|
||||||
|
|||||||
Reference in New Issue
Block a user