Loads of more bugfixes and tests
This commit is contained in:
@@ -545,9 +545,49 @@ const AppFramework = (props) => {
|
|||||||
|
|
||||||
const alert = useAlert()
|
const alert = useAlert()
|
||||||
|
|
||||||
|
const handleLoadNextSuggestion = (frameworkData) => {
|
||||||
|
console.log("Should check for next apps to load from App suggestion model")
|
||||||
|
//fetch(globalUrl + "/api/v1/workflows/usecases", {
|
||||||
|
//credentials: "include",
|
||||||
|
|
||||||
|
fetch("https://europe-west2-shuffler.cloudfunctions.net/app_recommendations", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(frameworkData),
|
||||||
|
cors: "no-cors",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
//if (response.status !== 200) {
|
||||||
|
// console.log("Status not 200 for framework!");
|
||||||
|
//}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
console.log("Response: ", responseJson)
|
||||||
|
//if (responseJson.success === false) {
|
||||||
|
// if (responseJson.reason !== undefined) {
|
||||||
|
// alert.error("Failed updating: " + responseJson.reason)
|
||||||
|
// } else {
|
||||||
|
// alert.error("Failed to update framework for your org.")
|
||||||
|
// }
|
||||||
|
//} else {
|
||||||
|
// alert.info("Updated usecase.")
|
||||||
|
//}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
//alert.error(error.toString());
|
||||||
|
//setFrameworkLoaded(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const showRecommendations = (changed, frameworkData) => {
|
const showRecommendations = (changed, frameworkData) => {
|
||||||
console.log("Inside recommendation loader")
|
console.log("Inside recommendation loader")
|
||||||
setChangedApp(changed)
|
setChangedApp(changed)
|
||||||
|
handleLoadNextSuggestion(frameworkData)
|
||||||
|
|
||||||
// Alternative changed
|
// Alternative changed
|
||||||
// This is for secondary values like email = comms
|
// This is for secondary values like email = comms
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const OrgHeader = (props) => {
|
|||||||
setSelectedOrganization,
|
setSelectedOrganization,
|
||||||
globalUrl,
|
globalUrl,
|
||||||
isCloud,
|
isCloud,
|
||||||
|
adminTab,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -41,103 +42,7 @@ const OrgHeader = (props) => {
|
|||||||
const [orgDescription, setOrgDescription] = React.useState(
|
const [orgDescription, setOrgDescription] = React.useState(
|
||||||
selectedOrganization.description
|
selectedOrganization.description
|
||||||
);
|
);
|
||||||
const [appDownloadUrl, setAppDownloadUrl] = React.useState(
|
|
||||||
selectedOrganization.defaults === undefined
|
|
||||||
? "https://github.com/frikky/shuffle-apps"
|
|
||||||
: selectedOrganization.defaults.app_download_repo === undefined ||
|
|
||||||
selectedOrganization.defaults.app_download_repo.length === 0
|
|
||||||
? "https://github.com/frikky/shuffle-apps"
|
|
||||||
: selectedOrganization.defaults.app_download_repo
|
|
||||||
);
|
|
||||||
const [appDownloadBranch, setAppDownloadBranch] = React.useState(
|
|
||||||
selectedOrganization.defaults === undefined
|
|
||||||
? defaultBranch
|
|
||||||
: selectedOrganization.defaults.app_download_branch === undefined ||
|
|
||||||
selectedOrganization.defaults.app_download_branch.length === 0
|
|
||||||
? defaultBranch
|
|
||||||
: selectedOrganization.defaults.app_download_branch
|
|
||||||
);
|
|
||||||
const [workflowDownloadUrl, setWorkflowDownloadUrl] = React.useState(
|
|
||||||
selectedOrganization.defaults === undefined
|
|
||||||
? "https://github.com/frikky/shuffle-apps"
|
|
||||||
: selectedOrganization.defaults.workflow_download_repo === undefined ||
|
|
||||||
selectedOrganization.defaults.workflow_download_repo.length === 0
|
|
||||||
? "https://github.com/frikky/shuffle-workflows"
|
|
||||||
: selectedOrganization.defaults.workflow_download_repo
|
|
||||||
);
|
|
||||||
const [workflowDownloadBranch, setWorkflowDownloadBranch] = React.useState(
|
|
||||||
selectedOrganization.defaults === undefined
|
|
||||||
? defaultBranch
|
|
||||||
: selectedOrganization.defaults.workflow_download_branch === undefined ||
|
|
||||||
selectedOrganization.defaults.workflow_download_branch.length === 0
|
|
||||||
? defaultBranch
|
|
||||||
: selectedOrganization.defaults.workflow_download_branch
|
|
||||||
);
|
|
||||||
const [ssoEntrypoint, setSsoEntrypoint] = React.useState(
|
|
||||||
selectedOrganization.sso_config === undefined
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.sso_entrypoint === undefined ||
|
|
||||||
selectedOrganization.sso_config.sso_entrypoint.length === 0
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.sso_entrypoint
|
|
||||||
);
|
|
||||||
const [ssoCertificate, setSsoCertificate] = React.useState(
|
|
||||||
selectedOrganization.sso_config === undefined
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.sso_certificate === undefined ||
|
|
||||||
selectedOrganization.sso_config.sso_certificate.length === 0
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.sso_certificate
|
|
||||||
);
|
|
||||||
const [notificationWorkflow, setNotificationWorkflow] = React.useState(
|
|
||||||
selectedOrganization.defaults === undefined
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.defaults.notification_workflow === undefined ||
|
|
||||||
selectedOrganization.defaults.notification_workflow.length === 0
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.defaults.notification_workflow
|
|
||||||
);
|
|
||||||
|
|
||||||
const [documentationReference, setDocumentationReference] = React.useState(
|
|
||||||
selectedOrganization.defaults === undefined
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.defaults.documentation_reference === undefined ||
|
|
||||||
selectedOrganization.defaults.documentation_reference.length === 0
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.defaults.documentation_reference
|
|
||||||
);
|
|
||||||
const [openidClientId, setOpenidClientId] = React.useState(
|
|
||||||
selectedOrganization.sso_config === undefined
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.client_id === undefined ||
|
|
||||||
selectedOrganization.sso_config.client_id.length === 0
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.client_id
|
|
||||||
);
|
|
||||||
const [openidClientSecret, setOpenidClientSecret] = React.useState(
|
|
||||||
selectedOrganization.sso_config === undefined
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.client_secret === undefined ||
|
|
||||||
selectedOrganization.sso_config.client_secret.length === 0
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.client_secret
|
|
||||||
);
|
|
||||||
const [openidAuthorization, setOpenidAuthorization] = React.useState(
|
|
||||||
selectedOrganization.sso_config === undefined
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.openid_authorization === undefined ||
|
|
||||||
selectedOrganization.sso_config.openid_authorization.length === 0
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.openid_authorization
|
|
||||||
);
|
|
||||||
const [openidToken, setOpenidToken] = React.useState(
|
|
||||||
selectedOrganization.sso_config === undefined
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.openid_token === undefined ||
|
|
||||||
selectedOrganization.sso_config.openid_token.length === 0
|
|
||||||
? ""
|
|
||||||
: selectedOrganization.sso_config.openid_token
|
|
||||||
)
|
|
||||||
|
|
||||||
const [file, setFile] = React.useState("");
|
const [file, setFile] = React.useState("");
|
||||||
const [fileBase64, setFileBase64] = React.useState(
|
const [fileBase64, setFileBase64] = React.useState(
|
||||||
@@ -249,20 +154,8 @@ const OrgHeader = (props) => {
|
|||||||
selectedOrganization.id,
|
selectedOrganization.id,
|
||||||
selectedOrganization.image,
|
selectedOrganization.image,
|
||||||
{
|
{
|
||||||
app_download_repo: appDownloadUrl,
|
|
||||||
app_download_branch: appDownloadBranch,
|
|
||||||
workflow_download_repo: workflowDownloadUrl,
|
|
||||||
workflow_download_branch: workflowDownloadBranch,
|
|
||||||
notification_workflow: notificationWorkflow,
|
|
||||||
documentation_reference: documentationReference,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sso_entrypoint: ssoEntrypoint,
|
|
||||||
sso_certificate: ssoCertificate,
|
|
||||||
client_id: openidClientId,
|
|
||||||
client_secret: openidClientSecret,
|
|
||||||
openid_authorization: openidAuthorization,
|
|
||||||
openid_token: openidToken,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -291,6 +184,7 @@ const OrgHeader = (props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
@@ -416,472 +310,6 @@ const OrgHeader = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<IconButton
|
|
||||||
style={{ color: "white", marginTop: 10 }}
|
|
||||||
onClick={() => {
|
|
||||||
setExpanded(!expanded);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{expanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
|
|
||||||
</IconButton>
|
|
||||||
{expanded ? (
|
|
||||||
<Grid container spacing={3} style={{ textAlign: "left" }}>
|
|
||||||
<Grid item xs={12} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>Notification Workflow ID</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="ID of the workflow to receive notifications"
|
|
||||||
value={notificationWorkflow}
|
|
||||||
onChange={(e) => {
|
|
||||||
setNotificationWorkflow(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>Org Documentation reference</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="URL to an external reference for this implementation"
|
|
||||||
value={documentationReference}
|
|
||||||
onChange={(e) => {
|
|
||||||
setDocumentationReference(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
{isCloud ? null :
|
|
||||||
<Grid item xs={12} style={{marginTop: 50 }}>
|
|
||||||
<Typography variant="h4" style={{textAlign: "center",}}>OpenID connect</Typography>
|
|
||||||
<Grid container style={{marginTop: 10, }}>
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>Client ID</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
multiline={true}
|
|
||||||
rows={2}
|
|
||||||
disabled={
|
|
||||||
selectedOrganization.manager_orgs !== undefined &&
|
|
||||||
selectedOrganization.manager_orgs !== null &&
|
|
||||||
selectedOrganization.manager_orgs.length > 0
|
|
||||||
}
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="The OpenID client ID from the identity provider"
|
|
||||||
value={openidClientId}
|
|
||||||
onChange={(e) => {
|
|
||||||
setOpenidClientId(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>Client Secret (optional)</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
multiline={true}
|
|
||||||
rows={2}
|
|
||||||
disabled={
|
|
||||||
selectedOrganization.manager_orgs !== undefined &&
|
|
||||||
selectedOrganization.manager_orgs !== null &&
|
|
||||||
selectedOrganization.manager_orgs.length > 0
|
|
||||||
}
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE"
|
|
||||||
value={openidClientSecret}
|
|
||||||
onChange={(e) => {
|
|
||||||
setOpenidClientSecret(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid container style={{marginTop: 10, }}>
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>Authorization URL</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
multiline={true}
|
|
||||||
rows={2}
|
|
||||||
placeholder="The OpenID authorization URL (usually ends with /authorize)"
|
|
||||||
value={openidAuthorization}
|
|
||||||
onChange={(e) => {
|
|
||||||
setOpenidAuthorization(e.target.value)
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>Token URL</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
multiline={true}
|
|
||||||
rows={2}
|
|
||||||
placeholder="The OpenID token URL (usually ends with /token)"
|
|
||||||
value={openidToken}
|
|
||||||
onChange={(e) => {
|
|
||||||
setOpenidToken(e.target.value)
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
{/*isCloud ? null : */}
|
|
||||||
<Grid item xs={12} style={{marginTop: 50,}}>
|
|
||||||
<Typography variant="h4" style={{textAlign: "center",}}>SAML SSO (v1.1)</Typography>
|
|
||||||
<Grid container style={{marginTop: 20, }}>
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>SSO Entrypoint (IdP)</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
multiline={true}
|
|
||||||
rows={2}
|
|
||||||
disabled={
|
|
||||||
selectedOrganization.manager_orgs !== undefined &&
|
|
||||||
selectedOrganization.manager_orgs !== null &&
|
|
||||||
selectedOrganization.manager_orgs.length > 0
|
|
||||||
}
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="The entrypoint URL from your provider"
|
|
||||||
value={ssoEntrypoint}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSsoEntrypoint(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>SSO Certificate (X509)</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
multiline={true}
|
|
||||||
rows={2}
|
|
||||||
placeholder="The X509 certificate to use"
|
|
||||||
value={ssoCertificate}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSsoCertificate(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
{isCloud ?
|
|
||||||
<Typography variant="body2" style={{textAlign: "left",}} color="textSecondary">
|
|
||||||
IdP URL for Shuffle: https://shuffler.io/api/v1/login_sso
|
|
||||||
</Typography>
|
|
||||||
: null}
|
|
||||||
</Grid>
|
|
||||||
{isCloud ? null : (
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>App Download URL</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="A description for the organization"
|
|
||||||
value={appDownloadUrl}
|
|
||||||
onChange={(e) => {
|
|
||||||
setAppDownloadUrl(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
{isCloud ? null : (
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>App Download Branch</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="A description for the organization"
|
|
||||||
value={appDownloadBranch}
|
|
||||||
onChange={(e) => {
|
|
||||||
setAppDownloadBranch(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
{isCloud ? null : (
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>Workflow Download URL</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="A description for the organization"
|
|
||||||
value={workflowDownloadUrl}
|
|
||||||
onChange={(e) => {
|
|
||||||
setWorkflowDownloadUrl(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
{isCloud ? null : (
|
|
||||||
<Grid item xs={6} style={{}}>
|
|
||||||
<span>
|
|
||||||
<Typography>Workflow Download Branch</Typography>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
style={{
|
|
||||||
flex: "1",
|
|
||||||
marginTop: "5px",
|
|
||||||
marginRight: "15px",
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
|
||||||
type="name"
|
|
||||||
id="outlined-with-placeholder"
|
|
||||||
margin="normal"
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="A description for the organization"
|
|
||||||
value={workflowDownloadBranch}
|
|
||||||
onChange={(e) => {
|
|
||||||
setWorkflowDownloadBranch(e.target.value);
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
classes: {
|
|
||||||
notchedOutline: classes.notchedOutline,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div style={{ margin: "auto", textalign: "center", marginTop: 15, marginBottom: 15, }}>
|
|
||||||
{orgSaveButton}
|
|
||||||
</div>
|
|
||||||
{/*
|
|
||||||
<span style={{textAlign: "center"}}>
|
|
||||||
{expanded ?
|
|
||||||
<ExpandLessIcon />
|
|
||||||
:
|
|
||||||
<ExpandMoreIcon />
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
*/}
|
|
||||||
</Grid>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import React, { useState, useEffect, useLayoutEffect } from "react";
|
||||||
|
import * as cytoscape from "cytoscape";
|
||||||
|
import CytoscapeComponent from "react-cytoscapejs";
|
||||||
|
import cystyle from "../defaultCytoscapeStyle";
|
||||||
|
|
||||||
|
const surfaceColor = "#27292D";
|
||||||
|
const CytoscapeWrapper = (props) => {
|
||||||
|
const { globalUrl, inworkflow } = props;
|
||||||
|
|
||||||
|
const [elements, setElements] = useState([]);
|
||||||
|
const [workflow, setWorkflow] = useState(inworkflow);
|
||||||
|
const [cy, setCy] = React.useState();
|
||||||
|
const bodyWidth = 200;
|
||||||
|
const bodyHeight = 150;
|
||||||
|
|
||||||
|
const setupGraph = () => {
|
||||||
|
const actions = workflow.actions.map((action) => {
|
||||||
|
const node = {};
|
||||||
|
node.position = action.position;
|
||||||
|
node.data = action;
|
||||||
|
|
||||||
|
node.data._id = action["id"];
|
||||||
|
node.data.type = "ACTION";
|
||||||
|
node.isStartNode = action["id"] === workflow.start;
|
||||||
|
|
||||||
|
var example = "";
|
||||||
|
if (
|
||||||
|
action.example !== undefined &&
|
||||||
|
action.example !== null &&
|
||||||
|
action.example.length > 0
|
||||||
|
) {
|
||||||
|
example = action.example;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.data.example = example;
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
|
||||||
|
const triggers = workflow.triggers.map((trigger) => {
|
||||||
|
const node = {};
|
||||||
|
node.position = trigger.position;
|
||||||
|
node.data = trigger;
|
||||||
|
|
||||||
|
node.data._id = trigger["id"];
|
||||||
|
node.data.type = "TRIGGER";
|
||||||
|
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
|
||||||
|
// FIXME - tmp branch update
|
||||||
|
var insertedNodes = [].concat(actions, triggers);
|
||||||
|
const edges = workflow.branches.map((branch, index) => {
|
||||||
|
//workflow.branches[index].conditions = [{
|
||||||
|
|
||||||
|
const edge = {};
|
||||||
|
var conditions = workflow.branches[index].conditions;
|
||||||
|
if (conditions === undefined || conditions === null) {
|
||||||
|
conditions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var label = "";
|
||||||
|
if (conditions.length === 1) {
|
||||||
|
label = conditions.length + " condition";
|
||||||
|
} else if (conditions.length > 1) {
|
||||||
|
label = conditions.length + " conditions";
|
||||||
|
}
|
||||||
|
|
||||||
|
edge.data = {
|
||||||
|
id: branch.id,
|
||||||
|
_id: branch.id,
|
||||||
|
source: branch.source_id,
|
||||||
|
target: branch.destination_id,
|
||||||
|
label: label,
|
||||||
|
conditions: conditions,
|
||||||
|
hasErrors: branch.has_errors,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is an attempt at prettier edges. The numbers are weird to work with.
|
||||||
|
/*
|
||||||
|
//http://manual.graphspace.org/projects/graphspace-python/en/latest/demos/edge-types.html
|
||||||
|
const sourcenode = actions.find(node => node.data._id === branch.source_id)
|
||||||
|
const destinationnode = actions.find(node => node.data._id === branch.destination_id)
|
||||||
|
if (sourcenode !== undefined && destinationnode !== undefined && branch.source_id !== branch.destination_id) {
|
||||||
|
//node.data._id = action["id"]
|
||||||
|
console.log("SOURCE: ", sourcenode.position)
|
||||||
|
console.log("DESTINATIONNODE: ", destinationnode.position)
|
||||||
|
|
||||||
|
var opposite = true
|
||||||
|
if (sourcenode.position.x > destinationnode.position.x) {
|
||||||
|
opposite = false
|
||||||
|
} else {
|
||||||
|
opposite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
edge.style = {
|
||||||
|
'control-point-distance': opposite ? ["25%", "-75%"] : ["-10%", "90%"],
|
||||||
|
'control-point-weight': ['0.3', '0.7'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return edge;
|
||||||
|
});
|
||||||
|
|
||||||
|
setWorkflow(workflow);
|
||||||
|
|
||||||
|
// Verifies if a branch is valid and skips others
|
||||||
|
var newedges = [];
|
||||||
|
for (var key in edges) {
|
||||||
|
var item = edges[key];
|
||||||
|
|
||||||
|
const sourcecheck = insertedNodes.find(
|
||||||
|
(data) => data.data.id === item.data.source
|
||||||
|
);
|
||||||
|
const destcheck = insertedNodes.find(
|
||||||
|
(data) => data.data.id === item.data.target
|
||||||
|
);
|
||||||
|
if (sourcecheck === undefined || destcheck === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newedges.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertedNodes = insertedNodes.concat(newedges);
|
||||||
|
setElements(insertedNodes);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (elements.length === 0) {
|
||||||
|
setupGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CytoscapeComponent
|
||||||
|
elements={elements}
|
||||||
|
minZoom={0.35}
|
||||||
|
maxZoom={2.0}
|
||||||
|
style={{
|
||||||
|
width: bodyWidth - 15,
|
||||||
|
height: bodyHeight - 5,
|
||||||
|
backgroundColor: surfaceColor,
|
||||||
|
}}
|
||||||
|
stylesheet={cystyle}
|
||||||
|
boxSelectionEnabled={true}
|
||||||
|
autounselectify={false}
|
||||||
|
showGrid={true}
|
||||||
|
cy={(incy) => {
|
||||||
|
// FIXME: There's something specific loading when
|
||||||
|
// you do the first hover of a node. Why is this different?
|
||||||
|
//console.log("CY: ", incy)
|
||||||
|
setCy(incy);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CytoscapeWrapper;
|
||||||
@@ -0,0 +1,648 @@
|
|||||||
|
import React, {useState, useEffect, useRef} from 'react';
|
||||||
|
|
||||||
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
|
import { useTheme } from '@material-ui/core/styles';
|
||||||
|
import SearchIcon from '@material-ui/icons/Search';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Chip,
|
||||||
|
IconButton,
|
||||||
|
TextField,
|
||||||
|
InputAdornment,
|
||||||
|
List,
|
||||||
|
Card,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemText,
|
||||||
|
Avatar,
|
||||||
|
Typography,
|
||||||
|
Tooltip,
|
||||||
|
} from '@material-ui/core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AvatarGroup,
|
||||||
|
} from "@mui/material"
|
||||||
|
|
||||||
|
import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons'
|
||||||
|
|
||||||
|
import algoliasearch from 'algoliasearch/lite';
|
||||||
|
import aa from 'search-insights'
|
||||||
|
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||||
|
//import { InstantSearch, SearchBox, Hits, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||||
|
|
||||||
|
// https://www.algolia.com/doc/api-reference/widgets/search-box/react/
|
||||||
|
const chipStyle = {
|
||||||
|
backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||||
|
const SearchField = props => {
|
||||||
|
const { serverside, userdata } = props
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
let navigate = useNavigate();
|
||||||
|
const borderRadius = 3
|
||||||
|
const node = useRef()
|
||||||
|
const [searchOpen, setSearchOpen] = useState(false)
|
||||||
|
const [oldPath, setOldPath] = useState("")
|
||||||
|
|
||||||
|
if (serverside === true) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window !== undefined && window.location !== undefined && window.location.pathname === "/search") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||||
|
|
||||||
|
if (window.location.pathname !== oldPath) {
|
||||||
|
setSearchOpen(false)
|
||||||
|
setOldPath(window.location.pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
//useEffect(() => {
|
||||||
|
// if (searchOpen) {
|
||||||
|
// var tarfield = document.getElementById("shuffle_search_field")
|
||||||
|
// tarfield.focus()
|
||||||
|
// }
|
||||||
|
//}, searchOpen)
|
||||||
|
|
||||||
|
const SearchBox = ({currentRefinement, refine, isSearchStalled, } ) => {
|
||||||
|
|
||||||
|
/*
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end" style={{textAlign: "right", zIndex: 5001, cursor: "pointer", width: 100, }} onMouseOver={(event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
}}>
|
||||||
|
<CloseIcon style={{marginRight: 5,}} onClick={() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}} />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form id="search_form" noValidate type="searchbox" action="" role="search" style={{margin: 0, }} onClick={() => {
|
||||||
|
}}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||||
|
InputProps={{
|
||||||
|
style:{
|
||||||
|
color: "white",
|
||||||
|
fontSize: "1em",
|
||||||
|
height: 50,
|
||||||
|
margin: 0,
|
||||||
|
},
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon style={{marginLeft: 5}}/>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
autoComplete='off'
|
||||||
|
type="search"
|
||||||
|
color="primary"
|
||||||
|
placeholder="Find Public Apps, Workflows, Documentation and more"
|
||||||
|
value={currentRefinement}
|
||||||
|
id="shuffle_search_field"
|
||||||
|
onClick={(event) => {
|
||||||
|
if (!searchOpen) {
|
||||||
|
setSearchOpen(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
var tarfield = document.getElementById("shuffle_search_field")
|
||||||
|
//console.log("TARFIELD: ", tarfield)
|
||||||
|
tarfield.focus()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={(event) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}, 500)
|
||||||
|
}}
|
||||||
|
onChange={(event) => {
|
||||||
|
//if (event.currentTarget.value.length > 0 && !searchOpen) {
|
||||||
|
// setSearchOpen(true)
|
||||||
|
//}
|
||||||
|
|
||||||
|
refine(event.currentTarget.value)
|
||||||
|
}}
|
||||||
|
limit={5}
|
||||||
|
/>
|
||||||
|
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const WorkflowHits = ({ hits }) => {
|
||||||
|
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||||
|
|
||||||
|
var tmp = searchOpen
|
||||||
|
if (!searchOpen) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||||
|
const outerlistitemStyle = {
|
||||||
|
width: "100%",
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.length > 4) {
|
||||||
|
hits = hits.slice(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = "workflows"
|
||||||
|
const baseImage = <PolymerIcon />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: 405, height: 408, left: 75, boxShadows: "none",}}>
|
||||||
|
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
||||||
|
Workflows
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
||||||
|
{hits.length === 0 ?
|
||||||
|
<ListItem style={outerlistitemStyle}>
|
||||||
|
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||||
|
<Avatar>
|
||||||
|
<FolderIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={"No workflows found."}
|
||||||
|
secondary={"Try a broader search term"}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
:
|
||||||
|
hits.map((hit, index) => {
|
||||||
|
const innerlistitemStyle = {
|
||||||
|
width: positionInfo.width+35,
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginLeft: 5,
|
||||||
|
marginRight: 5,
|
||||||
|
maxHeight: 75,
|
||||||
|
minHeight: 75,
|
||||||
|
maxWidth: 420,
|
||||||
|
minWidth: "100%",
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = hit.name === undefined ?
|
||||||
|
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
||||||
|
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
||||||
|
const secondaryText = hit.description !== undefined && hit.description !== null && hit.description.length > 3 ? hit.description.slice(0, 40)+"..." : ""
|
||||||
|
const appGroup = hit.action_references === undefined || hit.action_references === null ? [] : hit.action_references
|
||||||
|
const avatar = baseImage
|
||||||
|
|
||||||
|
var parsedUrl = isCloud ? `/workflows/${hit.objectID}` : `https://shuffler.io/workflows/${hit.objectID}`
|
||||||
|
parsedUrl += `?queryID=${hit.__queryID}`
|
||||||
|
|
||||||
|
// <a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||||
|
return (
|
||||||
|
<Link key={hit.objectID} to={{ pathname: parsedUrl }} rel="noopener noreferrer" style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||||
|
//console.log("CLICK")
|
||||||
|
setSearchOpen(true)
|
||||||
|
|
||||||
|
aa('init', {
|
||||||
|
appId: searchClient.appId,
|
||||||
|
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||||
|
})
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
aa('sendEvents', [
|
||||||
|
{
|
||||||
|
eventType: 'click',
|
||||||
|
eventName: 'Workflow Clicked',
|
||||||
|
index: 'workflows',
|
||||||
|
objectIDs: [hit.objectID],
|
||||||
|
timestamp: timestamp,
|
||||||
|
queryID: hit.__queryID,
|
||||||
|
positions: [hit.__position],
|
||||||
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!isCloud) {
|
||||||
|
event.preventDefault()
|
||||||
|
window.open(parsedUrl, '_blank');
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||||
|
setMouseHoverIndex(index)
|
||||||
|
}}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
{avatar}
|
||||||
|
</ListItemAvatar>
|
||||||
|
<div style={{}}>
|
||||||
|
<ListItemText
|
||||||
|
primary={name}
|
||||||
|
/>
|
||||||
|
<AvatarGroup max={10} style={{flexDirection: "row", padding: 0, margin: 0, itemAlign: "left", textAlign: "left",}}>
|
||||||
|
{appGroup.map((app, index) => {
|
||||||
|
// Putting all this in secondary of ListItemText looked weird.
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
style={{
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
filter: "brightness(0.6)",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/apps/"+app.id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tooltip color="primary" title={app.name} placement="bottom">
|
||||||
|
<Avatar alt={app.name} src={app.image_url} style={{width: 24, height: 24}}/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</AvatarGroup>
|
||||||
|
</div>
|
||||||
|
{/*
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton edge="end" aria-label="delete">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
*/}
|
||||||
|
</ListItem>
|
||||||
|
</Link>
|
||||||
|
)})
|
||||||
|
}
|
||||||
|
</List>
|
||||||
|
{/*
|
||||||
|
<span style={{display: "flex", textAlign: "left", float: "left", position: "absolute", left: 15, bottom: 10, }}>
|
||||||
|
<Link to="/search" style={{textDecoration: "none", color: "#f85a3e"}}>
|
||||||
|
<Typography variant="body2" style={{}}>
|
||||||
|
See all workflows
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
*/}
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppHits = ({ hits }) => {
|
||||||
|
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||||
|
|
||||||
|
var tmp = searchOpen
|
||||||
|
if (!searchOpen) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||||
|
const outerlistitemStyle = {
|
||||||
|
width: "100%",
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.length > 4) {
|
||||||
|
hits = hits.slice(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = "app"
|
||||||
|
const baseImage = <LibraryBooksIcon />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, width: 1155, height: 408, left: -305, boxShadows: "none",}}>
|
||||||
|
<IconButton style={{zIndex: 5000, position: "absolute", right: 14, color: "grey"}} onClick={() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
||||||
|
Apps
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
||||||
|
{hits.length === 0 ?
|
||||||
|
<ListItem style={outerlistitemStyle}>
|
||||||
|
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||||
|
<Avatar>
|
||||||
|
<FolderIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={"No apps found."}
|
||||||
|
secondary={"Try a broader search term"}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
:
|
||||||
|
hits.map((hit, index) => {
|
||||||
|
const innerlistitemStyle = {
|
||||||
|
width: positionInfo.width+35,
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginLeft: 5,
|
||||||
|
marginRight: 5,
|
||||||
|
maxHeight: 75,
|
||||||
|
minHeight: 75,
|
||||||
|
maxWidth: 420,
|
||||||
|
minWidth: "100%",
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = hit.name === undefined ?
|
||||||
|
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
||||||
|
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
||||||
|
|
||||||
|
var secondaryText = hit.data !== undefined ? hit.data.slice(0, 40)+"..." : ""
|
||||||
|
const avatar = hit.image_url === undefined ?
|
||||||
|
baseImage
|
||||||
|
:
|
||||||
|
<Avatar
|
||||||
|
src={hit.image_url}
|
||||||
|
variant="rounded"
|
||||||
|
/>
|
||||||
|
|
||||||
|
//console.log(hit)
|
||||||
|
if (hit.categories !== undefined && hit.categories !== null && hit.categories.length > 0) {
|
||||||
|
secondaryText = hit.categories.slice(0,3).map((data, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
return ", "+data
|
||||||
|
|
||||||
|
/*
|
||||||
|
<Chip
|
||||||
|
key={index}
|
||||||
|
style={chipStyle}
|
||||||
|
label={data}
|
||||||
|
onClick={() => {
|
||||||
|
//handleChipClick
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}`
|
||||||
|
parsedUrl += `?queryID=${hit.__queryID}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link key={hit.objectID} to={{ pathname: parsedUrl }} style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||||
|
console.log("CLICK")
|
||||||
|
setSearchOpen(true)
|
||||||
|
|
||||||
|
aa('init', {
|
||||||
|
appId: searchClient.appId,
|
||||||
|
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||||
|
})
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
aa('sendEvents', [
|
||||||
|
{
|
||||||
|
eventType: 'click',
|
||||||
|
eventName: 'App Clicked',
|
||||||
|
index: 'appsearch',
|
||||||
|
objectIDs: [hit.objectID],
|
||||||
|
timestamp: timestamp,
|
||||||
|
queryID: hit.__queryID,
|
||||||
|
positions: [hit.__position],
|
||||||
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!isCloud) {
|
||||||
|
event.preventDefault()
|
||||||
|
window.open(parsedUrl, '_blank');
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||||
|
setMouseHoverIndex(index)
|
||||||
|
}}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
{avatar}
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={name}
|
||||||
|
secondary={secondaryText}
|
||||||
|
/>
|
||||||
|
{/*
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton edge="end" aria-label="delete">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
*/}
|
||||||
|
</ListItem>
|
||||||
|
</Link>
|
||||||
|
)})
|
||||||
|
}
|
||||||
|
</List>
|
||||||
|
<span style={{display: "flex", textAlign: "left", float: "left", position: "absolute", left: 15, bottom: 10, }}>
|
||||||
|
<Link to="/search" style={{textDecoration: "none", color: "#f85a3e"}}>
|
||||||
|
<Typography variant="body1" style={{}}>
|
||||||
|
See more
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const DocHits = ({ hits }) => {
|
||||||
|
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||||
|
|
||||||
|
var tmp = searchOpen
|
||||||
|
if (!searchOpen) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||||
|
const outerlistitemStyle = {
|
||||||
|
width: "100%",
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.length > 4) {
|
||||||
|
hits = hits.slice(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = "documentation"
|
||||||
|
const baseImage = <LibraryBooksIcon />
|
||||||
|
|
||||||
|
//console.log(type, hits.length, hits)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: 405, height: 408, left: 470, boxShadows: "none",}}>
|
||||||
|
<IconButton style={{zIndex: 5000, position: "absolute", right: 14, color: "grey"}} onClick={() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
||||||
|
Documentation
|
||||||
|
</Typography>
|
||||||
|
{/*
|
||||||
|
<IconButton edge="end" aria-label="delete" style={{position: "absolute", top: 5, right: 15,}} onClick={() => {
|
||||||
|
setSearchOpen(false)
|
||||||
|
}}>
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
*/}
|
||||||
|
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
||||||
|
{hits.length === 0 ?
|
||||||
|
<ListItem style={outerlistitemStyle}>
|
||||||
|
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||||
|
<Avatar>
|
||||||
|
<FolderIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={"No documentation."}
|
||||||
|
secondary={"Try a broader search term"}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
:
|
||||||
|
hits.map((hit, index) => {
|
||||||
|
const innerlistitemStyle = {
|
||||||
|
width: positionInfo.width+35,
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginLeft: 5,
|
||||||
|
marginRight: 5,
|
||||||
|
maxHeight: 75,
|
||||||
|
minHeight: 75,
|
||||||
|
maxWidth: 420,
|
||||||
|
minWidth: "100%",
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = hit.name === undefined ?
|
||||||
|
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title
|
||||||
|
:
|
||||||
|
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
||||||
|
|
||||||
|
if (name.length > 30) {
|
||||||
|
name = name.slice(0, 30)+"..."
|
||||||
|
}
|
||||||
|
const secondaryText = hit.data !== undefined ? hit.data.slice(0, 40)+"..." : ""
|
||||||
|
const avatar = hit.image_url === undefined ?
|
||||||
|
baseImage
|
||||||
|
:
|
||||||
|
<Avatar
|
||||||
|
src={hit.image_url}
|
||||||
|
variant="rounded"
|
||||||
|
/>
|
||||||
|
|
||||||
|
var parsedUrl = hit.urlpath !== undefined ? hit.urlpath : ""
|
||||||
|
parsedUrl += `?queryID=${hit.__queryID}`
|
||||||
|
if (parsedUrl.includes("/apps/")) {
|
||||||
|
const extraHash = hit.url_hash === undefined ? "" : `#${hit.url_hash}`
|
||||||
|
|
||||||
|
parsedUrl = `/apps/${hit.filename}?tab=docs&queryID=${hit.__queryID}${extraHash}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link key={hit.objectID} to={parsedUrl} style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||||
|
aa('init', {
|
||||||
|
appId: searchClient.appId,
|
||||||
|
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||||
|
})
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
aa('sendEvents', [
|
||||||
|
{
|
||||||
|
eventType: 'click',
|
||||||
|
eventName: 'Document Clicked',
|
||||||
|
index: 'documentation',
|
||||||
|
objectIDs: [hit.objectID],
|
||||||
|
timestamp: timestamp,
|
||||||
|
queryID: hit.__queryID,
|
||||||
|
positions: [hit.__position],
|
||||||
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
console.log("CLICK")
|
||||||
|
setSearchOpen(true)
|
||||||
|
}}>
|
||||||
|
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||||
|
setMouseHoverIndex(index)
|
||||||
|
}}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
{avatar}
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={name}
|
||||||
|
secondary={secondaryText}
|
||||||
|
/>
|
||||||
|
{/*
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton edge="end" aria-label="delete">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
*/}
|
||||||
|
</ListItem>
|
||||||
|
</Link>
|
||||||
|
)})
|
||||||
|
}
|
||||||
|
</List>
|
||||||
|
{type === "documentation" ?
|
||||||
|
<span style={{display: "flex", textAlign: "right", position: "absolute", right: 15, bottom: 10,}}>
|
||||||
|
<Typography variant="body2" style={{}}>
|
||||||
|
Search by
|
||||||
|
</Typography>
|
||||||
|
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||||
|
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
: null}
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomSearchBox = connectSearchBox(SearchBox)
|
||||||
|
const CustomAppHits = connectHits(AppHits)
|
||||||
|
const CustomWorkflowHits = connectHits(WorkflowHits)
|
||||||
|
const CustomDocHits = connectHits(DocHits)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={node} style={{width: "100%", maxWidth: 425, margin: "auto", position: "relative", zIndex: 12500,}}>
|
||||||
|
<InstantSearch searchClient={searchClient} indexName="appsearch" onClick={() => {
|
||||||
|
console.log("CLICKED")
|
||||||
|
}}>
|
||||||
|
<Configure clickAnalytics />
|
||||||
|
<CustomSearchBox />
|
||||||
|
<Index indexName="appsearch">
|
||||||
|
<CustomAppHits />
|
||||||
|
</Index>
|
||||||
|
<Index indexName="documentation">
|
||||||
|
<CustomDocHits />
|
||||||
|
</Index>
|
||||||
|
<Index indexName="workflows">
|
||||||
|
<CustomWorkflowHits />
|
||||||
|
</Index>
|
||||||
|
</InstantSearch>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchField;
|
||||||
@@ -641,9 +641,10 @@ const CodeEditor = (props) => {
|
|||||||
backgroundColor: theme.palette.surfaceColor,
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
color: "white",
|
color: "white",
|
||||||
minWidth: isMobile ? "100%" : 600,
|
minWidth: isMobile ? "100%" : 600,
|
||||||
|
maxHeight: isMobile ? "100%" : 800,
|
||||||
padding: isMobile ? "25px 10px 25px 10px" : 25,
|
padding: isMobile ? "25px 10px 25px 10px" : 25,
|
||||||
border: theme.palette.defaultBorder,
|
border: theme.palette.defaultBorder,
|
||||||
zIndex: 10012,
|
zIndex: 12501,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1590,7 +1590,7 @@ const UsecaseSearch = (props) => {
|
|||||||
style={{ textDecoration: "none", }}
|
style={{ textDecoration: "none", }}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
style={{borderRadius: 25, marginLeft: 114, marginTop: 35, }}
|
style={{borderRadius: 25, marginLeft: 130, marginTop: 35, }}
|
||||||
variant={"outlined"}
|
variant={"outlined"}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -144,6 +144,14 @@ const WelcomeForm = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCloud) {
|
||||||
|
ReactGA.event({
|
||||||
|
category: "welcome",
|
||||||
|
action: `click_${label}`,
|
||||||
|
label: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
setSelectionOpen(true)
|
setSelectionOpen(true)
|
||||||
setDefaultSearch(label)
|
setDefaultSearch(label)
|
||||||
}
|
}
|
||||||
@@ -585,14 +593,8 @@ const WelcomeForm = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Fade in={true}>
|
<Fade in={true}>
|
||||||
<div style={{minHeight: sizing, maxHeight: sizing, marginTop: 20, maxWidth: 500, }}>
|
<div style={{minHeight: sizing, maxHeight: sizing, marginTop: 20, maxWidth: 500, }}>
|
||||||
<Typography variant="body1" style={{marginLeft: 8, marginTop: 25, marginRight: 30, marginBottom: 0, }} color="textSecondary">
|
<Typography variant="body1" style={{marginLeft: 8, marginTop: 50, marginRight: 30, marginBottom: 0, }} color="textSecondary">
|
||||||
Clicks the buttons below to find your apps, then we will help you find relevant workflows. Can't find your app? <span style={{color: "#f86a3e", cursor: "pointer"}} onClick={() => {
|
Use the buttons below to find your apps, and we will help you connect them later.
|
||||||
if (window.drift !== undefined) {
|
|
||||||
window.drift.api.startInteraction({ interactionId: 340043 })
|
|
||||||
} else {
|
|
||||||
console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
|
|
||||||
}
|
|
||||||
}}>Contact our App Developers!</span>
|
|
||||||
</Typography>
|
</Typography>
|
||||||
{/*The app framework helps us access and authenticate the most important APIs for you. */}
|
{/*The app framework helps us access and authenticate the most important APIs for you. */}
|
||||||
|
|
||||||
@@ -833,7 +835,7 @@ const WelcomeForm = (props) => {
|
|||||||
disabled={activeStep === 0}
|
disabled={activeStep === 0}
|
||||||
onClick={handleBack}
|
onClick={handleBack}
|
||||||
variant={"outlined"}
|
variant={"outlined"}
|
||||||
style={{marginLeft: 10, height: 64, width: 100, position: "absolute", top: activeStep === 1 ? -600-extraHeight : -577, left: activeStep === 1 ? 105 : -145+clickdiff, borderRadius: "50px 0px 0px 50px", }}
|
style={{marginLeft: 10, height: 64, width: 100, position: "absolute", top: activeStep === 1 ? -625-extraHeight : -576, left: activeStep === 1 ? 125 : -125+clickdiff, borderRadius: "50px 0px 0px 50px", }}
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
@@ -841,7 +843,7 @@ const WelcomeForm = (props) => {
|
|||||||
variant={"outlined"}
|
variant={"outlined"}
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
style={{marginLeft: 10, height: 64, width: 100, position: "absolute", top: activeStep === 1 ? -600-extraHeight: -577, left: activeStep === 1 ? 748 : 510+clickdiff, borderRadius: "0px 50px 50px 0px", }}
|
style={{marginLeft: 10, height: 64, width: 100, position: "absolute", top: activeStep === 1 ? -625-extraHeight : -576, left: activeStep === 1 ? 738 : 489+clickdiff, borderRadius: "0px 50px 50px 0px", }}
|
||||||
disabled={activeStep === 0 ? orgName.length === 0 || name.length === 0 : false}
|
disabled={activeStep === 0 ? orgName.length === 0 || name.length === 0 : false}
|
||||||
>
|
>
|
||||||
{activeStep === steps.length - 1 ? "Finish" : "Next"}
|
{activeStep === steps.length - 1 ? "Finish" : "Next"}
|
||||||
|
|||||||
+315
-330
@@ -72,9 +72,11 @@ import {
|
|||||||
} from "@material-ui/icons";
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
import Dropzone from "../components/Dropzone";
|
import Dropzone from "../components/Dropzone.jsx";
|
||||||
import HandlePaymentNew from "./HandlePaymentNew";
|
import HandlePaymentNew from "../views/HandlePaymentNew.jsx";
|
||||||
import OrgHeader from "../components/OrgHeader.jsx";
|
import OrgHeader from "../components/OrgHeader.jsx";
|
||||||
|
import OrgHeaderexpanded from "../components/OrgHeaderexpanded.jsx";
|
||||||
|
import Billing from "../components/Billing.jsx";
|
||||||
import { display, style } from "@mui/system";
|
import { display, style } from "@mui/system";
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
@@ -188,8 +190,10 @@ const Admin = (props) => {
|
|||||||
const [dealDiscount, setDealDiscount] = React.useState("");
|
const [dealDiscount, setDealDiscount] = React.useState("");
|
||||||
const [dealerror, setDealerror] = React.useState("");
|
const [dealerror, setDealerror] = React.useState("");
|
||||||
const [dealList, setDealList] = React.useState([]);
|
const [dealList, setDealList] = React.useState([]);
|
||||||
|
const [adminTab, setAdminTab] = React.useState(1);
|
||||||
|
|
||||||
const [fileContent, setFileContent] = React.useState("");
|
const [fileContent, setFileContent] = React.useState("");
|
||||||
|
const [billingInfo, setBillingInfo] = React.useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDropzone) {
|
if (isDropzone) {
|
||||||
@@ -1562,6 +1566,14 @@ const Admin = (props) => {
|
|||||||
5: "environments",
|
5: "environments",
|
||||||
6: "suborgs",
|
6: "suborgs",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const admin_views = {
|
||||||
|
0: "organization",
|
||||||
|
1: "cloud_sync",
|
||||||
|
2: "billing",
|
||||||
|
3: "stats",
|
||||||
|
};
|
||||||
|
|
||||||
const setConfig = (event, inputValue) => {
|
const setConfig = (event, inputValue) => {
|
||||||
const newValue = parseInt(inputValue);
|
const newValue = parseInt(inputValue);
|
||||||
|
|
||||||
@@ -1612,17 +1624,29 @@ const Admin = (props) => {
|
|||||||
) {
|
) {
|
||||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||||
const params = Object.fromEntries(urlSearchParams.entries());
|
const params = Object.fromEntries(urlSearchParams.entries());
|
||||||
const foundTab = params["tab"];
|
|
||||||
if (foundTab !== null && foundTab !== undefined) {
|
const adminTab = params["admin_tab"];
|
||||||
for (var key in Object.keys(views)) {
|
if (adminTab !== null && adminTab !== undefined) {
|
||||||
const value = views[key];
|
for (var key in Object.keys(admin_views)) {
|
||||||
console.log(key, value);
|
const value = admin_views[key];
|
||||||
if (value === foundTab) {
|
if (value === adminTab) {
|
||||||
setConfig("", key);
|
setAdminTab(parseInt(key));
|
||||||
|
setConfig("", 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
const foundTab = params["tab"];
|
||||||
|
if (foundTab !== null && foundTab !== undefined) {
|
||||||
|
for (var key in Object.keys(views)) {
|
||||||
|
const value = views[key];
|
||||||
|
if (value === foundTab) {
|
||||||
|
setConfig("", key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2612,47 +2636,6 @@ const Admin = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelSubscriptions = (subscription_id) => {
|
|
||||||
console.log(selectedOrganization);
|
|
||||||
const orgId = selectedOrganization.id;
|
|
||||||
const data = {
|
|
||||||
subscription_id: subscription_id,
|
|
||||||
action: "cancel",
|
|
||||||
org_id: selectedOrganization.id,
|
|
||||||
};
|
|
||||||
|
|
||||||
const url = globalUrl + `/api/v1/orgs/${orgId}`;
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
handleGetOrg(selectedOrganization.id);
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function (responseJson) {
|
|
||||||
if (responseJson.success !== undefined && responseJson.success) {
|
|
||||||
alert.success("Successfully stopped subscription!");
|
|
||||||
} else {
|
|
||||||
alert.error("Failed stopping subscription. Please contact us.");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (error) {
|
|
||||||
console.log("Error: ", error);
|
|
||||||
alert.error("Failed stopping subscription. Please contact us.");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const organizationView =
|
const organizationView =
|
||||||
curTab === 0 && selectedOrganization.id !== undefined ? (
|
curTab === 0 && selectedOrganization.id !== undefined ? (
|
||||||
@@ -2758,6 +2741,7 @@ const Admin = (props) => {
|
|||||||
setSelectedOrganization={setSelectedOrganization}
|
setSelectedOrganization={setSelectedOrganization}
|
||||||
globalUrl={globalUrl}
|
globalUrl={globalUrl}
|
||||||
selectedOrganization={selectedOrganization}
|
selectedOrganization={selectedOrganization}
|
||||||
|
adminTab={adminTab}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
@@ -2772,6 +2756,51 @@ const Admin = (props) => {
|
|||||||
<Typography>Loading Organization</Typography>
|
<Typography>Loading Organization</Typography>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
value={adminTab}
|
||||||
|
indicatorColor="primary"
|
||||||
|
textColor="secondary"
|
||||||
|
style={{marginTop: 20, }}
|
||||||
|
onChange={(event, inputValue) => {
|
||||||
|
const newValue = parseInt(inputValue);
|
||||||
|
setAdminTab(newValue);
|
||||||
|
|
||||||
|
//const setConfig = (event, inputValue) => {
|
||||||
|
navigate(`/admin?admin_tab=${admin_views[newValue]}`);
|
||||||
|
}}
|
||||||
|
aria-label="disabled tabs example"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
label=<span>
|
||||||
|
Edit Details
|
||||||
|
</span>
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
label=<span>
|
||||||
|
Cloud Synchronization
|
||||||
|
</span>
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
disabled={!isCloud}
|
||||||
|
label=<span>
|
||||||
|
Billing
|
||||||
|
</span>
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
disabled={true}
|
||||||
|
label=<span>
|
||||||
|
Usage
|
||||||
|
</span>
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
disabled={true}
|
||||||
|
label=<span>
|
||||||
|
Notifications
|
||||||
|
</span>
|
||||||
|
/>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
<Divider
|
<Divider
|
||||||
style={{
|
style={{
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
@@ -2779,205 +2808,241 @@ const Admin = (props) => {
|
|||||||
backgroundColor: theme.palette.inputColor,
|
backgroundColor: theme.palette.inputColor,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
style={{ marginBottom: "10px", color: "white" }}
|
|
||||||
>
|
|
||||||
Cloud syncronization
|
|
||||||
</Typography>
|
|
||||||
What does{" "}
|
|
||||||
<a
|
|
||||||
href="https://shuffler.io/docs/organizations#cloud_sync"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
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.
|
|
||||||
{isCloud ? (
|
|
||||||
<div style={{ marginTop: 15, display: "flex" }}>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<Typography style={{}}>
|
|
||||||
Currently syncronizing:{" "}
|
|
||||||
{selectedOrganization.cloud_sync_active === true
|
|
||||||
? "True"
|
|
||||||
: "False"}
|
|
||||||
</Typography>
|
|
||||||
{selectedOrganization.cloud_sync_active ? (
|
|
||||||
<Typography style={{}}>
|
|
||||||
Syncronization interval:{" "}
|
|
||||||
{selectedOrganization.sync_config.interval === 0
|
|
||||||
? "60"
|
|
||||||
: selectedOrganization.sync_config.interval}
|
|
||||||
</Typography>
|
|
||||||
) : null}
|
|
||||||
<Typography
|
|
||||||
style={{
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
marginTop: 25,
|
|
||||||
marginRight: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Your Apikey
|
|
||||||
</Typography>
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
<TextField
|
|
||||||
color="primary"
|
|
||||||
style={{ backgroundColor: theme.palette.inputColor }}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
height: "50px",
|
|
||||||
color: "white",
|
|
||||||
fontSize: "1em",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
fullWidth={true}
|
|
||||||
disabled={true}
|
|
||||||
autoComplete="cloud apikey"
|
|
||||||
id="apikey_field"
|
|
||||||
margin="normal"
|
|
||||||
placeholder="Cloud Apikey"
|
|
||||||
variant="outlined"
|
|
||||||
defaultValue={userSettings.apikey}
|
|
||||||
/>
|
|
||||||
{selectedOrganization.cloud_sync_active ? (
|
|
||||||
<Button
|
|
||||||
style={{
|
|
||||||
width: 150,
|
|
||||||
height: 50,
|
|
||||||
marginLeft: 10,
|
|
||||||
marginTop: 17,
|
|
||||||
}}
|
|
||||||
variant={
|
|
||||||
selectedOrganization.cloud_sync_active === true
|
|
||||||
? "outlined"
|
|
||||||
: "contained"
|
|
||||||
}
|
|
||||||
color="primary"
|
|
||||||
onClick={() => {
|
|
||||||
handleStopOrgSync(selectedOrganization.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Stop Sync
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<div style={{ display: "flex", marginBottom: 20 }}>
|
|
||||||
<TextField
|
|
||||||
color="primary"
|
|
||||||
style={{
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
marginRight: 10,
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
height: "50px",
|
|
||||||
color: "white",
|
|
||||||
fontSize: "1em",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
fullWidth={true}
|
|
||||||
disabled={selectedOrganization.cloud_sync}
|
|
||||||
autoComplete="cloud apikey"
|
|
||||||
id="apikey_field"
|
|
||||||
margin="normal"
|
|
||||||
placeholder="Cloud Apikey"
|
|
||||||
variant="outlined"
|
|
||||||
onChange={(event) => {
|
|
||||||
setCloudSyncApikey(event.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
disabled={
|
|
||||||
(!selectedOrganization.cloud_sync &&
|
|
||||||
cloudSyncApikey.length === 0) ||
|
|
||||||
loading
|
|
||||||
}
|
|
||||||
style={{ marginTop: 15, height: 50, width: 150 }}
|
|
||||||
onClick={() => {
|
|
||||||
setLoading(true);
|
|
||||||
enableCloudSync(
|
|
||||||
cloudSyncApikey,
|
|
||||||
selectedOrganization,
|
|
||||||
selectedOrganization.cloud_sync
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
color="primary"
|
|
||||||
variant={
|
|
||||||
selectedOrganization.cloud_sync === true
|
|
||||||
? "outlined"
|
|
||||||
: "contained"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{selectedOrganization.cloud_sync
|
|
||||||
? "Stop sync"
|
|
||||||
: "Start sync"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{orgSyncResponse.length > 0 ? (
|
|
||||||
<Typography style={{ marginTop: 5, marginBottom: 10 }}>
|
|
||||||
Message from Shuffle Cloud: <b>{orgSyncResponse}</b>
|
|
||||||
</Typography>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Typography
|
|
||||||
style={{ marginTop: 40, marginLeft: 10, marginBottom: 5 }}
|
|
||||||
>
|
|
||||||
Cloud sync features (monthly usage)
|
|
||||||
</Typography>
|
|
||||||
<Grid container style={{ width: "100%", marginBottom: 15 }}>
|
|
||||||
{selectedOrganization.sync_features === undefined ||
|
|
||||||
selectedOrganization.sync_features === null
|
|
||||||
? null
|
|
||||||
: Object.keys(selectedOrganization.sync_features).map(function (
|
|
||||||
key,
|
|
||||||
index
|
|
||||||
) {
|
|
||||||
// unnecessary parts
|
|
||||||
if (key === "schedule" || key === "apps" || key === "updates") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const item = selectedOrganization.sync_features[key];
|
{adminTab === 0 ? (
|
||||||
if (item === null) {
|
<OrgHeaderexpanded
|
||||||
return null
|
isCloud={isCloud}
|
||||||
}
|
userdata={userdata}
|
||||||
|
setSelectedOrganization={setSelectedOrganization}
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
selectedOrganization={selectedOrganization}
|
||||||
|
adminTab={adminTab}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: adminTab === 1 ? (
|
||||||
|
<div>
|
||||||
|
<Typography
|
||||||
|
variant="h6"
|
||||||
|
style={{ marginBottom: "10px", color: "white" }}
|
||||||
|
>
|
||||||
|
Cloud syncronization
|
||||||
|
</Typography>
|
||||||
|
What does{" "}
|
||||||
|
<a
|
||||||
|
href="https://shuffler.io/docs/organizations#cloud_sync"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
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.
|
||||||
|
{isCloud ? (
|
||||||
|
<div style={{ marginTop: 15, display: "flex" }}>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<Typography style={{}}>
|
||||||
|
Currently syncronizing:{" "}
|
||||||
|
{selectedOrganization.cloud_sync_active === true
|
||||||
|
? "True"
|
||||||
|
: "False"}
|
||||||
|
</Typography>
|
||||||
|
{selectedOrganization.cloud_sync_active ? (
|
||||||
|
<Typography style={{}}>
|
||||||
|
Syncronization interval:{" "}
|
||||||
|
{selectedOrganization.sync_config.interval === 0
|
||||||
|
? "60"
|
||||||
|
: selectedOrganization.sync_config.interval}
|
||||||
|
</Typography>
|
||||||
|
) : null}
|
||||||
|
<Typography
|
||||||
|
style={{
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
marginTop: 25,
|
||||||
|
marginRight: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Your Apikey
|
||||||
|
</Typography>
|
||||||
|
<div style={{ display: "flex" }}>
|
||||||
|
<TextField
|
||||||
|
color="primary"
|
||||||
|
style={{ backgroundColor: theme.palette.inputColor }}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
height: "50px",
|
||||||
|
color: "white",
|
||||||
|
fontSize: "1em",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
fullWidth={true}
|
||||||
|
disabled={true}
|
||||||
|
autoComplete="cloud apikey"
|
||||||
|
id="apikey_field"
|
||||||
|
margin="normal"
|
||||||
|
placeholder="Cloud Apikey"
|
||||||
|
variant="outlined"
|
||||||
|
defaultValue={userSettings.apikey}
|
||||||
|
/>
|
||||||
|
{selectedOrganization.cloud_sync_active ? (
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
width: 150,
|
||||||
|
height: 50,
|
||||||
|
marginLeft: 10,
|
||||||
|
marginTop: 17,
|
||||||
|
}}
|
||||||
|
variant={
|
||||||
|
selectedOrganization.cloud_sync_active === true
|
||||||
|
? "outlined"
|
||||||
|
: "contained"
|
||||||
|
}
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
handleStopOrgSync(selectedOrganization.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Stop Sync
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<div style={{ display: "flex", marginBottom: 20 }}>
|
||||||
|
<TextField
|
||||||
|
color="primary"
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
marginRight: 10,
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
height: "50px",
|
||||||
|
color: "white",
|
||||||
|
fontSize: "1em",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
fullWidth={true}
|
||||||
|
disabled={selectedOrganization.cloud_sync}
|
||||||
|
autoComplete="cloud apikey"
|
||||||
|
id="apikey_field"
|
||||||
|
margin="normal"
|
||||||
|
placeholder="Cloud Apikey"
|
||||||
|
variant="outlined"
|
||||||
|
onChange={(event) => {
|
||||||
|
setCloudSyncApikey(event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
disabled={
|
||||||
|
(!selectedOrganization.cloud_sync &&
|
||||||
|
cloudSyncApikey.length === 0) ||
|
||||||
|
loading
|
||||||
|
}
|
||||||
|
style={{ marginTop: 15, height: 50, width: 150 }}
|
||||||
|
onClick={() => {
|
||||||
|
setLoading(true);
|
||||||
|
enableCloudSync(
|
||||||
|
cloudSyncApikey,
|
||||||
|
selectedOrganization,
|
||||||
|
selectedOrganization.cloud_sync
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
variant={
|
||||||
|
selectedOrganization.cloud_sync === true
|
||||||
|
? "outlined"
|
||||||
|
: "contained"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{selectedOrganization.cloud_sync
|
||||||
|
? "Stop sync"
|
||||||
|
: "Start sync"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{orgSyncResponse.length > 0 ? (
|
||||||
|
<Typography style={{ marginTop: 5, marginBottom: 10 }}>
|
||||||
|
Message from Shuffle Cloud: <b>{orgSyncResponse}</b>
|
||||||
|
</Typography>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Typography
|
||||||
|
style={{ marginTop: 40, marginLeft: 10, marginBottom: 5 }}
|
||||||
|
>
|
||||||
|
Cloud sync features (monthly usage)
|
||||||
|
</Typography>
|
||||||
|
<Grid container style={{ width: "100%", marginBottom: 15 }}>
|
||||||
|
{selectedOrganization.sync_features === undefined ||
|
||||||
|
selectedOrganization.sync_features === null
|
||||||
|
? null
|
||||||
|
: Object.keys(selectedOrganization.sync_features).map(function (
|
||||||
|
key,
|
||||||
|
index
|
||||||
|
) {
|
||||||
|
// unnecessary parts
|
||||||
|
if (key === "schedule" || key === "apps" || key === "updates") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const newkey = key.replaceAll("_", " ");
|
const item = selectedOrganization.sync_features[key];
|
||||||
const griditem = {
|
if (item === null) {
|
||||||
primary: newkey,
|
return null
|
||||||
secondary:
|
}
|
||||||
item.description === undefined ||
|
|
||||||
item.description === null ||
|
|
||||||
item.description.length === 0
|
|
||||||
? "Not defined yet"
|
|
||||||
: item.description,
|
|
||||||
limit: item.limit,
|
|
||||||
usage: item.usage === undefined ||
|
|
||||||
item.usage === null ? 0 : item.usage,
|
|
||||||
data_collection: "None",
|
|
||||||
active: item.active,
|
|
||||||
icon: <PolymerIcon style={{ color: itemColor }} />,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const newkey = key.replaceAll("_", " ");
|
||||||
<Zoom key={index}>
|
const griditem = {
|
||||||
<GridItem data={griditem} />
|
primary: newkey,
|
||||||
</Zoom>
|
secondary:
|
||||||
);
|
item.description === undefined ||
|
||||||
})}
|
item.description === null ||
|
||||||
</Grid>
|
item.description.length === 0
|
||||||
|
? "Not defined yet"
|
||||||
|
: item.description,
|
||||||
|
limit: item.limit,
|
||||||
|
usage: item.usage === undefined ||
|
||||||
|
item.usage === null ? 0 : item.usage,
|
||||||
|
data_collection: "None",
|
||||||
|
active: item.active,
|
||||||
|
icon: <PolymerIcon style={{ color: itemColor }} />,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Zoom key={index}>
|
||||||
|
<GridItem data={griditem} />
|
||||||
|
</Zoom>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: adminTab === 2 ?
|
||||||
|
<Billing
|
||||||
|
isCloud={isCloud}
|
||||||
|
userdata={userdata}
|
||||||
|
setSelectedOrganization={setSelectedOrganization}
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
selectedOrganization={selectedOrganization}
|
||||||
|
adminTab={adminTab}
|
||||||
|
billingInfo={billingInfo}
|
||||||
|
selectedOrganization={selectedOrganization}
|
||||||
|
stripeKey={props.stripeKey}
|
||||||
|
handleGetOrg={handleGetOrg}
|
||||||
|
/>
|
||||||
|
: adminTab === 3 ?
|
||||||
|
<div>
|
||||||
|
<Typography style={{ marginTop: 20, marginBottom: 10 }}>
|
||||||
|
Stats
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
<Divider
|
<Divider
|
||||||
style={{
|
style={{
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
@@ -2986,8 +3051,8 @@ const Admin = (props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{isCloud &&
|
{isCloud &&
|
||||||
selectedOrganization.partner_info !== undefined &&
|
selectedOrganization.partner_info !== undefined &&
|
||||||
selectedOrganization.partner_info.reseller === true ? (
|
selectedOrganization.partner_info.reseller === true ? (
|
||||||
<div style={{ marginTop: 30, marginBottom: 200 }}>
|
<div style={{ marginTop: 30, marginBottom: 200 }}>
|
||||||
<Typography
|
<Typography
|
||||||
style={{ marginTop: 40, marginLeft: 10, marginBottom: 5 }}
|
style={{ marginTop: 40, marginLeft: 10, marginBottom: 5 }}
|
||||||
@@ -3169,89 +3234,9 @@ const Admin = (props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{isCloud &&
|
|
||||||
selectedOrganization.subscriptions !== undefined &&
|
|
||||||
selectedOrganization.subscriptions !== null &&
|
|
||||||
selectedOrganization.subscriptions.length > 0 ? (
|
|
||||||
<div style={{ marginTop: 30, marginBottom: 20 }}>
|
|
||||||
<Typography
|
|
||||||
style={{ marginTop: 40, marginLeft: 10, marginBottom: 5 }}
|
|
||||||
>
|
|
||||||
Your subscription
|
|
||||||
{selectedOrganization.subscriptions.length > 1 ? "s" : ""}
|
|
||||||
</Typography>
|
|
||||||
<Grid container spacing={3} style={{ marginTop: 15 }}>
|
|
||||||
{selectedOrganization.subscriptions
|
|
||||||
.reverse()
|
|
||||||
.map((sub, index) => {
|
|
||||||
return (
|
|
||||||
<Grid item key={index} xs={4}>
|
|
||||||
<Card
|
|
||||||
elevation={6}
|
|
||||||
style={{
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
color: "white",
|
|
||||||
padding: 25,
|
|
||||||
textAlign: "left",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<b>Type</b>: {sub.level}
|
|
||||||
<div />
|
|
||||||
<b>Recurrence</b>: {sub.recurrence}
|
|
||||||
<div />
|
|
||||||
{sub.active ? (
|
|
||||||
<div>
|
|
||||||
<b>Started</b>:{" "}
|
|
||||||
{new Date(sub.startdate * 1000).toISOString()}
|
|
||||||
<div />
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
style={{ marginTop: 15 }}
|
|
||||||
onClick={() => {
|
|
||||||
cancelSubscriptions(sub.reference);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel subscription
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<b>Cancelled</b>:{" "}
|
|
||||||
{new Date(
|
|
||||||
sub.cancellationdate * 1000
|
|
||||||
).toISOString()}
|
|
||||||
<div />
|
|
||||||
<Typography color="textSecondary">
|
|
||||||
<b>Status</b>: Deactivated
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Grid>
|
|
||||||
<Divider
|
|
||||||
style={{
|
|
||||||
marginTop: 20,
|
|
||||||
backgroundColor: theme.palette.inputColor,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ backgroundColor: "#1f2023", paddingTop: 25 }}>
|
|
||||||
<HandlePaymentNew
|
|
||||||
theme={theme}
|
|
||||||
stripeKey={props.stripeKey}
|
|
||||||
userdata={userdata}
|
|
||||||
globalUrl={globalUrl}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
|||||||
@@ -6144,7 +6144,6 @@ const AngularWorkflow = (defaultprops) => {
|
|||||||
const [hover, setHover] = React.useState(false);
|
const [hover, setHover] = React.useState(false);
|
||||||
|
|
||||||
if (app.id === "" || app.name === "") {
|
if (app.id === "" || app.name === "") {
|
||||||
console.log("Skipping app: ", app)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+306
-17
@@ -26,6 +26,12 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Zoom,
|
Zoom,
|
||||||
|
InputAdornment,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemText,
|
||||||
|
Avatar,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -37,18 +43,24 @@ import {
|
|||||||
CloudDownload as CloudDownloadIcon,
|
CloudDownload as CloudDownloadIcon,
|
||||||
Edit as EditIcon,
|
Edit as EditIcon,
|
||||||
Delete as DeleteIcon,
|
Delete as DeleteIcon,
|
||||||
|
Search as SearchIcon,
|
||||||
|
Folder as FolderIcon,
|
||||||
|
LibraryBooks as LibraryBooksIcon,
|
||||||
} from "@material-ui/icons";
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ForkRight as ForkRightIcon,
|
ForkRight as ForkRightIcon,
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
|
import aa from 'search-insights'
|
||||||
import { useTheme } from "@material-ui/core/styles";
|
import { useTheme } from "@material-ui/core/styles";
|
||||||
|
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||||
|
import algoliasearch from 'algoliasearch/lite';
|
||||||
|
|
||||||
import YAML from "yaml";
|
import YAML from "yaml";
|
||||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
import Dropzone from "../components/Dropzone";
|
import Dropzone from "../components/Dropzone.jsx";
|
||||||
|
|
||||||
const surfaceColor = "#27292D";
|
const surfaceColor = "#27292D";
|
||||||
const inputColor = "#383B40";
|
const inputColor = "#383B40";
|
||||||
@@ -254,6 +266,7 @@ export const GetParsedPaths = (inputdata, basekey) => {
|
|||||||
return parsedValues;
|
return parsedValues;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||||
const Apps = (props) => {
|
const Apps = (props) => {
|
||||||
const { globalUrl, isLoggedIn, isLoaded, userdata } = props;
|
const { globalUrl, isLoggedIn, isLoaded, userdata } = props;
|
||||||
|
|
||||||
@@ -1487,6 +1500,268 @@ const Apps = (props) => {
|
|||||||
|
|
||||||
var appDelay = -75
|
var appDelay = -75
|
||||||
|
|
||||||
|
const leftBarSize = viewWidth
|
||||||
|
const SearchBox = ({ currentRefinement, refine, isSearchStalled, }) => {
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (document !== undefined) {
|
||||||
|
const appsearchValue = document.getElementById("app_search_field")
|
||||||
|
if (appsearchValue !== undefined && appsearchValue !== null) {
|
||||||
|
console.log("Value2: ", appsearchValue.value)
|
||||||
|
if (appsearchValue.value !== undefined && appsearchValue.value !== null && appsearchValue.value.length > 0) {
|
||||||
|
refine(appsearchValue.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form id="search_form" noValidate type="searchbox" action="" role="search" style={{ margin: 0, display: "none", }} onClick={() => {
|
||||||
|
}}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
style={{ backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, maxWidth: leftBarSize - 20, }}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
fontSize: "1em",
|
||||||
|
height: 50,
|
||||||
|
margin: 0,
|
||||||
|
},
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon style={{ marginLeft: 5 }} />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
autoComplete='off'
|
||||||
|
type="search"
|
||||||
|
color="primary"
|
||||||
|
placeholder="Find Public Apps, Workflows, Documentation and more"
|
||||||
|
value={currentRefinement}
|
||||||
|
id="shuffle_search_field"
|
||||||
|
onClick={(event) => {
|
||||||
|
console.log("Click!")
|
||||||
|
}}
|
||||||
|
onBlur={(event) => {
|
||||||
|
//setSearchOpen(false)
|
||||||
|
}}
|
||||||
|
onChange={(event) => {
|
||||||
|
//if (event.currentTarget.value.length > 0 && !searchOpen) {
|
||||||
|
// setSearchOpen(true)
|
||||||
|
//}
|
||||||
|
|
||||||
|
refine(event.currentTarget.value)
|
||||||
|
}}
|
||||||
|
limit={5}
|
||||||
|
/>
|
||||||
|
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const activateApp = (appid, refresh) => {
|
||||||
|
fetch(globalUrl + "/api/v1/apps/" + appid + "/activate", {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Failed to activate")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success === false) {
|
||||||
|
alert.error("Failed to activate the app")
|
||||||
|
} else {
|
||||||
|
alert.success("App activated for your organization! Refresh the page to use the app.")
|
||||||
|
|
||||||
|
if (refresh === true) {
|
||||||
|
getApps()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
//alert.error(error.toString())
|
||||||
|
console.log("Activate app error: ", error.toString())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const AppHits = ({ hits }) => {
|
||||||
|
const [mouseHoverIndex, setMouseHoverIndex] = React.useState(0)
|
||||||
|
|
||||||
|
//var tmp = searchOpen
|
||||||
|
//if (!searchOpen) {
|
||||||
|
// return null
|
||||||
|
//}
|
||||||
|
|
||||||
|
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||||
|
const outerlistitemStyle = {
|
||||||
|
width: "100%",
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.length > 4) {
|
||||||
|
hits = hits.slice(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = "app"
|
||||||
|
const baseImage = <LibraryBooksIcon />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: "relative", marginTop: 15, marginLeft: 0, marginRight: 10, position: "absolute", color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, minWidth: leftBarSize - 10, maxWidth: leftBarSize - 10, boxShadows: "none", overflowX: "hidden", }}>
|
||||||
|
<List style={{ backgroundColor: theme.palette.inputColor, }}>
|
||||||
|
{hits.length === 0 ?
|
||||||
|
<ListItem style={outerlistitemStyle}>
|
||||||
|
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||||
|
<Avatar>
|
||||||
|
<FolderIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={"No public apps found."}
|
||||||
|
secondary={"Try a broader search term"}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
:
|
||||||
|
hits.map((hit, index) => {
|
||||||
|
const innerlistitemStyle = {
|
||||||
|
width: positionInfo.width + 35,
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "hidden",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||||
|
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginLeft: 0,
|
||||||
|
marginRight: 0,
|
||||||
|
maxHeight: 75,
|
||||||
|
minHeight: 75,
|
||||||
|
maxWidth: 420,
|
||||||
|
minWidth: "100%",
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = hit.name === undefined ?
|
||||||
|
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
||||||
|
(hit.name.charAt(0).toUpperCase() + hit.name.slice(1)).replaceAll("_", " ")
|
||||||
|
|
||||||
|
var secondaryText = hit.data !== undefined ? hit.data.slice(0, 40) + "..." : ""
|
||||||
|
const avatar = hit.image_url === undefined ?
|
||||||
|
baseImage
|
||||||
|
:
|
||||||
|
<Avatar
|
||||||
|
src={hit.image_url}
|
||||||
|
variant="rounded"
|
||||||
|
/>
|
||||||
|
|
||||||
|
//console.log(hit)
|
||||||
|
if (hit.categories !== undefined && hit.categories !== null && hit.categories.length > 0) {
|
||||||
|
secondaryText = hit.categories.slice(0, 3).map((data, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
return ", " + data
|
||||||
|
|
||||||
|
/*
|
||||||
|
<Chip
|
||||||
|
key={index}
|
||||||
|
style={chipStyle}
|
||||||
|
label={data}
|
||||||
|
onClick={() => {
|
||||||
|
//handleChipClick
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}`
|
||||||
|
parsedUrl += `?queryID=${hit.__queryID}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
|
||||||
|
//if (!isCloud) {
|
||||||
|
// alert.info("Since this is an on-prem instance. You will need to activate the app yourself. Opening link to download it in a new window.")
|
||||||
|
// setTimeout(() => {
|
||||||
|
// event.preventDefault()
|
||||||
|
// window.open(parsedUrl, '_blank')
|
||||||
|
// }, 2000)
|
||||||
|
//} else {
|
||||||
|
alert.info(`Activating ${name}`)
|
||||||
|
//}
|
||||||
|
|
||||||
|
console.log("CLICK: ", hit)
|
||||||
|
|
||||||
|
const queryID = hit.__queryID
|
||||||
|
console.log("QUERY: ", queryID)
|
||||||
|
|
||||||
|
if (queryID !== undefined && queryID !== null) {
|
||||||
|
aa('init', {
|
||||||
|
appId: "JNSS5CFDZZ",
|
||||||
|
apiKey: "db08e40265e2941b9a7d8f644b6e5240",
|
||||||
|
})
|
||||||
|
|
||||||
|
const timestamp = new Date().getTime()
|
||||||
|
aa('sendEvents', [
|
||||||
|
{
|
||||||
|
eventType: 'conversion',
|
||||||
|
eventName: 'Public App Activated',
|
||||||
|
index: 'appsearch',
|
||||||
|
objectIDs: [hit.objectID],
|
||||||
|
timestamp: timestamp,
|
||||||
|
queryID: queryID,
|
||||||
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
console.log("No query to handle when activating")
|
||||||
|
}
|
||||||
|
|
||||||
|
activateApp(hit.objectID, true)
|
||||||
|
}}>
|
||||||
|
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||||
|
setMouseHoverIndex(index)
|
||||||
|
}}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
{avatar}
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={name}
|
||||||
|
secondary={secondaryText}
|
||||||
|
/>
|
||||||
|
{/*
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton edge="end" aria-label="delete">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
*/}
|
||||||
|
</ListItem>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const CustomSearchBox = connectSearchBox(SearchBox)
|
||||||
|
const CustomAppHits = connectHits(AppHits)
|
||||||
|
|
||||||
const appView = isLoggedIn ? (
|
const appView = isLoggedIn ? (
|
||||||
<Dropzone
|
<Dropzone
|
||||||
style={{ width: viewWidth * 2 + 20, margin: "auto", padding: 20 }}
|
style={{ width: viewWidth * 2 + 20, margin: "auto", padding: 20 }}
|
||||||
@@ -1643,28 +1918,42 @@ const Apps = (props) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Paper square style={uploadViewPaperStyle}>
|
<Paper square style={{
|
||||||
<Typography style={{ margin: 10 }}>
|
minWidth: viewWidth,
|
||||||
<span>
|
maxWidth: viewWidth,
|
||||||
<a
|
color: "white",
|
||||||
rel="noopener noreferrer"
|
borderRadius: 5,
|
||||||
href={"https://shuffler.io/search"}
|
//display: "flex",
|
||||||
style={{ textDecoration: "none", color: "#f85a3e" }}
|
marginBottom: 10,
|
||||||
target="_blank"
|
overflow: "hidden",
|
||||||
>
|
backgroundColor: theme.palette.platformColor,
|
||||||
Click here
|
border: null,
|
||||||
</a>{" "}
|
}}>
|
||||||
to search ALL apps, not just your activated ones.
|
|
||||||
</span>
|
|
||||||
</Typography>
|
|
||||||
<div />
|
|
||||||
|
|
||||||
{appSearchLoading ? (
|
{appSearchLoading ? (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
color="primary"
|
color="primary"
|
||||||
style={{ margin: "auto" }}
|
style={{ margin: "auto" }}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{ textAlign: "center", width: leftBarSize, marginTop: 10 }}
|
||||||
|
onLoad={() => {
|
||||||
|
console.log("Should load in extra apps?")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body1" color="textSecondary">
|
||||||
|
Couldn't find the app you're looking for? Searching unactivated apps. Click one of the below apps to Activate it for your organization.
|
||||||
|
</Typography>
|
||||||
|
<InstantSearch searchClient={searchClient} indexName="appsearch" onClick={() => {
|
||||||
|
console.log("CLICKED")
|
||||||
|
}}>
|
||||||
|
<CustomSearchBox />
|
||||||
|
<Index indexName="appsearch">
|
||||||
|
<CustomAppHits />
|
||||||
|
</Index>
|
||||||
|
</InstantSearch>
|
||||||
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
)
|
||||||
) : isLoading ? (
|
) : isLoading ? (
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
|||||||
|
|
||||||
//import JSONPretty from 'react-json-pretty';
|
//import JSONPretty from 'react-json-pretty';
|
||||||
//import JSONPrettyMon from 'react-json-pretty/dist/monikai'
|
//import JSONPrettyMon from 'react-json-pretty/dist/monikai'
|
||||||
import Dropzone from "../components/Dropzone";
|
import Dropzone from "../components/Dropzone.jsx";
|
||||||
|
|
||||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
import { useNavigate, Link } from "react-router-dom";
|
import { useNavigate, Link } from "react-router-dom";
|
||||||
|
import Drift from "react-driftjs";
|
||||||
|
|
||||||
const Welcome = (props) => {
|
const Welcome = (props) => {
|
||||||
const { globalUrl, surfaceColor, newColor, mini, inputColor, userdata, isLoggedIn, isLoaded, serverside } = props;
|
const { globalUrl, surfaceColor, newColor, mini, inputColor, userdata, isLoggedIn, isLoaded, serverside } = props;
|
||||||
@@ -25,7 +26,7 @@ const Welcome = (props) => {
|
|||||||
const [inputUsecase, setInputUsecase] = useState({});
|
const [inputUsecase, setInputUsecase] = useState({});
|
||||||
const [frameworkData, setFrameworkData] = useState(undefined);
|
const [frameworkData, setFrameworkData] = useState(undefined);
|
||||||
const [discoveryWrapper, setDiscoveryWrapper] = useState(undefined);
|
const [discoveryWrapper, setDiscoveryWrapper] = useState(undefined);
|
||||||
const [activeStep, setActiveStep] = React.useState(0);
|
const [activeStep, setActiveStep] = React.useState(1);
|
||||||
const [apps, setApps] = React.useState([]);
|
const [apps, setApps] = React.useState([]);
|
||||||
const [defaultSearch, setDefaultSearch] = React.useState("")
|
const [defaultSearch, setDefaultSearch] = React.useState("")
|
||||||
const [selectionOpen, setSelectionOpen] = React.useState(false)
|
const [selectionOpen, setSelectionOpen] = React.useState(false)
|
||||||
@@ -281,7 +282,8 @@ const Welcome = (props) => {
|
|||||||
|
|
||||||
setActiveStep(foundTab-1)
|
setActiveStep(foundTab-1)
|
||||||
} else {
|
} else {
|
||||||
navigate(`/welcome?tab=1`)
|
//navigate(`/welcome?tab=1`)
|
||||||
|
navigate(`/welcome?tab=2`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
@@ -426,29 +428,31 @@ const Welcome = (props) => {
|
|||||||
Who do you identify with the most?
|
Who do you identify with the most?
|
||||||
</Typography>
|
</Typography>
|
||||||
<div style={{display: "flex", marginTop: 70, width: 700, margin: "auto",}}>
|
<div style={{display: "flex", marginTop: 70, width: 700, margin: "auto",}}>
|
||||||
<Card style={paperObject} onClick={() => {
|
<div style={{border: "1px solid #49A928",}}>
|
||||||
if (isCloud) {
|
<Card style={paperObject} onClick={() => {
|
||||||
ReactGA.event({
|
if (isCloud) {
|
||||||
category: "welcome",
|
ReactGA.event({
|
||||||
action: "click_welcome_continue",
|
category: "welcome",
|
||||||
label: "",
|
action: "click_welcome_continue",
|
||||||
})
|
label: "",
|
||||||
} else {
|
})
|
||||||
//setActiveStep(1)
|
} else {
|
||||||
}
|
//setActiveStep(1)
|
||||||
|
}
|
||||||
|
|
||||||
setShowWelcome(true)
|
setShowWelcome(true)
|
||||||
}}>
|
}}>
|
||||||
<CardActionArea style={actionObject}>
|
<CardActionArea style={actionObject}>
|
||||||
<Typography variant="h4" style={{color: "#49A928"}}>
|
<Typography variant="h4" style={{color: "#49A928"}}>
|
||||||
New to Shuffle
|
New to Shuffle
|
||||||
</Typography>
|
</Typography>
|
||||||
<img src="/images/welcome_cog.png" style={imageStyle} />
|
<img src="/images/welcome_cog.png" style={imageStyle} />
|
||||||
<Typography variant="body1" style={{marginTop: 30, color: "rgba(255,255,255,0.8)"}}>
|
<Typography variant="body1" style={{marginTop: 30, color: "rgba(255,255,255,0.8)"}}>
|
||||||
Follow our short introduction and learn some tips and tricks
|
Follow our short introduction and learn some tips and tricks
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
</Card>
|
</Card>
|
||||||
|
</div>
|
||||||
<div style={{marginLeft: 25, marginRight: 25, }}>
|
<div style={{marginLeft: 25, marginRight: 25, }}>
|
||||||
<Typography style={{marginTop: 200, }}>
|
<Typography style={{marginTop: 200, }}>
|
||||||
OR
|
OR
|
||||||
@@ -476,6 +480,19 @@ const Welcome = (props) => {
|
|||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
{/*
|
||||||
|
<div style={{margin: "auto", border: "1px solid rgba(255,255,255,0.5)", borderRadius: theme.palette.borderRadius, marginTop: 50, width: 200, overflow: "wrap", padding: 25, cursor: "pointer", }} onClick={() => {
|
||||||
|
if (window.drift !== undefined) {
|
||||||
|
window.drift.api.startInteraction({ interactionId: 341911 })
|
||||||
|
} else {
|
||||||
|
console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<Typography variant="body1" style={{margin: "auto", textAlign: "center"}}>
|
||||||
|
Want a free Proof of Value with our support team?
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
*/}
|
||||||
</div>
|
</div>
|
||||||
</Fade>
|
</Fade>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
|||||||
|
|
||||||
//import JSONPretty from 'react-json-pretty';
|
//import JSONPretty from 'react-json-pretty';
|
||||||
//import JSONPrettyMon from 'react-json-pretty/dist/monikai'
|
//import JSONPrettyMon from 'react-json-pretty/dist/monikai'
|
||||||
import Dropzone from "../components/Dropzone";
|
import Dropzone from "../components/Dropzone.jsx";
|
||||||
|
|
||||||
import { useNavigate, Link } from "react-router-dom";
|
import { useNavigate, Link } from "react-router-dom";
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
@@ -1262,8 +1262,9 @@ const Workflows = (props) => {
|
|||||||
|
|
||||||
const exportAllWorkflows = (allWorkflows) => {
|
const exportAllWorkflows = (allWorkflows) => {
|
||||||
for (var i = 0; i < allWorkflows.length; i++) {
|
for (var i = 0; i < allWorkflows.length; i++) {
|
||||||
|
console.log(allWorkflows[i])
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log(allWorkflows[i].name)
|
console.log(allWorkflows[i])
|
||||||
exportWorkflow(allWorkflows[i], false)
|
exportWorkflow(allWorkflows[i], false)
|
||||||
}, i * 200);
|
}, i * 200);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user