Added organizational EDIT possibility
This commit is contained in:
+126
-1
@@ -72,7 +72,9 @@ var gceProject = "shuffle"
|
|||||||
var bucketName = "shuffler.appspot.com"
|
var bucketName = "shuffler.appspot.com"
|
||||||
var baseAppPath = "/home/frikky/git/shaffuru/tmp/apps"
|
var baseAppPath = "/home/frikky/git/shaffuru/tmp/apps"
|
||||||
var baseDockerName = "frikky/shuffle"
|
var baseDockerName = "frikky/shuffle"
|
||||||
var syncUrl = "http://192.168.3.6:5002"
|
|
||||||
|
//var syncUrl = "http://192.168.102.54:5002"
|
||||||
|
var syncUrl = "http://localhost:5002"
|
||||||
|
|
||||||
var dbclient *datastore.Client
|
var dbclient *datastore.Client
|
||||||
|
|
||||||
@@ -2825,6 +2827,11 @@ func fixUserOrg(ctx context.Context, user *User) *User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if userFound {
|
if userFound {
|
||||||
|
user.PrivateApps = []WorkflowApp{}
|
||||||
|
user.Executions = ExecutionInfo{}
|
||||||
|
user.Limits = UserLimits{}
|
||||||
|
user.Authentication = []UserAuth{}
|
||||||
|
|
||||||
org.Users[orgIndex] = *user
|
org.Users[orgIndex] = *user
|
||||||
} else {
|
} else {
|
||||||
org.Users = append(org.Users, *user)
|
org.Users = append(org.Users, *user)
|
||||||
@@ -7500,6 +7507,123 @@ func handleStopCloudSync(syncUrl string, org Org) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleEditOrg(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
cors := handleCors(resp, request)
|
||||||
|
if cors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := handleApiAuthentication(resp, request)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Api authentication failed in cloud setup: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Role != "admin" {
|
||||||
|
log.Printf("Not admin.")
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Not admin"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(request.Body)
|
||||||
|
if err != nil {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Failed reading body"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnData struct {
|
||||||
|
Image string `json:"image" datastore:"image"`
|
||||||
|
Name string `json:"name" datastore:"name"`
|
||||||
|
Description string `json:"description" datastore:"description"`
|
||||||
|
OrgId string `json:"org_id" datastore:"org_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpData ReturnData
|
||||||
|
err = json.Unmarshal(body, &tmpData)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed unmarshalling test: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpData.OrgId != user.ActiveOrg.Id || fileId != user.ActiveOrg.Id {
|
||||||
|
log.Printf("User can't edit the org")
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "No permission to edit this org"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
org, err := getOrg(ctx, tmpData.OrgId)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Organization doesn't exist: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
admin := false
|
||||||
|
userFound := false
|
||||||
|
for _, inneruser := range org.Users {
|
||||||
|
if inneruser.Id == user.Id {
|
||||||
|
userFound = true
|
||||||
|
if inneruser.Role == "admin" {
|
||||||
|
admin = true
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !userFound {
|
||||||
|
log.Printf("User %s doesn't exist in organization for edit %s", user.Id, org.Id)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !admin {
|
||||||
|
log.Printf("User %s doesn't have edit rights to %s", user.Id, org.Id)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
org.Image = tmpData.Image
|
||||||
|
org.Name = tmpData.Name
|
||||||
|
org.Description = tmpData.Description
|
||||||
|
//log.Printf("Org: %#v", org)
|
||||||
|
err = setOrg(ctx, *org, org.Id)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("User %s doesn't have edit rights to %s", user.Id, org.Id)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Successfully updated org"}`)))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// INFO: https://docs.google.com/drawings/d/1JJebpPeEVEbmH_qsAC6zf9Noygp7PytvesrkhE19QrY/edit
|
// INFO: https://docs.google.com/drawings/d/1JJebpPeEVEbmH_qsAC6zf9Noygp7PytvesrkhE19QrY/edit
|
||||||
/*
|
/*
|
||||||
This is here to both enable and disable cloud sync features for an organization
|
This is here to both enable and disable cloud sync features for an organization
|
||||||
@@ -8023,6 +8147,7 @@ func initHandlers() {
|
|||||||
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}", handleGetOrg).Methods("GET", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/orgs/{orgId}", handleEditOrg).Methods("POST", "OPTIONS")
|
||||||
//r.HandleFunc("/api/v1/orgs/{orgId}", handleEditOrg).Methods("POST", "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:
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ type SyncFeatures struct {
|
|||||||
|
|
||||||
type SyncData struct {
|
type SyncData struct {
|
||||||
Active bool `json:"active" datastore:"active"`
|
Active bool `json:"active" datastore:"active"`
|
||||||
|
Type string `json:"type" datastore:"type"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description"`
|
||||||
Limit int64 `json:"limit" datastore:"limit"`
|
Limit int64 `json:"limit" datastore:"limit"`
|
||||||
@@ -95,7 +96,7 @@ type SyncConfig struct {
|
|||||||
type Org struct {
|
type Org struct {
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description"`
|
||||||
Image string `json:"image" datastore:"image"`
|
Image string `json:"image" datastore:"image,noindex"`
|
||||||
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"`
|
||||||
|
|||||||
+130
-32
@@ -30,6 +30,7 @@ import { useAlert } from "react-alert";
|
|||||||
|
|
||||||
import { Dialog, DialogTitle, DialogActions, DialogContent } from '@material-ui/core';
|
import { Dialog, DialogTitle, DialogActions, DialogContent } from '@material-ui/core';
|
||||||
import { useTheme } from '@material-ui/core/styles';
|
import { useTheme } from '@material-ui/core/styles';
|
||||||
|
import HandlePayment from './HandlePayment'
|
||||||
|
|
||||||
import PolymerIcon from '@material-ui/icons/Polymer';
|
import PolymerIcon from '@material-ui/icons/Polymer';
|
||||||
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
||||||
@@ -253,7 +254,7 @@ const Admin = (props) => {
|
|||||||
if (disableSync) {
|
if (disableSync) {
|
||||||
alert.success("Successfully disabled sync!")
|
alert.success("Successfully disabled sync!")
|
||||||
} else {
|
} else {
|
||||||
alert.success("Sync successfully set up!")
|
alert.success("Cloud Syncronization successfully set up!")
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedOrganization.cloud_sync = !selectedOrganization.cloud_sync
|
selectedOrganization.cloud_sync = !selectedOrganization.cloud_sync
|
||||||
@@ -269,6 +270,51 @@ const Admin = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const orgSaveButton =
|
||||||
|
<Button
|
||||||
|
style={{ width: 150, height: 55, flex: 1 }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => handleEditOrg(selectedOrganization.name, selectedOrganization.description, selectedOrganization.id, selectedOrganization.image)}
|
||||||
|
>
|
||||||
|
Save Changes
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
const handleEditOrg = (name, description, orgId, image) => {
|
||||||
|
const data = {
|
||||||
|
"name": name,
|
||||||
|
"description": description,
|
||||||
|
"org_id": orgId,
|
||||||
|
"image": image,
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
alert.error("Failed updating org")
|
||||||
|
} else {
|
||||||
|
alert.success("Successfully edited org!")
|
||||||
|
setSelectedUserModalOpen(false)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.catch(error => {
|
||||||
|
alert.error("Err: " + error.toString())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const onPasswordChange = () => {
|
const onPasswordChange = () => {
|
||||||
const data = { "username": selectedUser.username, "newpassword": newPassword }
|
const data = { "username": selectedUser.username, "newpassword": newPassword }
|
||||||
const url = globalUrl + '/api/v1/users/passwordchange';
|
const url = globalUrl + '/api/v1/users/passwordchange';
|
||||||
@@ -360,9 +406,13 @@ const Admin = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(responseJson.sync_features).map(function(key, index) {
|
Object.keys(responseJson.sync_features).map(function(key, index) {
|
||||||
console.log(responseJson.sync_features[key])
|
//console.log(responseJson.sync_features[key])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if (responseJson.image !== undefined && responseJson.image !== null && responseJson.image.length > 0) {
|
||||||
|
setFileBase64(responseJson.image)
|
||||||
|
}
|
||||||
setOrganizationFeatures(lists)
|
setOrganizationFeatures(lists)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -668,6 +718,22 @@ const Admin = (props) => {
|
|||||||
if (firstRequest) {
|
if (firstRequest) {
|
||||||
setFirstRequest(false)
|
setFirstRequest(false)
|
||||||
getUsers()
|
getUsers()
|
||||||
|
|
||||||
|
const views = {
|
||||||
|
"organization": 0,
|
||||||
|
"users": 1,
|
||||||
|
"app_auth": 2,
|
||||||
|
"environments": 3,
|
||||||
|
"schedules": 4,
|
||||||
|
"categories": 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.match.params.key !== undefined) {
|
||||||
|
const tmpitem = views[props.match.params.key]
|
||||||
|
if (tmpitem !== undefined) {
|
||||||
|
setCurTab(tmpitem)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedOrganization.id === undefined && userdata !== undefined && userdata.active_org !== undefined) {
|
if (selectedOrganization.id === undefined && userdata !== undefined && userdata.active_org !== undefined) {
|
||||||
@@ -1044,25 +1110,33 @@ const Admin = (props) => {
|
|||||||
|
|
||||||
const canvasUrl = canvas.toDataURL()
|
const canvasUrl = canvas.toDataURL()
|
||||||
if (canvasUrl !== fileBase64) {
|
if (canvasUrl !== fileBase64) {
|
||||||
//console.log("SET URL TO: ", canvasUrl)
|
|
||||||
setFileBase64(canvasUrl)
|
setFileBase64(canvasUrl)
|
||||||
|
selectedOrganization.image = canvasUrl
|
||||||
|
setSelectedOrganization(selectedOrganization)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const imageData = file.length > 0 ? file : fileBase64
|
const imageData = file.length > 0 ? file : fileBase64
|
||||||
|
if (imageData !== undefined && imageData.length === 0) {
|
||||||
|
//imageData
|
||||||
|
}
|
||||||
|
|
||||||
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 ?
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="h6" style={{marginBottom: "10px", color: "white"}}>Organization overview</Typography>
|
<div style={{ marginTop: 20, marginBottom: 20, }}>
|
||||||
<a target="_blank" href="https://shuffler.io/docs/admin#organization" style={{textDecoration: "none", color: "#f85a3e"}}>Click here to learn more about organization editing</a>
|
<h2 style={{ display: "inline", }}>Organization overview</h2>
|
||||||
|
<span style={{ marginLeft: 25 }}>
|
||||||
|
On this page you can configure individual parts of your organization.<a target="_blank" href="https://shuffler.io/docs/admin#organization" style={{textDecoration: "none", color: "#f85a3e"}}>Learn more</a>.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{selectedOrganization.id === undefined ?
|
{selectedOrganization.id === undefined ?
|
||||||
null :
|
null :
|
||||||
<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" 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()}}>
|
<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>
|
||||||
@@ -1111,32 +1185,38 @@ const Admin = (props) => {
|
|||||||
/>
|
/>
|
||||||
<div style={{marginTop: "10px"}}/>
|
<div style={{marginTop: "10px"}}/>
|
||||||
Description
|
Description
|
||||||
<TextField
|
<div style={{display: "flex"}}>
|
||||||
required
|
<TextField
|
||||||
style={{flex: "1", marginTop: "5px", marginRight: "15px", backgroundColor: theme.palette.inputColor}}
|
required
|
||||||
fullWidth={true}
|
style={{flex: "1", marginTop: "5px", marginRight: "15px", backgroundColor: theme.palette.inputColor}}
|
||||||
type="name"
|
fullWidth={true}
|
||||||
id="outlined-with-placeholder"
|
type="name"
|
||||||
margin="normal"
|
id="outlined-with-placeholder"
|
||||||
variant="outlined"
|
margin="normal"
|
||||||
placeholder="A description for the service"
|
variant="outlined"
|
||||||
defaultValue={selectedOrganization.description}
|
placeholder="A description for the service"
|
||||||
onChange={e => {
|
defaultValue={selectedOrganization.description}
|
||||||
selectedOrganization.description = e.target.value
|
onChange={e => {
|
||||||
setSelectedOrganization(selectedOrganization)
|
selectedOrganization.description = e.target.value
|
||||||
}}
|
setSelectedOrganization(selectedOrganization)
|
||||||
InputProps={{
|
}}
|
||||||
classes: {
|
InputProps={{
|
||||||
notchedOutline: classes.notchedOutline,
|
classes: {
|
||||||
},
|
notchedOutline: classes.notchedOutline,
|
||||||
style:{
|
},
|
||||||
color: "white",
|
style:{
|
||||||
},
|
color: "white",
|
||||||
}}
|
},
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
<div style={{margin: "auto", textalign: "center",}}>
|
||||||
|
{orgSaveButton}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</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, }}>
|
<div style={{display: "flex", marginBottom: 20, }}>
|
||||||
@@ -1582,9 +1662,9 @@ const Admin = (props) => {
|
|||||||
primary="Actions"
|
primary="Actions"
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{authentication === undefined ? null : authentication.map(data => {
|
{authentication === undefined ? null : authentication.map((data, index) => {
|
||||||
return (
|
return (
|
||||||
<ListItem>
|
<ListItem key={index}>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary=<img alt="" src={data.app.large_image} style={{maxWidth: 50,}} />
|
primary=<img alt="" src={data.app.large_image} style={{maxWidth: 50,}} />
|
||||||
style={{minWidth: 150, maxWidth: 150}}
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
@@ -1869,6 +1949,24 @@ const Admin = (props) => {
|
|||||||
console.log("Should get apps for categories.")
|
console.log("Should get apps for categories.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const views = {
|
||||||
|
0: "organization",
|
||||||
|
1: "users",
|
||||||
|
2: "app_auth",
|
||||||
|
3: "environments",
|
||||||
|
4: "schedules",
|
||||||
|
5: "categories",
|
||||||
|
}
|
||||||
|
|
||||||
|
//var theURL = window.location.pathname
|
||||||
|
//FIXME: Add url edits
|
||||||
|
//var theURL = window.location
|
||||||
|
//theURL.replace(`/${views[curTab]}`, `/${views[newValue]}`)
|
||||||
|
//window.history.pushState({"html":response.html,"pageTitle":response.pageTitle},"", urlPath);
|
||||||
|
|
||||||
|
//console.log(newpath)
|
||||||
|
//window.location.pathame = newpath
|
||||||
|
|
||||||
setModalUser({})
|
setModalUser({})
|
||||||
setCurTab(newValue)
|
setCurTab(newValue)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user