Fixed debouncecallback ref
This commit is contained in:
@@ -365,6 +365,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
|
|||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/shuffle/shuffle-shared v0.9.30 h1:3CYvNyD7sTxdxoZjTVrtaDqFvSWQRKAFGaga6rPGf8A=
|
github.com/shuffle/shuffle-shared v0.9.30 h1:3CYvNyD7sTxdxoZjTVrtaDqFvSWQRKAFGaga6rPGf8A=
|
||||||
github.com/shuffle/shuffle-shared v0.9.30/go.mod h1:PhDEizuz4SmJaSmy0+yrFWwD1mXVUsy8/knKlrqF1qw=
|
github.com/shuffle/shuffle-shared v0.9.30/go.mod h1:PhDEizuz4SmJaSmy0+yrFWwD1mXVUsy8/knKlrqF1qw=
|
||||||
|
github.com/shuffle/shuffle-shared v0.9.31 h1:BMoDO4Sgz4+I12aHhc3cWHiCuAQ827XIxajPqCJ9cXA=
|
||||||
|
github.com/shuffle/shuffle-shared v0.9.31/go.mod h1:vfI2QDGphZGrcwuUPQ1yI/Hgc8aseFro5+2k36irfkQ=
|
||||||
github.com/shuffle/singul v0.0.16 h1:dW+0Mln9R1aUJ0fjikpWcxbjoQWqJHxe4kSxh2tQN5E=
|
github.com/shuffle/singul v0.0.16 h1:dW+0Mln9R1aUJ0fjikpWcxbjoQWqJHxe4kSxh2tQN5E=
|
||||||
github.com/shuffle/singul v0.0.16/go.mod h1:LYkp320A6gsoPlYbXUM+WvEPUVAuutlSsqnVKyRy4gs=
|
github.com/shuffle/singul v0.0.16/go.mod h1:LYkp320A6gsoPlYbXUM+WvEPUVAuutlSsqnVKyRy4gs=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
|||||||
@@ -0,0 +1,891 @@
|
|||||||
|
import React, { useState, useEffect, useContext, memo } from "react";
|
||||||
|
import { CodeHandler, Img, OuterLink, } from '../views/Docs.jsx'
|
||||||
|
import { getTheme } from "../theme.jsx";
|
||||||
|
import { isMobile } from "react-device-detect"
|
||||||
|
import Markdown from "react-markdown";
|
||||||
|
import { Context } from '../context/ContextApi.jsx';
|
||||||
|
import PaperComponent from "../components/PaperComponent.jsx";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { v4 as uuidv4} from "uuid";
|
||||||
|
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Edit as EditIcon,
|
||||||
|
DragIndicator as DragIndicatorIcon,
|
||||||
|
Close as CloseIcon,
|
||||||
|
LockOpen as LockOpenIcon,
|
||||||
|
} from "@mui/icons-material";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
DialogActions,
|
||||||
|
MenuItem,
|
||||||
|
Select,
|
||||||
|
TextField,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
Divider,
|
||||||
|
} from "@mui/material";
|
||||||
|
|
||||||
|
const AuthenticationModal = (props) => {
|
||||||
|
const {
|
||||||
|
globalUrl,
|
||||||
|
userdata,
|
||||||
|
|
||||||
|
selectedAppData,
|
||||||
|
getAppAuthentication,
|
||||||
|
appAuthentication,
|
||||||
|
setSelectedAction,
|
||||||
|
|
||||||
|
selectedMeta,
|
||||||
|
setSelectedMeta,
|
||||||
|
|
||||||
|
setAppAuthentication,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io";
|
||||||
|
const [selectedAuthentication, setSelectedAuthentication] = React.useState({});
|
||||||
|
const [authenticationModalOpen, setAuthenticationModalOpen] = React.useState(false);
|
||||||
|
const [authenticationType, setAuthenticationType] = React.useState({})
|
||||||
|
|
||||||
|
const [appid, setAppId] = useState("")
|
||||||
|
|
||||||
|
//const [appAuthentication, setAppAuthentication] = useState([]);
|
||||||
|
|
||||||
|
const { themeMode, supportEmail, brandColor } = useContext(Context)
|
||||||
|
const theme = getTheme(themeMode, brandColor)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedAppData === undefined || selectedAppData === null || Object.getOwnPropertyNames(selectedAppData).length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedAppData.authentication === undefined || selectedAppData.authentication === null) {
|
||||||
|
setAuthenticationType({
|
||||||
|
type: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
selectedAppData.authentication = {
|
||||||
|
type: "",
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setAuthenticationType(
|
||||||
|
selectedAppData.authentication.type === "oauth2-app" || (selectedAppData.authentication.type === "oauth2" && selectedAppData.authentication.redirect_uri !== undefined && selectedAppData.authentication.redirect_uri !== null) ? {
|
||||||
|
type: selectedAppData.authentication.type,
|
||||||
|
redirect_uri: selectedAppData.authentication.redirect_uri,
|
||||||
|
refresh_uri: selectedAppData.authentication.refresh_uri,
|
||||||
|
token_uri: selectedAppData.authentication.token_uri,
|
||||||
|
scope: selectedAppData.authentication.scope,
|
||||||
|
client_id: selectedAppData.authentication.client_id,
|
||||||
|
client_secret: selectedAppData.authentication.client_secret,
|
||||||
|
grant_type: selectedAppData.authentication.grant_type,
|
||||||
|
} : {
|
||||||
|
type: "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [selectedAppData])
|
||||||
|
|
||||||
|
if (selectedAppData === undefined || selectedAppData === null || Object.getOwnPropertyNames(selectedAppData).length === 0) {
|
||||||
|
console.log("No app data for authentication modal");
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authenticationModalOpen === false) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="contained"
|
||||||
|
style={{
|
||||||
|
marginBottom: 20,
|
||||||
|
marginTop: 20,
|
||||||
|
flex: 1,
|
||||||
|
textTransform: "none",
|
||||||
|
textAlign: "left",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
backgroundColor: "#ffffff",
|
||||||
|
color: "#2f2f2f",
|
||||||
|
borderRadius: theme.palette?.borderRadius,
|
||||||
|
minWidth: 275,
|
||||||
|
maxWidth: 275,
|
||||||
|
maxHeight: 50,
|
||||||
|
overflow: "hidden",
|
||||||
|
border: `1px solid ${theme.palette.inputColor}`,
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
fullWidth
|
||||||
|
onClick={() => {
|
||||||
|
setAppId(selectedAppData?.id)
|
||||||
|
setAuthenticationModalOpen(true)
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<span style={{display: "flex"}}>
|
||||||
|
<img
|
||||||
|
alt={selectedAppData?.name}
|
||||||
|
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
|
||||||
|
src={selectedAppData?.large_image}
|
||||||
|
/>
|
||||||
|
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 8, color: "#2f2f2f",}} variant="body1">
|
||||||
|
Authenticate {selectedAppData?.name?.replaceAll("_", " ", -1)}
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Heading(props) {
|
||||||
|
const element = React.createElement(
|
||||||
|
`h${props.level}`,
|
||||||
|
{ style: { marginTop: 40 } },
|
||||||
|
props.children
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Typography>
|
||||||
|
{props.level !== 1 ? (
|
||||||
|
<Divider
|
||||||
|
style={{
|
||||||
|
width: "90%",
|
||||||
|
marginTop: 40,
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{element}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateAppAuthentication = (data) => {
|
||||||
|
if (data === undefined || data === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredData = data.filter((appAuth) => appAuth?.app?.id === appid);
|
||||||
|
if (filteredData.length === 0) {
|
||||||
|
setAppAuthentication([]);
|
||||||
|
setSelectedAuthentication({});
|
||||||
|
} else {
|
||||||
|
setAppAuthentication(filteredData);
|
||||||
|
setSelectedAuthentication(filteredData[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const HandleAppAuthentication = () => {
|
||||||
|
|
||||||
|
const url = `${globalUrl}/api/v1/apps/authentication`;
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}).then((responseJson) => {
|
||||||
|
if (responseJson.success === true) {
|
||||||
|
UpdateAppAuthentication(responseJson.data);
|
||||||
|
} else {
|
||||||
|
toast.error("Failed to get app authentication data");
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error("error for app is :", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuthenticationData = (props) => {
|
||||||
|
const selectedApp = props.app;
|
||||||
|
|
||||||
|
const [authenticationOption, setAuthenticationOptions] = React.useState({
|
||||||
|
app: JSON.parse(JSON.stringify(selectedApp)),
|
||||||
|
fields: {},
|
||||||
|
label: "",
|
||||||
|
usage: [
|
||||||
|
{
|
||||||
|
// workflow_id: workflow.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: uuidv4(),
|
||||||
|
active: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectedApp.authentication === undefined ||
|
||||||
|
selectedApp.authentication.parameters === null ||
|
||||||
|
selectedApp.authentication.parameters === undefined ||
|
||||||
|
selectedApp.authentication.parameters.length === 0
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<DialogContent style={{ textAlign: "center", marginTop: 50 }}>
|
||||||
|
<Typography variant="h4" id="draggable-dialog-title" style={{ cursor: "move", }}>
|
||||||
|
{selectedApp.name} does not require authentication
|
||||||
|
</Typography>
|
||||||
|
</DialogContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationOption.app.actions = [];
|
||||||
|
|
||||||
|
for (let paramkey in selectedApp.authentication.parameters) {
|
||||||
|
if (
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[paramkey].name
|
||||||
|
] === undefined
|
||||||
|
) {
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[paramkey].name
|
||||||
|
] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setNewAppAuth = (appAuthData, refresh) => {
|
||||||
|
setSelectedAuthentication(appAuthData);
|
||||||
|
var headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
headers["Org-Id"] = userdata?.active_org?.id
|
||||||
|
|
||||||
|
fetch(globalUrl + "/api/v1/apps/authentication", {
|
||||||
|
method: "PUT",
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify(appAuthData),
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for setting app auth :O!");
|
||||||
|
|
||||||
|
if (response.status === 400) {
|
||||||
|
toast.error("Failed setting new auth. Please try again", {
|
||||||
|
"autoClose": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (!responseJson.success) {
|
||||||
|
toast.error("Error: " + responseJson.reason, {
|
||||||
|
"autoClose": false,
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
HandleAppAuthentication()
|
||||||
|
setAuthenticationModalOpen(false)
|
||||||
|
getAppAuthentication()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log("New auth error: ", error.toString());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmitCheck = () => {
|
||||||
|
if (authenticationOption.label.length === 0) {
|
||||||
|
authenticationOption.label = `Auth for ${selectedApp.name}`;
|
||||||
|
}
|
||||||
|
for (let paramkey in selectedApp.authentication.parameters) {
|
||||||
|
if (
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[paramkey].name
|
||||||
|
].length === 0
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
selectedApp.authentication.parameters[paramkey].value !== undefined &&
|
||||||
|
selectedApp.authentication.parameters[paramkey].value !== null &&
|
||||||
|
selectedApp.authentication.parameters[paramkey].value.length > 0
|
||||||
|
) {
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[paramkey].name
|
||||||
|
] = selectedApp.authentication.parameters[paramkey].value;
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
selectedApp.authentication.parameters[paramkey].schema.type === "bool"
|
||||||
|
) {
|
||||||
|
authenticationOption.fields[
|
||||||
|
selectedApp.authentication.parameters[paramkey].name
|
||||||
|
] = "false";
|
||||||
|
} else {
|
||||||
|
toast(
|
||||||
|
"Field " +
|
||||||
|
selectedApp.authentication.parameters[paramkey].name +
|
||||||
|
" can't be empty"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption));
|
||||||
|
var newFields = [];
|
||||||
|
for (let authkey in newAuthOption.fields) {
|
||||||
|
const value = newAuthOption.fields[authkey];
|
||||||
|
newFields.push({
|
||||||
|
"key": authkey,
|
||||||
|
"value": value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
newAuthOption.fields = newFields
|
||||||
|
setNewAppAuth(newAuthOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authenticationOption.label === null || authenticationOption.label === undefined) {
|
||||||
|
authenticationOption.label = selectedApp.name + " authentication";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DialogTitle id="draggable-dialog-title" style={{ cursor: "move", }}>
|
||||||
|
<div style={{ color: theme.palette.textColor }}>
|
||||||
|
Authentication for {selectedApp.name.replaceAll("_", " ", -1)}
|
||||||
|
</div>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://shuffler.io/docs/apps#authentication"
|
||||||
|
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||||
|
>
|
||||||
|
What is app authentication?
|
||||||
|
</a>
|
||||||
|
<div />
|
||||||
|
These are required fields for authenticating with {selectedApp.name}
|
||||||
|
<div style={{ marginTop: 15 }} />
|
||||||
|
<b>Label for you to remember</b>
|
||||||
|
<TextField
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
borderRadius: theme.palette?.borderRadius,
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
color="primary"
|
||||||
|
placeholder={"Auth july 2020"}
|
||||||
|
defaultValue={`Auth for ${selectedApp.name}`}
|
||||||
|
onChange={(event) => {
|
||||||
|
authenticationOption.label = event.target.value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Divider
|
||||||
|
style={{
|
||||||
|
marginTop: 15,
|
||||||
|
marginBottom: 15,
|
||||||
|
backgroundColor: "rgb(91, 96, 100)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div />
|
||||||
|
{selectedApp.authentication.parameters.map((data, index) => {
|
||||||
|
if (data.value === "" || data.value === null || data.value === undefined || data.name === "url") {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={index} style={{ marginTop: 10 }}>
|
||||||
|
<LockOpenIcon style={{ marginRight: 10 }} />
|
||||||
|
<b>{data.name}</b>
|
||||||
|
|
||||||
|
{data.schema !== undefined &&
|
||||||
|
data.schema !== null &&
|
||||||
|
data.schema.type === "bool" ? (
|
||||||
|
<Select
|
||||||
|
MenuProps={{
|
||||||
|
disableScrollLock: true,
|
||||||
|
}}
|
||||||
|
SelectDisplayProps={{
|
||||||
|
style: {
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
defaultValue={"false"}
|
||||||
|
fullWidth
|
||||||
|
onChange={(e) => {
|
||||||
|
authenticationOption.fields[data.name] = e.target.value;
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.surfaceColor,
|
||||||
|
color: theme.palette.textColor,
|
||||||
|
height: 50,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem
|
||||||
|
key={"false"}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
color: theme.palette.textColor,
|
||||||
|
}}
|
||||||
|
value={"false"}
|
||||||
|
>
|
||||||
|
false
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
key={"true"}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
color: theme.palette.textColor,
|
||||||
|
}}
|
||||||
|
value={"true"}
|
||||||
|
>
|
||||||
|
true
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
) : (
|
||||||
|
<TextField
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
borderRadius: theme.palette?.borderRadius,
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
type={
|
||||||
|
data.example !== undefined && data.example.includes("***")
|
||||||
|
? "password"
|
||||||
|
: "text"
|
||||||
|
}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={
|
||||||
|
data.value !== undefined && data.value !== null
|
||||||
|
? data.value
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
placeholder={data.example}
|
||||||
|
onChange={(event) => {
|
||||||
|
authenticationOption.fields[data.name] =
|
||||||
|
event.target.value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
style={{}}
|
||||||
|
onClick={() => {
|
||||||
|
setAuthenticationModalOpen(false);
|
||||||
|
}}
|
||||||
|
color="secondary"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{}}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => {
|
||||||
|
setAuthenticationOptions(authenticationOption);
|
||||||
|
handleSubmitCheck()
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const authenticationModal = authenticationModalOpen ? (
|
||||||
|
<Dialog
|
||||||
|
PaperComponent={PaperComponent}
|
||||||
|
aria-labelledby="draggable-dialog-title"
|
||||||
|
hideBackdrop={true}
|
||||||
|
disableEnforceFocus={true}
|
||||||
|
disableBackdropClick={true}
|
||||||
|
style={{ pointerEvents: "none" }}
|
||||||
|
open={authenticationModalOpen}
|
||||||
|
onClose={() => {setSelectedMeta(undefined)}}
|
||||||
|
PaperProps={{
|
||||||
|
style: {
|
||||||
|
pointerEvents: "auto",
|
||||||
|
color: theme.palette.textColor,
|
||||||
|
minWidth: 1100,
|
||||||
|
minHeight: 800,
|
||||||
|
maxHeight: 800,
|
||||||
|
padding: 15,
|
||||||
|
overflow: "hidden",
|
||||||
|
zIndex: 10012,
|
||||||
|
border: theme.palette.defaultBorder,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
zIndex: 5000,
|
||||||
|
position: "absolute",
|
||||||
|
top: 20,
|
||||||
|
right: 75,
|
||||||
|
height: 50,
|
||||||
|
width: 50,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ selectedAppData.reference_info === undefined ||
|
||||||
|
selectedAppData.reference_info === null ||
|
||||||
|
selectedAppData.reference_info.github_url === undefined ||
|
||||||
|
selectedAppData.reference_info.github_url === null ||
|
||||||
|
selectedAppData.reference_info.github_url.length === 0 ? (
|
||||||
|
|
||||||
|
<a
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href={"https://github.com/shuffle/python-apps"}
|
||||||
|
style={{ textDecoration: "none", color: "#f86a3e" }}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt={`Documentation image for ${selectedAppData.name}`}
|
||||||
|
src={selectedAppData.large_image}
|
||||||
|
style={{
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
border: "2px solid rgba(255,255,255,0.6)",
|
||||||
|
borderRadius: theme.palette?.borderRadius,
|
||||||
|
maxHeight: 30,
|
||||||
|
maxWidth: 30,
|
||||||
|
overflow: "hidden",
|
||||||
|
fontSize: 8,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<a
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href={selectedAppData.reference_info.github_url}
|
||||||
|
style={{ textDecoration: "none", color: "#f86a3e" }}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt={`Documentation image for ${selectedAppData.name}`}
|
||||||
|
src={selectedAppData.large_image}
|
||||||
|
style={{
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
border: "2px solid rgba(255,255,255,0.6)",
|
||||||
|
borderRadius: theme.palette?.borderRadius,
|
||||||
|
maxWidth: 30,
|
||||||
|
maxHeight: 30,
|
||||||
|
overflow: "hidden",
|
||||||
|
fontSize: 8,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Tooltip
|
||||||
|
color="primary"
|
||||||
|
title={`Move window`}
|
||||||
|
placement="left"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
id="draggable-dialog-title"
|
||||||
|
style={{
|
||||||
|
zIndex: 5000,
|
||||||
|
position: "absolute",
|
||||||
|
top: 14,
|
||||||
|
right: 50,
|
||||||
|
color: "grey",
|
||||||
|
|
||||||
|
cursor: "move",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DragIndicatorIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<IconButton
|
||||||
|
style={{
|
||||||
|
zIndex: 5000,
|
||||||
|
position: "absolute",
|
||||||
|
top: 14,
|
||||||
|
right: 18,
|
||||||
|
color: "grey",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setAuthenticationModalOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 2,
|
||||||
|
padding: 0,
|
||||||
|
minHeight: isMobile ? "90%" : 800,
|
||||||
|
maxHeight: isMobile ? "90%" : 800,
|
||||||
|
overflowY: "auto",
|
||||||
|
overflowX: isMobile ? "auto" : "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{authenticationType?.type === "oauth2" || authenticationType?.type === "oauth2-app" ?
|
||||||
|
<AuthenticationOauth2
|
||||||
|
selectedApp={selectedAppData}
|
||||||
|
selectedAction={{
|
||||||
|
"app_name": selectedAppData.name,
|
||||||
|
"app_id": selectedAppData.id,
|
||||||
|
"app_version": selectedAppData.version,
|
||||||
|
"large_image": selectedAppData.large_image,
|
||||||
|
}}
|
||||||
|
authenticationType={authenticationType}
|
||||||
|
getAppAuthentication={getAppAuthentication}
|
||||||
|
appAuthentication={appAuthentication}
|
||||||
|
setAuthenticationModalOpen={setAuthenticationModalOpen}
|
||||||
|
isCloud={isCloud}
|
||||||
|
org_id={userdata?.active_org?.id}
|
||||||
|
setSelectedAction={setSelectedAction}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<AuthenticationData app={selectedAppData} />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 3,
|
||||||
|
borderLeft: `1px solid ${theme.palette.inputColor}`,
|
||||||
|
padding: "70px 30px 30px 30px",
|
||||||
|
maxHeight: 630,
|
||||||
|
minHeight: 630,
|
||||||
|
overflowY: "auto",
|
||||||
|
overflowX: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{selectedAppData?.documentation === undefined ||
|
||||||
|
selectedAppData?.documentation === null ||
|
||||||
|
selectedAppData?.documentation.length === 0 ? (
|
||||||
|
<span
|
||||||
|
style={{ textAlign: "center" }}
|
||||||
|
>
|
||||||
|
<div style={{textAlign: "left", }}>
|
||||||
|
<Markdown
|
||||||
|
components={{
|
||||||
|
img: Img,
|
||||||
|
code: CodeHandler,
|
||||||
|
h1: Heading,
|
||||||
|
h2: Heading,
|
||||||
|
h3: Heading,
|
||||||
|
h4: Heading,
|
||||||
|
h5: Heading,
|
||||||
|
h6: Heading,
|
||||||
|
a: OuterLink,
|
||||||
|
}}
|
||||||
|
id="markdown_wrapper"
|
||||||
|
escapeHtml={false}
|
||||||
|
style={{
|
||||||
|
maxWidth: "100%",
|
||||||
|
minWidth: "100%",
|
||||||
|
textAlign: "left",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{selectedAppData?.description}
|
||||||
|
</Markdown>
|
||||||
|
</div>
|
||||||
|
<Divider
|
||||||
|
style={{
|
||||||
|
marginTop: 25,
|
||||||
|
marginBottom: 25,
|
||||||
|
backgroundColor: "rgba(255,255,255,0.6)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
padding: 15,
|
||||||
|
borderRadius: theme.palette?.borderRadius,
|
||||||
|
marginBottom: 30,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h6" style={{marginBottom: 25, }}>
|
||||||
|
There is no Shuffle-specific documentation for this app yet outside of the general description above. Documentation is written for each api, and is a community effort. We hope to see your contribution!
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
toast.success("Opening remote Github documentation link. Thanks for contributing!")
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
window.open(`https://github.com/Shuffle/openapi-apps/new/master/docs?filename=${selectedAppData.name.toLowerCase()}.md`, "_blank")
|
||||||
|
}, 2500)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EditIcon /> Create Docs
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Typography variant="body1" style={{ marginTop: 25 }}>
|
||||||
|
Want to help the making of, or improve this app?{" "}
|
||||||
|
<br />
|
||||||
|
<a
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href="https://discord.gg/B2CBzUm"
|
||||||
|
style={{ textDecoration: "none", color: "#f86a3e" }}
|
||||||
|
>
|
||||||
|
Join the community on Discord!
|
||||||
|
</a>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="h6" style={{ marginTop: 50 }}>
|
||||||
|
Want to help change this app directly?
|
||||||
|
</Typography>
|
||||||
|
{selectedAppData.reference_info === undefined ||
|
||||||
|
selectedAppData.reference_info === null ||
|
||||||
|
selectedAppData.reference_info.github_url === undefined ||
|
||||||
|
selectedAppData.reference_info.github_url === null ||
|
||||||
|
selectedAppData.reference_info.github_url.length === 0 ? (
|
||||||
|
<span>
|
||||||
|
<Typography variant="body1" style={{ marginTop: 25 }}>
|
||||||
|
<a
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href={"https://github.com/shuffle/python-apps"}
|
||||||
|
style={{ textDecoration: "none", color: "#f86a3e" }}
|
||||||
|
>
|
||||||
|
Check it out on Github!
|
||||||
|
</a>
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span>
|
||||||
|
<Typography variant="body1" style={{ marginTop: 25 }}>
|
||||||
|
<a
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href={selectedAppData.reference_info.github_url}
|
||||||
|
style={{ textDecoration: "none", color: "#f86a3e" }}
|
||||||
|
>
|
||||||
|
Check it out on Github!
|
||||||
|
</a>
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
{selectedMeta !== undefined && selectedMeta !== null && Object.getOwnPropertyNames(selectedMeta).length > 0 && selectedMeta.name !== undefined && selectedMeta.name !== null ?
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
padding: 15,
|
||||||
|
borderRadius: theme.palette?.borderRadius,
|
||||||
|
marginBottom: 30,
|
||||||
|
display: "flex",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ flex: 3, display: "flex", vAlign: "center", position: "sticky", top: 50, }}>
|
||||||
|
{isMobile ? null : (
|
||||||
|
<Typography style={{ display: "inline", marginTop: 6 }}>
|
||||||
|
<a
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href={selectedMeta.link}
|
||||||
|
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||||
|
>
|
||||||
|
<Button style={{ color: theme.palette.textColor, }} variant="outlined" color="secondary">
|
||||||
|
<EditIcon /> Edit
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
{isMobile ? null : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: "100%",
|
||||||
|
width: 1,
|
||||||
|
backgroundColor: theme.palette.textColor,
|
||||||
|
marginLeft: 50,
|
||||||
|
marginRight: 50,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Typography style={{ display: "inline", marginTop: 11 }}>
|
||||||
|
{selectedMeta.read_time} minute
|
||||||
|
{selectedMeta.read_time === 1 ? "" : "s"} to read
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 2 }}>
|
||||||
|
{isMobile ||
|
||||||
|
selectedMeta.contributors === undefined ||
|
||||||
|
selectedMeta.contributors === null ? (
|
||||||
|
""
|
||||||
|
) : (
|
||||||
|
<div style={{ margin: 10, height: "100%", display: "inline" }}>
|
||||||
|
{selectedMeta.contributors.slice(0, 7).map((data, index) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={index}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href={data.url}
|
||||||
|
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||||
|
>
|
||||||
|
<Tooltip title={data.url} placement="bottom">
|
||||||
|
<img
|
||||||
|
alt={data.url}
|
||||||
|
src={data.image}
|
||||||
|
style={{
|
||||||
|
marginTop: 5,
|
||||||
|
marginRight: 10,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: 40,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
|
||||||
|
<Markdown
|
||||||
|
components={{
|
||||||
|
img: Img,
|
||||||
|
code: CodeHandler,
|
||||||
|
h1: Heading,
|
||||||
|
h2: Heading,
|
||||||
|
h3: Heading,
|
||||||
|
h4: Heading,
|
||||||
|
h5: Heading,
|
||||||
|
h6: Heading,
|
||||||
|
a: OuterLink,
|
||||||
|
}}
|
||||||
|
id="markdown_wrapper"
|
||||||
|
escapeHtml={false}
|
||||||
|
style={{
|
||||||
|
maxWidth: "100%", minWidth: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{selectedAppData.documentation}
|
||||||
|
</Markdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return authenticationModal
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthenticationModal;
|
||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
ListItemText,
|
ListItemText,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Search as SearchIcon } from '@mui/icons-material';
|
import { Search as SearchIcon } from '@mui/icons-material';
|
||||||
import useDebouncedCallback from '../utils/useDebouncedCallback.js';
|
import useDebouncedCallback from '../utils/useDebouncedCallback.jsx';
|
||||||
|
|
||||||
|
|
||||||
const searchClient = algoliasearch("JNSS5CFDZZ", "1e5f29b1550939855de5915eac3bf5f7");
|
const searchClient = algoliasearch("JNSS5CFDZZ", "1e5f29b1550939855de5915eac3bf5f7");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect, useContext, memo, useMemo } from 'react'
|
import React, { useState, useEffect, useContext, memo, useMemo } from 'react'
|
||||||
import {getTheme} from '../theme.jsx';
|
import {getTheme} from '../theme.jsx';
|
||||||
import { Context } from '../context/ContextApi.jsx';
|
import { Context } from '../context/ContextApi.jsx';
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Typography,
|
Typography,
|
||||||
@@ -14,19 +15,104 @@ import {
|
|||||||
|
|
||||||
GridlineSeries,
|
GridlineSeries,
|
||||||
Gridline,
|
Gridline,
|
||||||
TooltipArea,
|
|
||||||
ChartTooltip,
|
ChartTooltip,
|
||||||
TooltipTemplate,
|
TooltipTemplate,
|
||||||
|
TooltipArea,
|
||||||
} from 'reaviz';
|
} from 'reaviz';
|
||||||
|
|
||||||
|
export const LoadStats = (globalUrl, cachekey) => {
|
||||||
|
if (globalUrl === undefined) {
|
||||||
|
console.log("Error: Global URL is undefined")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachekey === undefined) {
|
||||||
|
console.log("Error: Cachekey is undefined")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var basedata = {
|
||||||
|
"key": cachekey,
|
||||||
|
"total": 0,
|
||||||
|
"available_keys": [],
|
||||||
|
"labels": [],
|
||||||
|
"datasets": [
|
||||||
|
{
|
||||||
|
"label": "",
|
||||||
|
"data": [],
|
||||||
|
"backgroundColor": [],
|
||||||
|
"barThickness": 15,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
//const url = `${globalUrl}/api/v1/stats/app_executions_test2`
|
||||||
|
//cachekey = cachekey.replace(" ", "_", -1)
|
||||||
|
const url = `${globalUrl}/api/v1/stats/${cachekey}`
|
||||||
|
return fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((resp) => {
|
||||||
|
return resp.json()
|
||||||
|
}).then((respJson) => {
|
||||||
|
const selectedIndex = 0
|
||||||
|
|
||||||
|
//console.log("Stats response: ", respJson)
|
||||||
|
|
||||||
|
if (respJson.success === true) {
|
||||||
|
for (let entryKey in respJson.entries) {
|
||||||
|
const entry = respJson.entries[entryKey]
|
||||||
|
basedata.labels.push(entry.date)
|
||||||
|
|
||||||
|
basedata.datasets[0].data.push(entry.value)
|
||||||
|
basedata.datasets[0].backgroundColor.push(entry.value > 0 ? "rgba(255,255,255,0.4)" : "red")
|
||||||
|
}
|
||||||
|
|
||||||
|
basedata.available_keys = respJson.available_keys
|
||||||
|
basedata.total = respJson.total
|
||||||
|
|
||||||
|
return basedata
|
||||||
|
} else {
|
||||||
|
console.log("Failed to get stats")
|
||||||
|
return basedata
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
toast("Failed to get stats")
|
||||||
|
return basedata
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const LineChartWrapper = (props) => {
|
const LineChartWrapper = (props) => {
|
||||||
const {keys, inputname, height, width, border} = props
|
const {keys, inputname, height, width, border, color} = props
|
||||||
|
|
||||||
const [hovered, setHovered] = useState("");
|
const [hovered, setHovered] = useState("");
|
||||||
const {themeMode} = useContext(Context)
|
const {themeMode} = useContext(Context)
|
||||||
const theme = getTheme(themeMode)
|
const theme = getTheme(themeMode)
|
||||||
|
|
||||||
var inputdata = keys.data === undefined ? keys : keys.data
|
// Correct format:
|
||||||
|
/* keys={[
|
||||||
|
{
|
||||||
|
key: "2025-07-23T00:00:09.409718Z",
|
||||||
|
data: 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "2025-07-24T00:00:09.409718Z",
|
||||||
|
data: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "2025-07-25T00:00:09.409718Z",
|
||||||
|
data: 75,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "2025-07-26T00:00:09.409718Z",
|
||||||
|
data: 42,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
*/
|
||||||
|
|
||||||
|
var inputdata = keys?.data === undefined ? keys : keys.data
|
||||||
|
|
||||||
var newname = inputname === undefined || inputname === null ? "" : inputname.trim().replaceAll("_", " ")
|
var newname = inputname === undefined || inputname === null ? "" : inputname.trim().replaceAll("_", " ")
|
||||||
newname = newname.charAt(0).toUpperCase() + newname.slice(1)
|
newname = newname.charAt(0).toUpperCase() + newname.slice(1)
|
||||||
@@ -35,7 +121,6 @@ const LineChartWrapper = (props) => {
|
|||||||
|
|
||||||
var tmpdata = inputdata?.datasets[0]
|
var tmpdata = inputdata?.datasets[0]
|
||||||
if (tmpdata?.data !== undefined && tmpdata?.data !== null && tmpdata?.data.length > 0 && inputdata?.labels?.length === tmpdata?.data?.length) {
|
if (tmpdata?.data !== undefined && tmpdata?.data !== null && tmpdata?.data.length > 0 && inputdata?.labels?.length === tmpdata?.data?.length) {
|
||||||
console.log("Fix it!")
|
|
||||||
var newarray = []
|
var newarray = []
|
||||||
for (var key in tmpdata.data) {
|
for (var key in tmpdata.data) {
|
||||||
var entry = {
|
var entry = {
|
||||||
@@ -48,26 +133,25 @@ const LineChartWrapper = (props) => {
|
|||||||
|
|
||||||
inputdata = newarray
|
inputdata = newarray
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputdata === undefined || inputdata === null) {
|
if (inputdata === undefined || inputdata === null) {
|
||||||
return (
|
return null /*(
|
||||||
<Typography>
|
<Typography>
|
||||||
Invalid linegraph data format
|
Invalid linegraph data format
|
||||||
</Typography>
|
</Typography>
|
||||||
)
|
)*/
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultStyle = {
|
var defaultStyle = {
|
||||||
color: "white",
|
color: "white",
|
||||||
padding: 30,
|
padding: "5px 5px 10px 5px",
|
||||||
marginTop: 15,
|
marginTop: 15,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
|
|
||||||
border: "1px solid rgba(255,255,255,0.3)",
|
border: "1px solid rgba(255,255,255,0.3)",
|
||||||
borderRadius: theme.palette?.borderRadius,
|
borderRadius: theme.palette?.borderRadius,
|
||||||
backgroundColor: theme.palette.platformColor,
|
//backgroundColor: theme.palette.platformColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (border === false) {
|
if (border === false) {
|
||||||
@@ -76,11 +160,64 @@ const LineChartWrapper = (props) => {
|
|||||||
defaultStyle.backgroundColor = "transparent"
|
defaultStyle.backgroundColor = "transparent"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if it's a list or not
|
||||||
|
if (!Array.isArray(inputdata) || inputdata.length === 0 || (inputdata.length > 0 && (inputdata[0].key === undefined && inputdata[0].data === undefined))) {
|
||||||
|
console.log("Invalid graph data format: ", inputdata)
|
||||||
|
console.log("Expected format: [{key: 'label1', data: 10}, {key: 'label2', data: 20}]")
|
||||||
|
//inputdata = inputdata?.datasets[0]?.data
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("FORMAT: ", inputdata)
|
||||||
|
|
||||||
|
const tooltip = <TooltipArea
|
||||||
|
tooltip={
|
||||||
|
<ChartTooltip
|
||||||
|
placement={"top"}
|
||||||
|
followCursor={true}
|
||||||
|
content={(data) => (
|
||||||
|
<div style={{
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
border: theme.palette.defaultBorder,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
padding: 6,
|
||||||
|
maxWidth: 240,
|
||||||
|
}}>
|
||||||
|
<Typography variant="body2" style={{fontWeight: 600}}>{data?.x ?? ''}</Typography>
|
||||||
|
<Typography variant="body2">{data?.y ?? ''}</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>;
|
||||||
|
|
||||||
|
const selectedColor = color === undefined || color === null || color === "" ? "" : color
|
||||||
|
const barseries = color === undefined || color === null || color === "" ?
|
||||||
|
<BarSeries
|
||||||
|
bar={
|
||||||
|
<Bar
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
tooltip={tooltip}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<BarSeries
|
||||||
|
colorScheme={[selectedColor]}
|
||||||
|
bar={
|
||||||
|
<Bar
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
tooltip={tooltip}
|
||||||
|
/>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={defaultStyle}>
|
<div style={defaultStyle}>
|
||||||
<Typography variant="h6" style={{marginBotton: 30, }}>
|
{newname !== "" &&
|
||||||
{newname}
|
<Typography variant="h6" style={{paddingBottom: 25, }}>
|
||||||
</Typography>
|
{newname}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
|
||||||
<BarChart
|
<BarChart
|
||||||
style={{marginTop: 100, }}
|
style={{marginTop: 100, }}
|
||||||
@@ -89,11 +226,8 @@ const LineChartWrapper = (props) => {
|
|||||||
data={inputdata}
|
data={inputdata}
|
||||||
|
|
||||||
series={
|
series={
|
||||||
<BarSeries
|
barseries
|
||||||
bar={
|
|
||||||
<Bar />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
gridlines={
|
gridlines={
|
||||||
<GridlineSeries line={<Gridline direction="all" />} />
|
<GridlineSeries line={<Gridline direction="all" />} />
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { useRef, useEffect, useCallback } from "react";
|
||||||
|
|
||||||
|
export const useDebouncedCallback = (callback, delay = 300) => {
|
||||||
|
const timeoutRef = useRef(null);
|
||||||
|
const savedCallback = useRef(callback);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
savedCallback.current = callback;
|
||||||
|
}, [callback]);
|
||||||
|
|
||||||
|
useEffect(() => () => {
|
||||||
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return useCallback((...args) => {
|
||||||
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||||
|
timeoutRef.current = setTimeout(() => {
|
||||||
|
savedCallback.current(...args);
|
||||||
|
}, delay);
|
||||||
|
}, [delay]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDebouncedCallback;
|
||||||
|
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ import { CodeHandler, Img, OuterLink, } from "../views/Docs.jsx";
|
|||||||
|
|
||||||
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||||
import algoliasearch from 'algoliasearch/lite';
|
import algoliasearch from 'algoliasearch/lite';
|
||||||
import useDebouncedCallback from "../utils/useDebouncedCallback.js";
|
import useDebouncedCallback from "../utils/useDebouncedCallback.jsx";
|
||||||
import {
|
import {
|
||||||
Zoom,
|
Zoom,
|
||||||
Fade,
|
Fade,
|
||||||
|
|||||||
Reference in New Issue
Block a user