Added further admin panel fixes
This commit is contained in:
@@ -749,7 +749,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
image: image,
|
||||
defaults: defaults,
|
||||
sso_config: sso_config,
|
||||
lead_info: lead_info,
|
||||
lead_info: lead_info,
|
||||
};
|
||||
|
||||
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
|
||||
@@ -2371,7 +2371,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
*/}
|
||||
|
||||
{userdata.support === true ?
|
||||
<span style={{display: "flex", top: -10, right: 50, position: "absolute"}}>
|
||||
<span style={{display: "flex", top: -10, right: -50, position: "absolute"}}>
|
||||
{/*<a href={mailsendingButton(selectedOrganization)} target="_blank" rel="noopener noreferrer" style={{textDecoration: "none"}} disabled={selectedStatus.length !== 0}>*/}
|
||||
<Button
|
||||
variant="outlined"
|
||||
@@ -2468,8 +2468,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
setSelectedOrganization={setSelectedOrganization}
|
||||
globalUrl={globalUrl}
|
||||
selectedOrganization={selectedOrganization}
|
||||
adminTab={adminTab}
|
||||
handleEditOrg={handleEditOrg}
|
||||
adminTab={adminTab}
|
||||
handleEditOrg={handleEditOrg}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@@ -2517,13 +2517,13 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
<Tab
|
||||
disabled={!isCloud}
|
||||
label=<span>
|
||||
Billing (Beta)
|
||||
Billing
|
||||
</span>
|
||||
/>
|
||||
<Tab
|
||||
disabled={!isCloud || true}
|
||||
disabled={!isCloud}
|
||||
label=<span>
|
||||
Branding
|
||||
Branding (Beta)
|
||||
</span>
|
||||
/>
|
||||
</Tabs>
|
||||
@@ -2798,7 +2798,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
globalUrl={globalUrl}
|
||||
handleGetOrg={handleGetOrg}
|
||||
selectedOrganization={selectedOrganization}
|
||||
selectedOrganization={selectedOrganization}
|
||||
setSelectedOrganization={setSelectedOrganization}
|
||||
/>
|
||||
: null
|
||||
@@ -2995,7 +2994,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
Add, edit, block or change passwords.{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="noopener noreferrer"
|
||||
href="/docs/organizations#user_management"
|
||||
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
>
|
||||
|
||||
@@ -22,9 +22,11 @@ import {
|
||||
Breadcrumbs,
|
||||
CircularProgress,
|
||||
Chip,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Publish as PublishIcon,
|
||||
LockOpen as LockOpenIcon,
|
||||
FileCopy as FileCopyIcon,
|
||||
Delete as DeleteIcon,
|
||||
@@ -40,10 +42,11 @@ import {
|
||||
ZoomOutOutlined as ZoomOutOutlinedIcon,
|
||||
Loop as LoopIcon,
|
||||
AddPhotoAlternate as AddPhotoAlternateIcon,
|
||||
CallMerge as CallMergeIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import YAML from "yaml";
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
//import { useAlert
|
||||
@@ -360,6 +363,8 @@ const AppCreator = (defaultprops) => {
|
||||
props.match.params = params
|
||||
|
||||
var upload = "";
|
||||
let navigate = useNavigate();
|
||||
|
||||
const increaseAmount = 50;
|
||||
const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"];
|
||||
const actionBodyRequest = ["POST", "PUT", "PATCH"];
|
||||
@@ -422,6 +427,109 @@ const AppCreator = (defaultprops) => {
|
||||
// and make categories + labels modifyable.
|
||||
// Categories are the main categories in the App Framework
|
||||
const [categories, setCategories] = useState(appCategories)
|
||||
|
||||
|
||||
|
||||
const redirectOpenApi = () => {
|
||||
navigate(`/apps/new?id=${appValidation}`)
|
||||
}
|
||||
|
||||
const newUpload = React.useRef(null);
|
||||
const [openApiError, setOpenApiError] = React.useState("");
|
||||
const [validation, setValidation] = React.useState("");
|
||||
const [appValidation, setAppValidation] = React.useState("");
|
||||
const [openApi, setOpenApi] = React.useState("");
|
||||
const [openApiData, setOpenApiData] = React.useState("");
|
||||
const [openApiModal, setOpenApiModal] = React.useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("In useEffect for openApiData: ", openApiData)
|
||||
}, [openApiData]);
|
||||
|
||||
const uploadFile = (e) => {
|
||||
console.log("In uploadFile")
|
||||
const isDropzone =
|
||||
e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
|
||||
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
||||
const reader = new FileReader();
|
||||
|
||||
try {
|
||||
reader.addEventListener("load", (e) => {
|
||||
const content = e.target.result;
|
||||
console.log("set openapi data! ", content)
|
||||
setOpenApiData(content);
|
||||
setOpenApiModal(true);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Error in dropzone: ", e);
|
||||
}
|
||||
|
||||
try {
|
||||
reader.readAsText(files[0]);
|
||||
} catch (error) {
|
||||
toast("Failed to read file");
|
||||
}
|
||||
}
|
||||
|
||||
const escapeApiData = (apidata) => {
|
||||
//console.log(apidata)
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(apidata));
|
||||
} catch (error) {
|
||||
console.log("JSON DECODE ERROR - TRY YAML");
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = YAML.parse(YAML.stringify(apidata));
|
||||
//const parsed = YAML.parse(apidata))
|
||||
return YAML.stringify(parsed);
|
||||
} catch (error) {
|
||||
console.log("YAML DECODE ERROR - TRY SOMETHING ELSE?: " + error);
|
||||
setOpenApiError("Local error: " + error.toString());
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
const validateOpenApi = (openApidata) => {
|
||||
var newApidata = escapeApiData(openApidata);
|
||||
if (newApidata === "") {
|
||||
// Used to return here
|
||||
newApidata = openApidata;
|
||||
return;
|
||||
}
|
||||
|
||||
//console.log(newApidata)
|
||||
|
||||
setValidation(true);
|
||||
fetch(globalUrl + "/api/v1/validate_openapi", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: openApidata,
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
setValidation(false);
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success) {
|
||||
setAppValidation(responseJson.id);
|
||||
} else {
|
||||
if (responseJson.reason !== undefined) {
|
||||
setOpenApiError(responseJson.reason);
|
||||
}
|
||||
toast("An error occurred in the response");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
setValidation(false);
|
||||
toast(error.toString());
|
||||
setOpenApiError(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
@@ -2084,9 +2192,9 @@ const AppCreator = (defaultprops) => {
|
||||
},
|
||||
};
|
||||
|
||||
if (queryitem.example !== undefined) {
|
||||
newitem.example = queryitem.example
|
||||
}
|
||||
if (queryitem.example !== undefined) {
|
||||
newitem.example = queryitem.example
|
||||
}
|
||||
|
||||
if (queryitem.description !== undefined) {
|
||||
newitem.description = queryitem.description;
|
||||
@@ -2936,18 +3044,6 @@ const AppCreator = (defaultprops) => {
|
||||
setOauth2Scopes(chips)
|
||||
setUpdate(Math.random())
|
||||
}}
|
||||
onAdd={(chip) => {
|
||||
oauth2Scopes.push(chip);
|
||||
console.log(oauth2Scopes);
|
||||
setOauth2Scopes(oauth2Scopes);
|
||||
setUpdate(Math.random());
|
||||
}}
|
||||
onDelete={(chip, index) => {
|
||||
oauth2Scopes.splice(index, 1);
|
||||
console.log(oauth2Scopes);
|
||||
setOauth2Scopes(oauth2Scopes);
|
||||
setUpdate(Math.random());
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
@@ -4408,16 +4504,7 @@ const AppCreator = (defaultprops) => {
|
||||
setNewWorkflowTags(chips)
|
||||
setUpdate("added "+chips)
|
||||
}}
|
||||
onAdd={(chip) => {
|
||||
newWorkflowTags.push(chip);
|
||||
setNewWorkflowTags(newWorkflowTags);
|
||||
setUpdate("added" + chip);
|
||||
}}
|
||||
onDelete={(chip, index) => {
|
||||
newWorkflowTags.splice(index, 1);
|
||||
setNewWorkflowTags(newWorkflowTags);
|
||||
setUpdate("delete " + chip);
|
||||
}}
|
||||
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -5360,6 +5447,167 @@ const AppCreator = (defaultprops) => {
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
const validateRemote = () => {
|
||||
setValidation(true);
|
||||
|
||||
fetch(globalUrl + "/api/v1/get_openapi_uri", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(openApi),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
setValidation(false);
|
||||
if (response.status !== 200) {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
return response.text();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (typeof responseJson !== "string" && !responseJson.success) {
|
||||
console.log(responseJson.reason);
|
||||
if (responseJson.reason !== undefined) {
|
||||
setOpenApiError(responseJson.reason);
|
||||
} else {
|
||||
setOpenApiError("Undefined issue with OpenAPI validation");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Validating response!");
|
||||
validateOpenApi(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
toast(error.toString());
|
||||
setOpenApiError(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const circularLoader = validation ? (
|
||||
<CircularProgress color="primary" />
|
||||
) : null;
|
||||
|
||||
const newApimodalView = openApiModal ?
|
||||
<Dialog
|
||||
open={openApiModal}
|
||||
onClose={() => {
|
||||
setOpenApiModal(false)
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
||||
Merge with another OpenAPI document. You will get to choose Actions before they are merged.
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
Paste in the URI for the OpenAPI
|
||||
<TextField
|
||||
style={{ backgroundColor: inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
endAdornment: (
|
||||
<Button
|
||||
style={{
|
||||
borderRadius: "0px",
|
||||
marginTop: "0px",
|
||||
height: "50px",
|
||||
}}
|
||||
variant="contained"
|
||||
disabled={openApi.length === 0 || appValidation.length > 0}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setOpenApiError("");
|
||||
validateRemote();
|
||||
}}
|
||||
>
|
||||
Validate
|
||||
</Button>
|
||||
),
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setOpenApi(e.target.value);
|
||||
}}
|
||||
helperText={
|
||||
<span style={{ color: "white", marginBottom: "2px" }}>
|
||||
Must point to a version 2 or 3 OpenAPI specification.
|
||||
</span>
|
||||
}
|
||||
placeholder="OpenAPI URI"
|
||||
fullWidth
|
||||
/>
|
||||
{/*
|
||||
<div style={{marginTop: "15px"}}/>
|
||||
Example:
|
||||
<div />
|
||||
https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/uber.json
|
||||
*/}
|
||||
<p>Or upload a YAML/JSON specification</p>
|
||||
<input
|
||||
hidden
|
||||
type="file"
|
||||
ref={newUpload}
|
||||
accept="application/JSON,application/YAML,application/yaml,text/yaml,text/x-yaml,application/x-yaml,application/vnd.yaml,.yml,.yaml"
|
||||
multiple={false}
|
||||
onChange={uploadFile}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => newUpload.current.click()}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
{errorText}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{circularLoader}
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => {
|
||||
setOpenApiModal(false);
|
||||
setAppValidation("");
|
||||
setOpenApiError("");
|
||||
setOpenApi("");
|
||||
setOpenApiData("");
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "0px" }}
|
||||
disabled={appValidation.length === 0}
|
||||
onClick={() => {
|
||||
redirectOpenApi();
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</FormControl>
|
||||
</Dialog>
|
||||
: null
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageDataBrowser = (
|
||||
<div style={{ paddingBottom: 100, color: "white" }}>
|
||||
@@ -5391,9 +5639,30 @@ const AppCreator = (defaultprops) => {
|
||||
onChange={editHeaderImage}
|
||||
/>
|
||||
<Paper style={boxStyle}>
|
||||
<h2 style={{ marginBottom: "10px", color: "white" }}>
|
||||
General information
|
||||
</h2>
|
||||
<div style={{display: "flex", }}>
|
||||
<div style={{flex: 1, }}>
|
||||
<h2 style={{ marginBottom: "10px", color: "white" }}>
|
||||
General information
|
||||
</h2>
|
||||
</div>
|
||||
<div style={{flex: 1, itemAlign: "right", textAlign: "right",}}>
|
||||
<Tooltip title="Merge with another API (coming soon)" placement="bottom">
|
||||
<IconButton
|
||||
disabled
|
||||
onClick={() => {
|
||||
setOpenApiModal(true)
|
||||
}}
|
||||
>
|
||||
<CallMergeIcon
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
setOpenApiModal(true)
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://shuffler.io/docs/app_creation#app-creator-instructions"
|
||||
@@ -5706,6 +5975,7 @@ const AppCreator = (defaultprops) => {
|
||||
isLoaded && isAppLoaded ? (
|
||||
<div>
|
||||
<div style={bodyDivStyle}>{landingpageDataBrowser}</div>
|
||||
{newApimodalView}
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
|
||||
@@ -1354,7 +1354,7 @@ const Apps = (props) => {
|
||||
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
|
||||
cursor: hover ? "pointer" : "default",
|
||||
textAlign: "center",
|
||||
height: 150,
|
||||
height: 125,
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
@@ -2556,6 +2556,7 @@ const Apps = (props) => {
|
||||
const circularLoader = validation ? (
|
||||
<CircularProgress color="primary" />
|
||||
) : null;
|
||||
|
||||
const appsModalLoad = loadAppsModalOpen ? (
|
||||
<Dialog
|
||||
open={loadAppsModalOpen}
|
||||
|
||||
@@ -730,28 +730,28 @@ const Settings = (props) => {
|
||||
</Button>
|
||||
<h3>{passwordFormMessage}</h3>
|
||||
<Divider style={{ marginTop: "40px" }} />
|
||||
<h2>Platform Earnings</h2>
|
||||
<h2>Creator Incentive Program</h2>
|
||||
<div style={{ display: runFlex ? "flex" : "", width: "100%" }}>
|
||||
<div>
|
||||
{isCloud ?
|
||||
<span>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
By connecting your Github account, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. This enables you to earn a passive income from Shuffle. This IS reversible. Support: support@shuffler.io
|
||||
</Typography>
|
||||
<Button
|
||||
style={{ height: 40, marginTop: 10 }}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
onClick={() => {
|
||||
handleGithubConnection();
|
||||
}}
|
||||
>
|
||||
Connect to Github
|
||||
</Button>
|
||||
</span>
|
||||
: null}
|
||||
</div>
|
||||
<div>
|
||||
{isCloud ?
|
||||
<span>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
By <a href="/creators" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>joining the Creator Incentive Program</a> and connecting your Github account, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. This enables you to earn a passive income from Shuffle. This IS reversible. Support: support@shuffler.io
|
||||
</Typography>
|
||||
<Button
|
||||
style={{ height: 40, marginTop: 10 }}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
onClick={() => {
|
||||
handleGithubConnection();
|
||||
}}
|
||||
>
|
||||
Connect to Github
|
||||
</Button>
|
||||
</span>
|
||||
: null}
|
||||
</div>
|
||||
<div style={{ flex: 1, display: "flex" }}>
|
||||
<div>
|
||||
{userdata.eth_info !== undefined &&
|
||||
|
||||
@@ -638,7 +638,7 @@ const Workflows = (props) => {
|
||||
window.location.host === "shuffler.io";
|
||||
|
||||
const findWorkflow = (filters) => {
|
||||
console.log("Using filters: ", filters)
|
||||
console.log("Using filters: ", filters)
|
||||
if (filters.length === 0) {
|
||||
setFilteredWorkflows(workflows);
|
||||
handleKeysetting(allUsecases, workflows)
|
||||
@@ -656,6 +656,12 @@ const Workflows = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.length > 0) {
|
||||
// Make them all lowercase
|
||||
curWorkflow.tags = curWorkflow.tags.map((tag) => tag.toLowerCase())
|
||||
}
|
||||
|
||||
|
||||
if (found.every((v) => v !== true)) {
|
||||
found = filters.map((filter) => {
|
||||
if (filter === undefined || filter === null) {
|
||||
@@ -666,7 +672,7 @@ const Workflows = (props) => {
|
||||
|
||||
if (curWorkflow.name.toLowerCase().includes(filter.toLowerCase())) {
|
||||
return true;
|
||||
} else if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.includes(filter)) {
|
||||
} else if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.includes(filter.toLowerCase())) {
|
||||
return true;
|
||||
} else if (curWorkflow.owner === filter) {
|
||||
return true;
|
||||
@@ -674,17 +680,17 @@ const Workflows = (props) => {
|
||||
return true;
|
||||
} else if (curWorkflow.usecase_ids !== undefined && curWorkflow.usecase_ids !== null && curWorkflow.usecase_ids.length > 0) {
|
||||
// Check if the usecase is the right category
|
||||
for (var key in usecases) {
|
||||
if (usecases[key].name.toLowerCase() !== newfilter) {
|
||||
continue
|
||||
}
|
||||
for (var key in usecases) {
|
||||
if (usecases[key].name.toLowerCase() !== newfilter) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (var subkey in usecases[key].list) {
|
||||
if (curWorkflow.usecase_ids.includes(usecases[key].list[subkey].name)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for (var subkey in usecases[key].list) {
|
||||
if (curWorkflow.usecase_ids.includes(usecases[key].list[subkey].name)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
curWorkflow.actions !== null &&
|
||||
curWorkflow.actions !== undefined
|
||||
|
||||
Reference in New Issue
Block a user