Minor fixes for frontend
This commit is contained in:
@@ -2,7 +2,7 @@ module main
|
||||
|
||||
go 1.16
|
||||
|
||||
//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
|
||||
replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
|
||||
|
||||
//replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi
|
||||
//replace github.com/frikky/go-elasticsearch => ../../../../git/go-elasticsearch
|
||||
|
||||
@@ -1048,6 +1048,10 @@ func cloudExecuteAction(execution shuffle.WorkflowExecution) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 1. Check CORS
|
||||
// 2. Check authentication
|
||||
// 3. Check authorization
|
||||
// 4. Run the actual function
|
||||
func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := shuffle.HandleCors(resp, request)
|
||||
if cors {
|
||||
@@ -1104,8 +1108,8 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
executionAuthValid, newOrgId = shuffle.RunExecuteAccessValidation(request, workflow)
|
||||
if !executionAuthValid {
|
||||
log.Printf("[INFO] Api authentication failed in execute workflow: %s", userErr)
|
||||
resp.WriteHeader(401)
|
||||
log.Printf("[INFO] Api authorization failed in execute workflow: %s", userErr)
|
||||
resp.WriteHeader(403)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
} else {
|
||||
@@ -1122,7 +1126,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
log.Printf("[AUDIT] Letting user %s execute %s because they're admin of the same org", user.Username, workflow.ID)
|
||||
} else {
|
||||
log.Printf("[AUDIT] Wrong user (%s) for workflow %s (execute)", user.Username, workflow.ID)
|
||||
resp.WriteHeader(401)
|
||||
resp.WriteHeader(403)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
@@ -2606,7 +2610,7 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Execution: %s should execute onprem with execution environment \"%s\". Workflow: %s", workflowExecution.ExecutionId, environment, workflowExecution.Workflow.ID)
|
||||
log.Printf("[INFO] Execution (single action): %s should execute onprem with execution environment \"%s\". Workflow: %s", workflowExecution.ExecutionId, environment, workflowExecution.Workflow.ID)
|
||||
|
||||
executionRequest := shuffle.ExecutionRequest{
|
||||
ExecutionId: workflowExecution.ExecutionId,
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
version: '3'
|
||||
services:
|
||||
frontend:
|
||||
build: ./frontend
|
||||
#build: ./frontend
|
||||
image: ghcr.io/frikky/shuffle-frontend:nightly
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
|
||||
@@ -319,7 +319,6 @@ const App = (message, props) => {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
}
|
||||
<Header
|
||||
notifications={notifications}
|
||||
|
||||
@@ -167,6 +167,8 @@ const ParsedAction = (props) => {
|
||||
isCloud,
|
||||
lastSaved,
|
||||
setLastSaved,
|
||||
//expansionModalOpen,
|
||||
//setExpansionModalOpen,
|
||||
} = props;
|
||||
|
||||
//const theme = useTheme();
|
||||
@@ -1395,7 +1397,7 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Autocomplete the text" placement="top">
|
||||
<Tooltip title="Autocomplete text" placement="top">
|
||||
<AddCircleOutlineIcon
|
||||
style={{ cursor: "pointer", margin: multiline ? 5 : 0, }}
|
||||
onClick={(event) => {
|
||||
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
Typography,
|
||||
Paper
|
||||
Paper,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Button,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
@@ -34,11 +37,16 @@ import 'codemirror/theme/gruvbox-dark.css';
|
||||
import 'codemirror/theme/duotone-light.css';
|
||||
import { padding, textAlign } from '@mui/system';
|
||||
|
||||
const liquidFilters = [
|
||||
{"name": "Size", "value": "size", "example": ""},
|
||||
{"name": "Date", "value": "date", "example": `{{ "now" | date: "%s" }}`},
|
||||
]
|
||||
|
||||
const CodeEditor = (props) => {
|
||||
const { fieldCount, setFieldCount, actionlist, changeActionParameterCodeMirror, expansionModalOpen, setExpansionModalOpen, codedata, setcodedata } = props
|
||||
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
||||
// const {codelang, setcodelang} = props
|
||||
const theme = useTheme();
|
||||
const theme = useTheme();
|
||||
const [validation, setValidation] = React.useState(false);
|
||||
const [expOutput, setExpOutput] = React.useState(" ");
|
||||
const [linewrap, setlinewrap] = React.useState(true);
|
||||
@@ -51,6 +59,9 @@ const CodeEditor = (props) => {
|
||||
const [variableOccurences, setVariableOccurences] = React.useState([]);
|
||||
const [currentLocation, setCurrentLocation] = React.useState([]);
|
||||
const [currentVariable, setCurrentVariable] = React.useState("");
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const liquidOpen = Boolean(anchorEl);
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log(currentLocation)
|
||||
// }, [currentLocation])
|
||||
@@ -340,11 +351,23 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleClick = (item) => {
|
||||
if (item === undefined || item.value === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
setlocalcodedata(localcodedata+" "+item.value)
|
||||
setAnchorEl(null)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
disableEnforceFocus={true}
|
||||
hideBackdrop={true}
|
||||
aria-labelledby="draggable-code-modal"
|
||||
disableBackdropClick={true}
|
||||
disableEnforceFocus={true}
|
||||
//style={{ pointerEvents: "none" }}
|
||||
hideBackdrop={true}
|
||||
open={expansionModalOpen}
|
||||
onClose={() => {
|
||||
console.log("In closer")
|
||||
@@ -352,7 +375,6 @@ const CodeEditor = (props) => {
|
||||
//setExpansionModalOpen(false)
|
||||
}}
|
||||
PaperComponent={PaperComponent}
|
||||
aria-labelledby="draggable-dialog-title"
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
@@ -400,10 +422,46 @@ const CodeEditor = (props) => {
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<Button
|
||||
id="basic-button"
|
||||
aria-haspopup="true"
|
||||
aria-controls={liquidOpen ? 'basic-menu' : undefined}
|
||||
aria-expanded={liquidOpen ? 'true' : undefined}
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 200,
|
||||
marginLeft: 200,
|
||||
}}
|
||||
onClick={(event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
}}
|
||||
>
|
||||
Liquid
|
||||
</Button>
|
||||
<Menu
|
||||
id="basic-menu"
|
||||
anchorEl={anchorEl}
|
||||
open={liquidOpen}
|
||||
onClose={() => {
|
||||
setAnchorEl(null);
|
||||
}}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
>
|
||||
{liquidFilters.map((item, index) => {
|
||||
return (
|
||||
<MenuItem onClick={() => {
|
||||
handleClick(item)
|
||||
}}>{item.name}</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Menu>
|
||||
<span style={{
|
||||
border: `2px solid ${theme.palette.inputColor}`,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
position: "relative",
|
||||
}}>
|
||||
<CodeMirror
|
||||
value = {localcodedata}
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import countries from "../components/Countries.jsx"
|
||||
|
||||
import {
|
||||
FormControl,
|
||||
@@ -36,8 +37,13 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
CircularProgress,
|
||||
Box,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import {
|
||||
Autocomplete,
|
||||
} from "@mui/material"
|
||||
|
||||
import {
|
||||
Edit as EditIcon,
|
||||
FileCopy as FileCopyIcon,
|
||||
@@ -105,6 +111,7 @@ const Admin = (props) => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
const [selectedOrganization, setSelectedOrganization] = React.useState({});
|
||||
const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false);
|
||||
//console.log("Selected: ", selectedOrganization)
|
||||
const [organizationFeatures, setOrganizationFeatures] = React.useState({});
|
||||
const [loginInfo, setLoginInfo] = React.useState("");
|
||||
@@ -140,6 +147,17 @@ const Admin = (props) => {
|
||||
const [secret2FA, setSecret2FA] = React.useState("");
|
||||
const [show2faSetup, setShow2faSetup] = useState(false);
|
||||
|
||||
const [dealName, setDealName] = React.useState("");
|
||||
const [dealAddress, setDealAddress] = React.useState("");
|
||||
const [dealType, setDealType] = React.useState("MSSP");
|
||||
const [dealCountry, setDealCountry] = React.useState("United States");
|
||||
const [dealCurrency, setDealCurrency] = React.useState("USD");
|
||||
const [dealStatus, setDealStatus] = React.useState("initiated");
|
||||
const [dealValue, setDealValue] = React.useState("");
|
||||
const [dealDiscount, setDealDiscount] = React.useState("");
|
||||
const [dealerror, setDealerror] = React.useState("");
|
||||
const [dealList, setDealList] = React.useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDropzone) {
|
||||
//redirectOpenApi();
|
||||
@@ -648,6 +666,45 @@ const Admin = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleGetDeals = (orgId) => {
|
||||
console.log("Get deals!")
|
||||
|
||||
if (orgId.length === 0) {
|
||||
alert.error(
|
||||
"Organization ID not defined. Please contact us on https://shuffler.io if this persists logout."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const url = `${globalUrl}/api/v1/orgs/${orgId}/deals`
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Bad status code in get deals: ", response.status)
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
console.log("Got deals: ", responseJson)
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed loading deals. Contact support if this persists")
|
||||
} else {
|
||||
setDealList(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Error getting org deals: ", error);
|
||||
alert.error("Failed getting deals for your org. Contact support if this persists.");
|
||||
});
|
||||
}
|
||||
|
||||
const handleGetOrg = (orgId) => {
|
||||
if (orgId.length === 0) {
|
||||
alert.error(
|
||||
@@ -674,7 +731,7 @@ const Admin = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
alert.error("Failed getting org: ", responseJson.readon);
|
||||
alert.error("Failed getting your org: ", responseJson.readon);
|
||||
} else {
|
||||
if (
|
||||
responseJson.sync_features === undefined ||
|
||||
@@ -682,6 +739,12 @@ const Admin = (props) => {
|
||||
) {
|
||||
responseJson.sync_features = {};
|
||||
}
|
||||
|
||||
if (isCloud && responseJson.partner_info !== undefined && responseJson.partner_info.reseller === true) {
|
||||
handleGetDeals(orgId)
|
||||
}
|
||||
|
||||
|
||||
setSelectedOrganization(responseJson);
|
||||
var lists = {
|
||||
active: {
|
||||
@@ -1682,6 +1745,235 @@ const Admin = (props) => {
|
||||
</FormControl>
|
||||
: null
|
||||
|
||||
|
||||
const products = [
|
||||
{ code: '', label: 'MSSP', phone: '' },
|
||||
{ code: '', label: 'Enterprise', phone: '' },
|
||||
{ code: '', label: 'Consultancy', phone: '' },
|
||||
{ code: '', label: 'Support', phone: '' },
|
||||
]
|
||||
|
||||
const addDealModal = (
|
||||
<Dialog
|
||||
open={selectedDealModalOpen}
|
||||
onClose={() => {
|
||||
setSelectedDealModalOpen(false);
|
||||
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{ maxWidth: 450, margin: "auto" }}>
|
||||
<span style={{ color: "white" }}>
|
||||
Register new deal
|
||||
</span>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
style={{
|
||||
marginTop: 0,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
flex: 3,
|
||||
marginRight: 10,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
height: 50,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
required
|
||||
fullWidth={true}
|
||||
placeholder="Name"
|
||||
type="text"
|
||||
id="standard-required"
|
||||
autoComplete="username"
|
||||
margin="normal"
|
||||
label="Name"
|
||||
variant="outlined"
|
||||
defaultValue={dealName}
|
||||
onChange={(e) => {
|
||||
setDealName(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
style={{
|
||||
marginTop: 0,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
flex: 3,
|
||||
marginRight: 10,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
height: 50,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
required
|
||||
fullWidth={true}
|
||||
placeholder="Address"
|
||||
label="Address"
|
||||
type="text"
|
||||
id="standard-required"
|
||||
autoComplete="username"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
defaultValue={dealAddress}
|
||||
onChange={(e) => {
|
||||
setDealAddress(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: 10, }}>
|
||||
<TextField
|
||||
style={{
|
||||
marginTop: 0,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
flex: 1,
|
||||
marginRight: 10,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
height: 50,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
required
|
||||
fullWidth={true}
|
||||
placeholder="1000"
|
||||
label="Value (USD)"
|
||||
type="text"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
defaultValue={dealValue}
|
||||
onChange={(e) => {
|
||||
setDealValue(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<Autocomplete
|
||||
id="country-select"
|
||||
sx={{width: 250}}
|
||||
options={countries}
|
||||
variant="outlined"
|
||||
autoHighlight
|
||||
getOptionLabel={(option) => option.label}
|
||||
onChange={(event, newValue) => {
|
||||
setDealCountry(newValue.label)
|
||||
}}
|
||||
renderOption={(props, option) => (
|
||||
<Box component="li" sx={{ '& > img': { mr: 2, flexShrink: 0 } }} {...props}>
|
||||
<img
|
||||
loading="lazy"
|
||||
width="20"
|
||||
src={`https://flagcdn.com/w20/${option.code.toLowerCase()}.png`}
|
||||
srcSet={`https://flagcdn.com/w40/${option.code.toLowerCase()}.png 2x`}
|
||||
alt=""
|
||||
/>
|
||||
{option.label} ({option.code}) +{option.phone}
|
||||
</Box>
|
||||
)}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
flex: 1,
|
||||
marginTop: 0,
|
||||
marginRight: 10,
|
||||
}}
|
||||
variant="outlined"
|
||||
label="Choose a country"
|
||||
defaultValue={dealCountry}
|
||||
inputProps={{
|
||||
...params.inputProps,
|
||||
autoComplete: 'new-password', // disable autocomplete and autofill
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Autocomplete
|
||||
id="product-select"
|
||||
sx={{width: 250}}
|
||||
options={products}
|
||||
variant="outlined"
|
||||
autoHighlight
|
||||
onChange={(event, newValue) => {
|
||||
setDealType(newValue)
|
||||
}}
|
||||
getOptionLabel={(option) => option.label}
|
||||
renderOption={(props, option) => (
|
||||
<Box component="li" sx={{ '& > img': { mr: 2, flexShrink: 0 } }} {...props}>
|
||||
{option.label}
|
||||
</Box>
|
||||
)}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
flex: 1,
|
||||
marginTop: 0,
|
||||
}}
|
||||
variant="outlined"
|
||||
label="Choose a product"
|
||||
defaultValue={dealType}
|
||||
inputProps={{
|
||||
...params.inputProps,
|
||||
autoComplete: 'new-password', // disable autocomplete and autofill
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{dealerror.length > 0 ?
|
||||
<Typography variant="body1" color="textSecondary" style={{margin: 10, }}>
|
||||
error registering: {dealerror}
|
||||
</Typography>
|
||||
: null}
|
||||
<div style={{display: "flex", width: 300, margin: "auto"}}>
|
||||
<Button
|
||||
style={{ maxHeight: 50, flex: 1, margin: 5, }}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
setSelectedDealModalOpen(false);
|
||||
|
||||
//setDealName("")
|
||||
//setDealAddress("")
|
||||
//setDealCountry("")
|
||||
//setDealValue("")
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
style={{ maxHeight: 50, flex: 1, margin: 5, }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={dealName.length <= 3 || dealAddress.length <= 3 || dealCountry.length === 0 || dealValue.length === 0 || dealType.length === 0}
|
||||
onClick={() => {
|
||||
submitDeal(dealName, dealAddress, dealCountry, dealValue)
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const editUserModal = (
|
||||
<Dialog
|
||||
open={selectedUserModalOpen}
|
||||
@@ -2115,6 +2407,60 @@ const Admin = (props) => {
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const submitDeal = (dealName, dealAddress, dealCountry, dealValue) => {
|
||||
if (dealerror.length > 0) {
|
||||
setDealerror("")
|
||||
}
|
||||
|
||||
const orgId = selectedOrganization.id;
|
||||
const data = {
|
||||
reseller_org: orgId,
|
||||
name: dealName,
|
||||
address: dealAddress,
|
||||
country: dealCountry,
|
||||
value: dealValue,
|
||||
};
|
||||
|
||||
const url = `${globalUrl}/api/v1/orgs/${orgId}/deals`
|
||||
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");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then(function (responseJson) {
|
||||
if (responseJson.success === true) {
|
||||
setSelectedDealModalOpen(false);
|
||||
alert.success("Added new deal! We will be in touch shortly with an update.");
|
||||
|
||||
setDealName("")
|
||||
setDealAddress("")
|
||||
setDealValue("")
|
||||
setDealCountry("United States")
|
||||
setDealType("MSSP")
|
||||
} else {
|
||||
setDealerror(responseJson.reason)
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
//console.log("Error: ", error);
|
||||
setDealerror(error.toString())
|
||||
alert.error("Failed adding deal reg: ", error)
|
||||
});
|
||||
}
|
||||
|
||||
const cancelSubscriptions = (subscription_id) => {
|
||||
console.log(selectedOrganization);
|
||||
const orgId = selectedOrganization.id;
|
||||
@@ -2450,6 +2796,169 @@ const Admin = (props) => {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
}}
|
||||
/>
|
||||
|
||||
{isCloud && selectedOrganization.partner_info !== undefined && selectedOrganization.partner_info.reseller === true ?
|
||||
<div style={{ marginTop: 30, marginBottom: 200, }}>
|
||||
<Typography
|
||||
style={{ marginTop: 40, marginLeft: 10, marginBottom: 5 }}
|
||||
variant="h6"
|
||||
>
|
||||
Reseller dashboard
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={{ margin: 15, }}
|
||||
onClick={() => {
|
||||
setSelectedDealModalOpen(true)
|
||||
}}
|
||||
>
|
||||
Add deal
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 5, marginRight: 15 }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
handleGetDeals(userdata.active_org.id)
|
||||
}}
|
||||
>
|
||||
<CachedIcon />
|
||||
</Button>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary="Name"
|
||||
style={{
|
||||
minWidth: 200,
|
||||
maxWidth: 200,
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Status"
|
||||
style={{ minWidth: 150, maxWidth: 150, marginLeft: 5 }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Value"
|
||||
style={{ minWidth: 125, maxWidth: 125 }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Discount"
|
||||
style={{ minWidth: 100, maxWidth: 100 }}
|
||||
/>
|
||||
|
||||
<ListItemText
|
||||
primary="Address"
|
||||
style={{
|
||||
marginleft: 10,
|
||||
minWidth: 100,
|
||||
maxWidth: 100,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Country"
|
||||
style={{
|
||||
marginleft: 10,
|
||||
minWidth: 100,
|
||||
maxWidth: 100,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
/>
|
||||
|
||||
<ListItemText
|
||||
primary="Created"
|
||||
style={{ minWidth: 200, maxWidth: 100 }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Last edited"
|
||||
style={{ minWidth: 200, maxWidth: 100 }}
|
||||
/>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
{dealList.length === 0 ?
|
||||
<Typography variant="h6" style={{textAlign: "center", margin: "auto", width: 600, marginTop: 50, marginBottom: 50, }}>
|
||||
No deals registered yet. Click "Add deal" to register one
|
||||
</Typography>
|
||||
:
|
||||
dealList.map((deal, index) => {
|
||||
var bgColor = "#27292d";
|
||||
if (index % 2 === 0) {
|
||||
bgColor = "#1f2023";
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem key={index} style={{ backgroundColor: bgColor }}>
|
||||
<ListItemText
|
||||
primary={deal.name}
|
||||
style={{
|
||||
minWidth: 200,
|
||||
maxWidth: 200,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={deal.status}
|
||||
style={{
|
||||
minWidth: 150,
|
||||
maxWidth: 150,
|
||||
marginLeft: 5,
|
||||
color: deal.status.toLowerCase() === "requested" ? "yellow" : deal.status.toLowerCase() === "denied" || deal.status.toLowerCase() === "cancelled" ? "red" : "green",
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={`\$${deal.value}`}
|
||||
style={{ minWidth: 125, maxWidth: 125 }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={deal.discount.length === 0 ? "TBD" : deal.discount}
|
||||
style={{ minWidth: 100, maxWidth: 100 }}
|
||||
/>
|
||||
|
||||
<ListItemText
|
||||
primary={deal.address}
|
||||
style={{
|
||||
marginleft: 10,
|
||||
minWidth: 100,
|
||||
maxWidth: 100,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={deal.country}
|
||||
style={{
|
||||
marginleft: 10,
|
||||
minWidth: 100,
|
||||
maxWidth: 100,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
/>
|
||||
|
||||
<ListItemText
|
||||
primary={new Date(deal.created * 1000).toISOString()}
|
||||
style={{ minWidth: 200, maxWidth: 200 }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={new Date(deal.edited * 1000).toISOString()}
|
||||
style={{ minWidth: 200, maxWidth: 200 }}
|
||||
/>
|
||||
</ListItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
</List>
|
||||
|
||||
<Divider
|
||||
style={{
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
|
||||
{isCloud &&
|
||||
selectedOrganization.subscriptions !== undefined &&
|
||||
selectedOrganization.subscriptions !== null &&
|
||||
@@ -4291,6 +4800,7 @@ const Admin = (props) => {
|
||||
{modalView}
|
||||
{cloudSyncModal}
|
||||
{editUserModal}
|
||||
{addDealModal}
|
||||
{editAuthenticationModal}
|
||||
{data}
|
||||
<TextField
|
||||
|
||||
@@ -11601,7 +11601,7 @@ const parsedExecutionArgument = () => {
|
||||
variant="temporary"
|
||||
BackdropProps={{
|
||||
style: {
|
||||
backgroundColor: "transparent",
|
||||
//backgroundColor: "transparent",
|
||||
}
|
||||
}}
|
||||
PaperProps={{
|
||||
|
||||
@@ -1511,7 +1511,7 @@ const Apps = (props) => {
|
||||
fullWidth
|
||||
color="primary"
|
||||
id="app_search_field"
|
||||
placeholder={"Search apps"}
|
||||
placeholder={"Search your apps"}
|
||||
onChange={(event) => {
|
||||
handleSearchChange(event.target.value);
|
||||
setCursearch(event.target.value);
|
||||
|
||||
@@ -598,7 +598,7 @@ const Docs = (defaultprops) => {
|
||||
Documentation
|
||||
</Typography>
|
||||
<div style={{display: "flex", marginTop: 25, }}>
|
||||
<CustomButton title="Open a Ticket" icon=<img src="/images/Shuffle_logo_new.png" style={{height: 35, width: 35, border: "", borderRadius: theme.palette.borderRadius, }} /> link="https://support.shuffler.io" />
|
||||
<CustomButton title="Open a Ticket" icon=<img src="/images/Shuffle_logo_new.png" style={{height: 35, width: 35, border: "", borderRadius: theme.palette.borderRadius, }} /> link="mailto:support@shuffler.io" />
|
||||
<CustomButton title="Ask the community" icon=<img src="/images/social/discord.png" style={{height: 35, width: 35, border: "", borderRadius: theme.palette.borderRadius, }} /> link="https://discord.gg/B2CBzUm" />
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user