Fixed merge conflicts
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 449 KiB After Width: | Height: | Size: 449 KiB |
|
Before Width: | Height: | Size: 431 KiB After Width: | Height: | Size: 431 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 440 KiB After Width: | Height: | Size: 440 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -2,6 +2,7 @@ import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import {
|
||||
@@ -97,6 +98,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
} = props;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const alert = useAlert()
|
||||
|
||||
//const [update, setUpdate] = React.useState("|")
|
||||
const [defaultConfigSet, setDefaultConfigSet] = React.useState(
|
||||
@@ -282,6 +284,51 @@ const AuthenticationOauth2 = (props) => {
|
||||
}
|
||||
|
||||
|
||||
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => {
|
||||
setButtonClicked(true);
|
||||
//console.log("SCOPES: ", scopes);
|
||||
|
||||
client_id = client_id.trim()
|
||||
client_secret = client_secret.trim()
|
||||
oauth_url = oauth_url.trim()
|
||||
|
||||
var resources = "";
|
||||
if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
|
||||
console.log("IN scope 1")
|
||||
if (offlineAccess === true && !scopes.includes("offline_access")) {
|
||||
|
||||
console.log("IN scope 2")
|
||||
if (!authenticationType.redirect_uri.includes("google")) {
|
||||
console.log("Appending offline access")
|
||||
scopes.push("offline_access")
|
||||
}
|
||||
}
|
||||
|
||||
resources = scopes.join(" ");
|
||||
//resources = scopes.join(",");
|
||||
}
|
||||
|
||||
const authentication_url = authenticationType.token_uri;
|
||||
//console.log("AUTH: ", authenticationType)
|
||||
//console.log("SCOPES2: ", resources)
|
||||
const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`;
|
||||
const workflowId = workflow !== undefined ? workflow.id : "";
|
||||
var state = `workflow_id%3D${workflowId}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`;
|
||||
|
||||
|
||||
// This is to make sure authorization can be handled WITHOUT being logged in,
|
||||
// kind of making it act like an api key
|
||||
// https://shuffler.io/authorization -> 3rd party integration auth
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const userAuth = urlParams.get("authorization");
|
||||
if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) {
|
||||
console.log("Adding authorization from user side")
|
||||
state += `%26authorization%3d${userAuth}`;
|
||||
}
|
||||
// write:request:jira-service-management
|
||||
}
|
||||
|
||||
|
||||
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => {
|
||||
setButtonClicked(true);
|
||||
//console.log("SCOPES: ", scopes);
|
||||
@@ -317,6 +364,35 @@ const AuthenticationOauth2 = (props) => {
|
||||
console.log("ADDING OAUTH2 URL: ", state);
|
||||
}
|
||||
|
||||
if (
|
||||
authenticationType.refresh_uri !== undefined &&
|
||||
authenticationType.refresh_uri !== null &&
|
||||
authenticationType.refresh_uri.length > 0
|
||||
) {
|
||||
state += `%26refresh_uri%3d${authenticationType.refresh_uri}`;
|
||||
} else {
|
||||
state += `%26refresh_uri%3d${authentication_url}`;
|
||||
}
|
||||
|
||||
// No prompt forcing
|
||||
//var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`;
|
||||
var defaultPrompt = "login"
|
||||
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
|
||||
defaultPrompt = prompt
|
||||
}
|
||||
// Check for org_id
|
||||
const orgId = urlParams.get("org_id");
|
||||
if (orgId !== undefined && orgId !== null && orgId.length > 0) {
|
||||
console.log("Adding org_id from user side")
|
||||
state += `%26org_id%3d${orgId}`;
|
||||
}
|
||||
|
||||
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
|
||||
state += `%26oauth_url%3d${oauth_url}`;
|
||||
console.log("ADDING OAUTH2 URL: ", state);
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
authenticationType.refresh_uri !== undefined &&
|
||||
authenticationType.refresh_uri !== null &&
|
||||
@@ -356,7 +432,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
// How can we get a callback properly realtime?
|
||||
// How can we properly try-catch without breaks on error?
|
||||
try {
|
||||
var newwin = window.open(url, "", "width=800,height=600");
|
||||
var newwin = window.open(url, "", "width=582,height=700");
|
||||
//console.log(newwin)
|
||||
|
||||
var open = true;
|
||||
@@ -447,9 +523,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
] = "false";
|
||||
} else {
|
||||
alert.info(
|
||||
"Field " +
|
||||
selectedApp.authentication.parameters[key].name +
|
||||
" can't be empty"
|
||||
"Field " + selectedApp.authentication.parameters[key].name.replace("_basic", "", -1).replace("_", " ", -1) + " can't be empty"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1595,6 +1595,7 @@ const ParsedAction = (props) => {
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
},
|
||||
disableUnderline: true,
|
||||
endAdornment: hideExtraTypes ? null : (
|
||||
<InputAdornment position="end">
|
||||
<ButtonGroup orientation={multiline ? "vertical" : "horizontal"}>
|
||||
@@ -2958,6 +2959,7 @@ const ParsedAction = (props) => {
|
||||
style={theme.palette.textFieldStyle}
|
||||
InputProps={{
|
||||
style: theme.palette.innerTextfieldStyle,
|
||||
disableUnderline: true,
|
||||
}}
|
||||
fullWidth
|
||||
color="primary"
|
||||
@@ -3177,6 +3179,7 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
InputProps={{
|
||||
style: theme.palette.innerTextfieldStyle,
|
||||
disableUnderline: true,
|
||||
}}
|
||||
placeholder={selectedAction.execution_delay}
|
||||
defaultValue={selectedAction.execution_delay}
|
||||
@@ -3492,7 +3495,7 @@ const ParsedAction = (props) => {
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
options={selectedApp.actions.filter((a) => a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))}
|
||||
options={selectedApp.actions === undefined || selectedApp.actions === null ? [] : selectedApp.actions.filter((a) => a.category_label !== undefined && a.category_label !== null && a.category_label.length > 0).concat(sortByKey(selectedApp.actions, "label"))}
|
||||
ListboxProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
|
||||
@@ -1694,20 +1694,29 @@ const CodeEditor = (props) => {
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={(event) => {
|
||||
// Take localcodedata through the Shuffle JSON parser just in case
|
||||
// This is to make it so we don't need to handle these fixes on the
|
||||
// backend by itself
|
||||
var fixedcodedata = localcodedata
|
||||
const valid = validateJson(localcodedata, true)
|
||||
if (valid.valid) {
|
||||
fixedcodedata = JSON.stringify(valid.result, null, 2)
|
||||
}
|
||||
|
||||
// console.log(codedata)
|
||||
// console.log(fieldCount)
|
||||
if (isFileEditor === true){
|
||||
runUpdateText(localcodedata);
|
||||
setcodedata(localcodedata);
|
||||
runUpdateText(fixedcodedata);
|
||||
setcodedata(fixedcodedata);
|
||||
setExpansionModalOpen(false)
|
||||
} else {
|
||||
changeActionParameterCodeMirror(event, fieldCount, fixedcodedata)
|
||||
setExpansionModalOpen(false)
|
||||
setcodedata(fixedcodedata)
|
||||
}
|
||||
else {
|
||||
changeActionParameterCodeMirror(event, fieldCount, localcodedata)
|
||||
setExpansionModalOpen(false)
|
||||
setcodedata(localcodedata)}
|
||||
}}
|
||||
>
|
||||
Done
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</Dialog>)
|
||||
|
||||
@@ -443,17 +443,17 @@ const Admin = (props) => {
|
||||
// Get drift username from userdata.username before @ in email
|
||||
const username = userdata.username.substring(0, userdata.username.indexOf("@"))
|
||||
|
||||
var body = `Hey,%0D%0AI saw you trying to use Shuffle, and thought we may be able to help. Right now, it looks like you have ${workflow_amount} workflows made, but I'm not sure if you're getting the most out of Shuffle.%0D%0A%0D%0AIf you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A
|
||||
var body = `Hey,%0D%0A%0D%0AI saw you trying to use Shuffle, and thought we may be able to help. Right now, it looks like you have ${workflow_amount} workflows made, but it still doesn't look like you are getting the most out of Shuffle. If you're interested, I'd love to set up a quick call to see if we can help you get more out of Shuffle. %0D%0A%0D%0A
|
||||
|
||||
Some of the things we can help with:%0D%0A
|
||||
${your_apps}
|
||||
- Properly authenticating and custom building apps%0D%0A
|
||||
- Configuring and authenticating your apps%0D%0A
|
||||
${usecases}
|
||||
- Creating special usecases%0D%0A%0D%0A
|
||||
- Creating special usecases and apps%0D%0A%0D%0A
|
||||
|
||||
Let me know if you're interested, or set up a call here: https://drift.me/${username}`
|
||||
|
||||
return `mailto:${admins}?subject=${subject}&body=${body}`
|
||||
return `mailto:${admins}?bcc=frikky@shuffler.io&subject=${subject}&body=${body}`
|
||||
}
|
||||
|
||||
const deleteAuthentication = (data) => {
|
||||
@@ -3532,8 +3532,12 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
||||
<h2 style={{ display: "inline" }}>App Authentication</h2>
|
||||
<span style={{ marginLeft: 25 }}>
|
||||
<<<<<<< HEAD
|
||||
Control the authentication options for individual apps. PS: Actions
|
||||
performed here can be destructive!
|
||||
=======
|
||||
Control the authentication options for individual apps.
|
||||
>>>>>>> master
|
||||
</span>
|
||||
|
||||
<a
|
||||
@@ -3542,7 +3546,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
href="/docs/organizations#app_authentication"
|
||||
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
>
|
||||
<<<<<<< HEAD
|
||||
Learn more about authentication
|
||||
=======
|
||||
Learn more about App Authentication
|
||||
>>>>>>> master
|
||||
</a>
|
||||
</div>
|
||||
<Divider
|
||||
|
||||
@@ -4638,7 +4638,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
};
|
||||
|
||||
const addSuggestionButtons = (nodedata, event) => {
|
||||
//console.log("In suggestion buttons: ", nodedata, ". This is not enabled yet. Backend needs further work.")
|
||||
console.log("In suggestion buttons: ", nodedata, ". This is not enabled yet. Backend needs further work.")
|
||||
return
|
||||
// Skipping add for now. Should Re-enable
|
||||
|
||||
// Add a button for autocompletion based on input
|
||||
@@ -4677,8 +4678,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
|
||||
cy.add(decoratorNode);
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
//setWorkflowRecommendations(responseJson.actions)
|
||||
if (workflowRecommendations.length === 0) {
|
||||
@@ -14561,9 +14560,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
? "red"
|
||||
: yellow;
|
||||
|
||||
const validate = !codeModalOpen
|
||||
? ""
|
||||
: validateJson(selectedResult.result.trim());
|
||||
const validate = ! codeModalOpen? "" : validateJson(selectedResult.result.trim());
|
||||
|
||||
if (validate.valid && typeof validate.result === "string") {
|
||||
validate.result = JSON.parse(validate.result);
|
||||
@@ -14573,12 +14570,17 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const showVariable = data.value.length < 60
|
||||
|
||||
// Check if it's valid JSON
|
||||
const checked = validateJson(data.value.trim())
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: 600, overflowX: "hidden", }}>
|
||||
{data.value.length > 60 ?
|
||||
{data.value.length > 60 || checked.valid ?
|
||||
<IconButton
|
||||
style={{
|
||||
marginBottom: 0, marginTop: 5, cursor: "pointer",
|
||||
marginBottom: 0,
|
||||
marginTop: 5,
|
||||
cursor: "pointer",
|
||||
padding: 3,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
@@ -14593,7 +14595,16 @@ const AngularWorkflow = (defaultprops) => {
|
||||
variant="body1"
|
||||
style={{}}
|
||||
>
|
||||
<b>{data.name}</b> {showVariable ? data.value : null}
|
||||
<b>{data.name}</b>
|
||||
{checked.valid ?
|
||||
<Chip
|
||||
style={{marginLeft: 10, padding: 0, cursor: "pointer",}}
|
||||
label={"JSON"}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
/>
|
||||
: null}
|
||||
{showVariable ? data.value : null}
|
||||
</Typography>
|
||||
</IconButton>
|
||||
:
|
||||
@@ -14605,15 +14616,25 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</Typography>
|
||||
}
|
||||
{open ?
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{
|
||||
whiteSpace: 'pre-line',
|
||||
}}
|
||||
color="textSecondary"
|
||||
>
|
||||
{data.value}
|
||||
</Typography>
|
||||
checked.valid ?
|
||||
<ReactJson
|
||||
src={checked.result}
|
||||
theme={theme.palette.jsonTheme}
|
||||
style={theme.palette.reactJsonStyle}
|
||||
collapsed={data.value.length < 10000 ? false : true}
|
||||
displayDataTypes={false}
|
||||
name={"Parsed data for variable " + data.name}
|
||||
/>
|
||||
:
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{
|
||||
whiteSpace: 'pre-line',
|
||||
}}
|
||||
color="textSecondary"
|
||||
>
|
||||
{data.value}
|
||||
</Typography>
|
||||
: null}
|
||||
</div>
|
||||
)
|
||||
@@ -15071,10 +15092,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
>
|
||||
<FormControl>
|
||||
<DialogTitle id="draggable-dialog-title" style={{ cursor: "move", }}>
|
||||
<span style={{ color: "white" }}>Execution Variable</span>
|
||||
<span style={{ color: "white" }}>Runtime Variable</span>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
Execution Variables are TEMPORARY variables that you can only be set
|
||||
Runtime Variables are TEMPORARY variables that you can only be set
|
||||
and used during execution. Learn more{" "}
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
@@ -15095,7 +15116,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
},
|
||||
}}
|
||||
margin="dense"
|
||||
label="Name"
|
||||
label="Name"
|
||||
fullWidth
|
||||
defaultValue={newVariableName}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
import React, { useState } from 'react';
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
textAlign: "center",
|
||||
width: "900px",
|
||||
}
|
||||
|
||||
|
||||
// Should be different if logged in :|
|
||||
const Contact = (props) => {
|
||||
const { globalUrl, isLoaded } = props;
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
}
|
||||
|
||||
const bodyTextStyle = {
|
||||
color: "#ffffff",
|
||||
}
|
||||
|
||||
const [firstname, setFirstname] = useState("");
|
||||
const [lastname, setLastname] = useState("");
|
||||
const [title, setTitle] = useState("");
|
||||
const [companyname, setCompanyname] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
const [formMessage, setFormMessage] = useState("");
|
||||
|
||||
const submitContact = () => {
|
||||
const data = {
|
||||
"firstname": firstname,
|
||||
"lastname": lastname,
|
||||
"title": title,
|
||||
"companyname": companyname,
|
||||
"email": email,
|
||||
"phone": phone,
|
||||
"message": message,
|
||||
}
|
||||
console.log(data)
|
||||
|
||||
fetch(globalUrl + "/api/v1/contact", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.message)
|
||||
} else {
|
||||
setFormMessage("Something went wrong. Please contact frikky@shuffler.io.")
|
||||
}
|
||||
console.log(response)
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
});
|
||||
}
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageDataBrowser =
|
||||
<div>
|
||||
<div style={bodyTextStyle}>
|
||||
<h3 style={{ color: "#f85a3e" }}>Contact us</h3>
|
||||
<h2>Lets talk!</h2>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Contact Details</h2>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="First Name"
|
||||
type="firstname"
|
||||
id="standard-required"
|
||||
autoComplete="firstname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setFirstname(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Last Name"
|
||||
type="lastname"
|
||||
id="standard"
|
||||
autoComplete="lastname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setLastname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Job Title"
|
||||
type="jobtitle"
|
||||
id="standard-required"
|
||||
autoComplete="jobtitle"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
type="companyname"
|
||||
placeholder="Company Name"
|
||||
id="standard-required"
|
||||
autoComplete="companyname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setCompanyname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
id="standard-required"
|
||||
autoComplete="email"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
type="phone"
|
||||
placeholder="Phone number"
|
||||
id="standard-required"
|
||||
autoComplete="phone"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setPhone(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<h2>Message</h2>
|
||||
</div>
|
||||
<div style={{ flex: 4 }}>
|
||||
<TextField
|
||||
multiline
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
rows="6"
|
||||
fullWidth={true}
|
||||
placeholder="What can we help you with?"
|
||||
id="filled-multiline-static"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={email.length <= 0 || message.length <= 0}
|
||||
style={{ width: "100%", height: "60px", marginTop: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={submitContact}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<h3>{formMessage}</h3>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const landingpageDataMobile =
|
||||
<div style={{ paddingBottom: "50px" }}>
|
||||
<div style={{ color: "white", textAlign: "center" }}>
|
||||
<h3 style={{ color: "#f85a3e" }}>Contact us</h3>
|
||||
<h2>Lets talk!</h2>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Contact Details</h2>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Name"
|
||||
type="firstname"
|
||||
id="standard-required"
|
||||
autoComplete="firstname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setFirstname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
id="standard-required"
|
||||
autoComplete="email"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<h2>Message</h2>
|
||||
</div>
|
||||
<div style={{ flex: 4 }}>
|
||||
<TextField
|
||||
multiline
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
rows="6"
|
||||
fullWidth={true}
|
||||
placeholder="What can we help you with?"
|
||||
id="filled-multiline-static"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={email.length <= 0 || message.length <= 0}
|
||||
style={{ width: "100%", height: "60px", marginTop: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={submitContact}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<h3>{formMessage}</h3>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
const loadedCheck = isLoaded ?
|
||||
<div>
|
||||
<BrowserView>
|
||||
<div style={bodyDivStyle}>{landingpageDataBrowser}</div>
|
||||
</BrowserView>
|
||||
<MobileView>
|
||||
{landingpageDataMobile}
|
||||
</MobileView>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Contact;
|
||||
@@ -6,10 +6,16 @@ import theme from '../theme.jsx';
|
||||
const SetAuthentication = (props) => {
|
||||
const { globalUrl } = props;
|
||||
|
||||
var headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
const [firstRequest, setFirstRequest] = useState(true);
|
||||
const [finished, setFinished] = useState(false);
|
||||
const [response, setResponse] = useState("");
|
||||
const [failed, setFailed] = useState(false);
|
||||
const [requestHeaders, setRequestHeaders] = useState(headers);
|
||||
|
||||
if (firstRequest) {
|
||||
setFirstRequest(false);
|
||||
@@ -20,6 +26,8 @@ const SetAuthentication = (props) => {
|
||||
const params = Object.fromEntries(urlSearchParams.entries());
|
||||
console.log("PARAMS: ", params)
|
||||
|
||||
|
||||
|
||||
//const authenticationStore = [];
|
||||
var appAuthData = {
|
||||
label: "",
|
||||
@@ -40,7 +48,6 @@ const SetAuthentication = (props) => {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (params.session_state !== undefined && params.session_state !== null) {
|
||||
appAuthData.fields.push({
|
||||
key: "session_state",
|
||||
@@ -83,6 +90,14 @@ const SetAuthentication = (props) => {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (query[0] === "org_id") {
|
||||
headers["Org-Id"] = query[1]
|
||||
}
|
||||
|
||||
if (query[0] === "authorization") {
|
||||
headers["Authorization"] = "Bearer " + query[1]
|
||||
}
|
||||
|
||||
if (query[0] === "workflow_id") {
|
||||
appAuthData.reference_workflow = query[1];
|
||||
}
|
||||
@@ -136,6 +151,8 @@ const SetAuthentication = (props) => {
|
||||
appAuthData.fields.push({ key: "refresh_url", value: query[1] });
|
||||
}
|
||||
}
|
||||
|
||||
setRequestHeaders(headers)
|
||||
}
|
||||
|
||||
if (foundScope !== undefined && foundScope !== null && foundScope.length > 0) {
|
||||
@@ -153,15 +170,13 @@ const SetAuthentication = (props) => {
|
||||
setFailed(true)
|
||||
setResponse(`${foundTab}`)
|
||||
} else {
|
||||
|
||||
if (externalData.handleExternal) {
|
||||
console.log("RUN EXTERNAL!!: ", externalData)
|
||||
|
||||
fetch(globalUrl + "/api/v1/triggers/github/register", {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
headers: requestHeaders,
|
||||
credentials: "include",
|
||||
body: JSON.stringify(externalData),
|
||||
})
|
||||
@@ -191,7 +206,6 @@ const SetAuthentication = (props) => {
|
||||
.then((responseJson) => {
|
||||
//setUserSettings(responseJson)
|
||||
console.log("Resp: ", responseJson);
|
||||
|
||||
if (responseJson.reason !== undefined) {
|
||||
setResponse(responseJson.reason);
|
||||
setFinished(true);
|
||||
@@ -227,23 +241,16 @@ const SetAuthentication = (props) => {
|
||||
|
||||
fetch(globalUrl + "/api/v1/apps/authentication", {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
headers: requestHeaders,
|
||||
credentials: "include",
|
||||
body: JSON.stringify(appAuthData),
|
||||
})
|
||||
.then((response) => {
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
const tmpView = new URLSearchParams(cursearch).get("state");
|
||||
if (
|
||||
tmpView !== undefined &&
|
||||
tmpView !== null &&
|
||||
tmpView.length > 0
|
||||
) {
|
||||
console.log("State to find app name from: ", tmpView)
|
||||
}
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
const tmpView = new URLSearchParams(cursearch).get("state");
|
||||
if (tmpView !== undefined && tmpView !== null && tmpView.length > 0) {
|
||||
console.log("State to find app name from: ", tmpView)
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for oauth2 authentication");
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import ReactGA from "react-ga4";
|
||||
import {
|
||||
Typography,
|
||||
CircularProgress,
|
||||
Button,
|
||||
} from "@material-ui/core";
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
|
||||
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
|
||||
import AuthenticationWindow from "../components/AuthenticationWindow.jsx";
|
||||
import { base64_decode, appCategories } from "../views/AppCreator.jsx";
|
||||
|
||||
const SetAuthentication = (props) => {
|
||||
const { globalUrl, serverside } = props;
|
||||
|
||||
const [app, setApp] = useState({});
|
||||
const [isAppLoaded, setIsAppLoaded] = useState(false);
|
||||
const [loadFail, setLoadFail] = useState("");
|
||||
const [appAuthentication, setAppAuthentication] = React.useState([]);
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const alert = useAlert();
|
||||
|
||||
const parseIncomingOpenapiData = (data) => {
|
||||
if (data.app === undefined || data.app === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Should basically always be true if openapi exists too
|
||||
var parsedBaseapp = ""
|
||||
try {
|
||||
parsedBaseapp = base64_decode(data.app)
|
||||
} catch (e) {
|
||||
console.log("Failed JSON parsing: ", e)
|
||||
parsedBaseapp = data
|
||||
}
|
||||
|
||||
var parsedapp = JSON.parse(parsedBaseapp)
|
||||
parsedapp.name = parsedapp.name.replaceAll("_", " ");
|
||||
setApp(parsedapp);
|
||||
|
||||
document.title = parsedapp.name + " App Auth";
|
||||
|
||||
}
|
||||
|
||||
console.log("App: ", app)
|
||||
|
||||
const getApp = (appid) => {
|
||||
if (serverside === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`${globalUrl}/api/v1/apps/${appid}/config`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_not_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false || responseJson.success === undefined) {
|
||||
alert.error("Failed to get the app. Does it exist?")
|
||||
setIsAppLoaded(true)
|
||||
return;
|
||||
}
|
||||
|
||||
parseIncomingOpenapiData(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Error in app fetch: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Find the ID for the app from the "app_id" query
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const appid = urlParams.get("app_id");
|
||||
if (appid === null) {
|
||||
setLoadFail(
|
||||
<span>
|
||||
<Typography variant="h4">
|
||||
Failed to load the app. Please contact your provider or support@shuffler.io if this persists
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Reload Window
|
||||
</Button>
|
||||
</span>
|
||||
)
|
||||
} else {
|
||||
getApp(appid);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Handle:
|
||||
// 1. Check for org_id, authentication, and app keys in queries
|
||||
// 2. Load the app auth info from the orgs' apps
|
||||
// 3. Help them set info for the app
|
||||
// Make sure to test both private and public apps
|
||||
|
||||
const appname = app.name !== undefined ? app.name : "";
|
||||
|
||||
return (
|
||||
<div style={{width: 1000, margin: "auto", marginTop: 50, }}>
|
||||
{loadFail !== "" ?
|
||||
loadFail
|
||||
:
|
||||
<div>
|
||||
<Typography variant="h4" style={{marginBottom: 20,}}>
|
||||
Configure {appname} Authentication
|
||||
</Typography>
|
||||
{app.authentication === undefined || app.authentication === null || app.authentication.length === 0 ?
|
||||
null
|
||||
:
|
||||
app.authentication.type === "oauth2" ?
|
||||
<AuthenticationOauth2
|
||||
selectedApp={app}
|
||||
selectedAction={{
|
||||
"app_name": app.name,
|
||||
"app_id": app.id,
|
||||
"app_version": app.version,
|
||||
"large_image": app.large_image,
|
||||
}}
|
||||
authenticationType={app.authentication}
|
||||
isCloud={true}
|
||||
authButtonOnly={true}
|
||||
getAppAuthentication={undefined}
|
||||
/>
|
||||
:
|
||||
<AuthenticationWindow
|
||||
globalUrl={globalUrl}
|
||||
selectedApp={app}
|
||||
authFieldsOnly={true}
|
||||
getAppAuthentication={undefined}
|
||||
appAuthentication={appAuthentication}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export default SetAuthentication;
|
||||
@@ -462,7 +462,21 @@ export const validateJson = (showResult) => {
|
||||
try {
|
||||
var newstr = showResult.replaceAll("'", '"')
|
||||
|
||||
//console.log("Try replacements and trimming with new value: ", newstr)
|
||||
// Basic workarounds for issues with Python Dicts -> JSON
|
||||
if (newstr.includes(": None")) {
|
||||
newstr = newstr.replaceAll(": None", ': null')
|
||||
}
|
||||
|
||||
if (newstr.includes("[\"{") && newstr.includes("}\"]")) {
|
||||
newstr = newstr.replaceAll("[\"{", '[{')
|
||||
newstr = newstr.replaceAll("}\"]", '}]')
|
||||
}
|
||||
|
||||
if (newstr.includes("{\"[") && newstr.includes("]\"}")) {
|
||||
newstr = newstr.replaceAll("{\"[", '[{')
|
||||
newstr = newstr.replaceAll("]\"}", '}]')
|
||||
}
|
||||
|
||||
result = JSON.parse(newstr)
|
||||
jsonvalid = true
|
||||
} catch (e) {
|
||||
|
||||