From c50cb2ce4d8c656a2662ffbd813fe82b57eb2c03 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 10 May 2022 21:02:44 +0200 Subject: [PATCH] Minor fixes for frontend --- backend/go-app/go.mod | 2 +- backend/go-app/walkoff.go | 12 +- docker-compose.yml | 2 +- frontend/src/App.jsx | 1 - frontend/src/components/ParsedAction.jsx | 4 +- frontend/src/components/ShuffleCodeEditor.jsx | 70 ++- frontend/src/views/Admin.jsx | 512 +++++++++++++++++- frontend/src/views/AngularWorkflow.jsx | 2 +- frontend/src/views/Apps.jsx | 2 +- frontend/src/views/Docs.jsx | 2 +- 10 files changed, 591 insertions(+), 18 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index a2dfd189..287967fb 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -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 diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index ffdf9b4f..c83fb664 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -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, diff --git a/docker-compose.yml b/docker-compose.yml index fd2973f3..3f818755 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a4b5e132..e403f7c5 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -319,7 +319,6 @@ const App = (message, props) => { }, ]} /> - }
{ isCloud, lastSaved, setLastSaved, + //expansionModalOpen, + //setExpansionModalOpen, } = props; //const theme = useTheme(); @@ -1395,7 +1397,7 @@ const ParsedAction = (props) => { }} /> - + { diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 5afb4bd3..41456960 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -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 ( { 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) => { - + + + { + setAnchorEl(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {liquidFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + { 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) => { : null + + const products = [ + { code: '', label: 'MSSP', phone: '' }, + { code: '', label: 'Enterprise', phone: '' }, + { code: '', label: 'Consultancy', phone: '' }, + { code: '', label: 'Support', phone: '' }, + ] + + const addDealModal = ( + { + setSelectedDealModalOpen(false); + + }} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: "800px", + minHeight: "320px", + }, + }} + > + + + Register new deal + + + +
+ { + setDealName(e.target.value) + }} + /> + { + setDealAddress(e.target.value) + }} + /> +
+
+ { + setDealValue(e.target.value) + }} + /> + option.label} + onChange={(event, newValue) => { + setDealCountry(newValue.label) + }} + renderOption={(props, option) => ( + img': { mr: 2, flexShrink: 0 } }} {...props}> + + {option.label} ({option.code}) +{option.phone} + + )} + renderInput={(params) => ( + + )} + /> + { + setDealType(newValue) + }} + getOptionLabel={(option) => option.label} + renderOption={(props, option) => ( + img': { mr: 2, flexShrink: 0 } }} {...props}> + {option.label} + + )} + renderInput={(params) => ( + + )} + /> +
+ {dealerror.length > 0 ? + + error registering: {dealerror} + + : null} +
+ + +
+
+
+ ); + const editUserModal = ( { ); + 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 ? +
+ + Reseller dashboard + + + + + + + + + + + + + + + + + + {dealList.length === 0 ? + + No deals registered yet. Click "Add deal" to register one + + : + dealList.map((deal, index) => { + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } + + return ( + + + + + + + + + + + + + ) + }) + } + + + +
+ : null} + + {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && @@ -4291,6 +4800,7 @@ const Admin = (props) => { {modalView} {cloudSyncModal} {editUserModal} + {addDealModal} {editAuthenticationModal} {data} { variant="temporary" BackdropProps={{ style: { - backgroundColor: "transparent", + //backgroundColor: "transparent", } }} PaperProps={{ diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 5ce2fcbc..912acaa5 100644 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -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); diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 8dba1aa2..a58da57c 100644 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -598,7 +598,7 @@ const Docs = (defaultprops) => { Documentation
- link="https://support.shuffler.io" /> + link="mailto:support@shuffler.io" /> link="https://discord.gg/B2CBzUm" />