import React, { useState, useEffect } from "react"; import { useInterval } from "react-powerhooks"; import { makeStyles, useTheme } from "@material-ui/core/styles"; // nodejs library that concatenates classes import classNames from "classnames"; import theme from '../theme.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; // react plugin used to create charts //import { Line, Bar } from "react-chartjs-2"; import { useAlert } from "react-alert"; import Autocomplete from "@material-ui/lab/Autocomplete"; import Draggable from "react-draggable"; import { Tooltip, TextField, IconButton, Button, Typography, Grid, Paper, Chip, Checkbox, } from "@material-ui/core"; import { Close as CloseIcon, DoneAll as DoneAllIcon, Description as DescriptionIcon, PlayArrow as PlayArrowIcon, Edit as EditIcon, CheckBox as CheckBoxIcon, CheckBoxOutlineBlank as CheckBoxOutlineBlankIcon, OpenInNew as OpenInNewIcon, } from "@material-ui/icons"; import WorkflowPaper from "../components/WorkflowPaper.jsx" import { removeParam } from "../views/AngularWorkflow.jsx" // core components //import { // chartExample1, // chartExample2, // chartExample3, // chartExample4, //} from "../charts.js"; import { RadialBarChart, RadialAreaChart, RadialAxis, StackedBarSeries, TooltipArea, ChartTooltip, TooltipTemplate, RadialAreaSeries, RadialPointSeries, RadialArea, RadialLine, TreeMap, TreeMapSeries, TreeMapLabel, TreeMapRect, Line, LineChart, LineSeries, LinearYAxis, LinearXAxis, LinearYAxisTickSeries, LinearXAxisTickSeries, AreaChart, AreaSeries, PointSeries, } from 'reaviz'; const useStyles = makeStyles({ notchedOutline: { borderColor: "#f85a3e !important", }, root: { "& .MuiAutocomplete-listbox": { border: "2px solid #f85a3e", color: "white", fontSize: 18, "& li:nth-child(even)": { backgroundColor: "#CCC", }, "& li:nth-child(odd)": { backgroundColor: "#FFF", }, }, }, inputRoot: { color: "white", // This matches the specificity of the default styles at https://github.com/mui-org/material-ui/blob/v4.11.3/packages/material-ui-lab/src/Autocomplete/Autocomplete.js#L90 "&:hover .MuiOutlinedInput-notchedOutline": { borderColor: "#f86a3e", }, }, }); const inputdata = [ { "key": "Threat Intel", "value": 18, "x": "2020-02-17T08:00:00.000Z", "x0": "2020-02-17T08:00:00.000Z", "x1": "2020-02-17T08:00:00.000Z", "y": 18, "y0": 0, "y1": 18 }, { "key": "Threat Intel", "value": 3, "x": "2020-02-21T08:00:00.000Z", "x0": "2020-02-21T08:00:00.000Z", "x1": "2020-02-21T08:00:00.000Z", "y": 3, "y0": 0, "y1": 3 }, { "key": "Threat Intel", "value": 14, "x": "2020-02-26T08:00:00.000Z", "x0": "2020-02-26T08:00:00.000Z", "x1": "2020-02-26T08:00:00.000Z", "y": 14, "y0": 0, "y1": 14 }, { "key": "Threat Intel", "value": 18, "x": "2020-02-29T08:00:00.000Z", "x0": "2020-02-29T08:00:00.000Z", "x1": "", "y": 18, "y0": 0, "y1": 18 } ] const LineChartWrapper = ({keys, height, width}) => { const [hovered, setHovered] = useState(""); //console.log("Date: ", new Date("2019-11-14T08:00:00.000Z")) console.log("Keys: ", keys) var inputdata = keys.data /* const inputdata = [{ "key": "Intel", "data": [ { key: new Date('11/22/2019'), data: 3, metadata: {color: "orange", "name": "Intel"}}, { key: new Date('11/24/2019'), data: 8, metadata: {color: "orange", "name": "Intel"}}, { key: new Date('11/29/2019'), data: 2, metadata: {color: "orange", "name": "Intel"}}, ]}, { "key": "Popper", "data": [ { key: new Date('11/24/2019'), data: 9, }, { key: new Date('11/29/2019'), data: 3, }, ] } ] */ return (
{keys.title} } colorScheme={(colorInput) => { var color = "cybertron" if (colorInput !== undefined && colorInput.length > 0) { color = colorInput[0].metadata !== undefined && colorInput[0].metadata.color !== undefined ? colorInput[0].metadata.color : color } return color }} tooltip={ { if (hovered !== event.value.x) { //setHovered(event.value.x) } }} tooltip={ { console.log("DATA: ", data) const name = data.metadata !== undefined && data.metadata.name !== undefined ? data.metadata.name : "No" return (
{name}
) /* ) */ } } /> } /> } /> } />
) } const RadialChart = ({keys, setSelectedCategory}) => { const [hovered, setHovered] = useState(""); return (
{ console.log("Click: ", hovered) if (setSelectedCategory !== undefined) { setSelectedCategory(hovered) } }}> } series={ { return '#f86a3e' }} animated={false} id="workflow_series_id" style={{cursor: "pointer",}} line={ { console.log("INFO: ", data, color) return ( null ) }} /> } tooltip={ { if (hovered !== event.value.x) { setHovered(event.value.x) } }} tooltip={ { return (
{data.x}
) /* ) */ } } /> } /> } /> } />
) //axis={} } // This is the start of a dashboard that can be used. // What data do we fill in here? Idk const Dashboard = (props) => { const { globalUrl, isLoggedIn } = props; const alert = useAlert(); const [bigChartData, setBgChartData] = useState("data1"); const [dayAmount, setDayAmount] = useState(7); const [firstRequest, setFirstRequest] = useState(true); const [stats, setStats] = useState({}); const [changeme, setChangeme] = useState(""); const [statsRan, setStatsRan] = useState(false); const [keys, setKeys] = useState([]) const [treeKeys, setTreeKeys] = useState([]) const [selectedUsecaseCategory, setSelectedUsecaseCategory] = useState(""); const [selectedUsecases, setSelectedUsecases] = useState([]); const [usecases, setUsecases] = useState([]); const [workflows, setWorkflows] = useState([]); const [frameworkData, setFrameworkData] = useState(undefined); const [widgetData, setWidgetData] = useState([]); let navigate = useNavigate(); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; useEffect(() => { if (selectedUsecaseCategory.length === 0) { setSelectedUsecases(usecases) } else { const foundUsecase = usecases.find(data => data.name === selectedUsecaseCategory) if (foundUsecase !== undefined && foundUsecase !== null) { setSelectedUsecases([foundUsecase]) } } }, [selectedUsecaseCategory]) const checkSelectedParams = () => { const urlSearchParams = new URLSearchParams(window.location.search) const params = Object.fromEntries(urlSearchParams.entries()) const curpath = typeof window === "undefined" || window.location === undefined ? "" : window.location.pathname; const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; const foundQuery = params["selected"] if (foundQuery !== null && foundQuery !== undefined) { setSelectedUsecaseCategory(foundQuery) const newitem = removeParam("selected", cursearch); navigate(curpath + newitem) } const foundQuery2 = params["selected_object"] if (foundQuery2 !== null && foundQuery2 !== undefined) { console.log("Got selected_object: ", foundQuery2) const queryName = foundQuery2.toLowerCase().replaceAll("_", " ") // Waiting a bit for it to render setTimeout(() => { const foundItem = document.getElementById(queryName) if (foundItem !== undefined && foundItem !== null) { foundItem.click() } else { //console.log("Couldn't find item with name ", queryName) } }, 100); } } useEffect(() => { if (usecases.length > 0) { console.log(usecases) checkSelectedParams() } }, [usecases]) const getWidget = (dashboard, widget) => { fetch(`${globalUrl}/api/v1/dashboards/${dashboard}/widgets/${widget}`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for framework!"); } return response.json(); }) .then((responseJson) => { console.log("Resp: ", responseJson) if (responseJson.success === false) { if (responseJson.reason !== undefined) { //alert.error("Failed loading: " + responseJson.reason) } else { //alert.error("Failed to load framework for your org.") } } else { var tmpdata = responseJson for (var key in tmpdata.data) { for (var subkey in tmpdata.data[key].data) { tmpdata.data[key].data[subkey].key = new Date(tmpdata.data[key].data[subkey].key) } } const foundWidget = widgetData.findIndex(data => data.title === widget) console.log("Found: ", foundWidget) if (foundWidget !== undefined && foundWidget !== null && foundWidget >= 0) { widgetData[foundWidget] = tmpdata } else { widgetData.push(tmpdata) } console.log("Data: ", widgetData) setWidgetData(widgetData) } }) .catch((error) => { //alert.error(error.toString()); }) } document.title = "Shuffle - Dashboard"; var dayGraphLabels = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]; var dayGraphData = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]; const handleKeysetting = (categorydata) => { var allCategories = [] var treeCategories = [] for (key in categorydata) { const category = categorydata[key] allCategories.push({"key": category.name, "data": category.list.length, "color": category.color}) treeCategories.push({"key": category.name, "data": 100, "color": category.color,}) for (var subkey in category.list) { treeCategories.push({"key": category.list[subkey].name, "data": 20, "color": category.color}) } } setKeys(allCategories) setTreeKeys(treeCategories) } useEffect(() => { getWidget("main", "Overall") getWidget("main", "Overall2") }, []); const fetchdata = (stats_id) => { fetch(globalUrl + "/api/v1/stats/" + stats_id, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for " + stats_id); } return response.json(); }) .then((responseJson) => { stats[stats_id] = responseJson; setStats(stats); // Used to force updates setChangeme(stats_id); }) .catch((error) => { //alert.error("ERROR: " + error.toString()); console.log("ERROR: " + error.toString()); }); }; let chart1_2_options = { maintainAspectRatio: false, legend: { display: false, }, tooltips: { backgroundColor: "#f5f5f5", titleFontColor: "#333", bodyFontColor: "#666", bodySpacing: 4, xPadding: 12, mode: "nearest", intersect: 0, position: "nearest", }, responsive: true, scales: { yAxes: [ { barPercentage: 1.6, gridLines: { drawBorder: false, color: "rgba(29,140,248,0.0)", zeroLineColor: "transparent", }, ticks: { suggestedMin: 60, suggestedMax: 125, padding: 20, fontColor: "#9a9a9a", }, }, ], xAxes: [ { barPercentage: 1.6, gridLines: { drawBorder: false, color: "rgba(29,140,248,0.1)", zeroLineColor: "transparent", }, ticks: { padding: 20, fontColor: "#9a9a9a", }, }, ], }, }; const dayGraph = { data: (canvas) => { let ctx = canvas.getContext("2d"); let gradientStroke = ctx.createLinearGradient(0, 230, 0, 50); gradientStroke.addColorStop(1, "rgba(29,140,248,0.2)"); gradientStroke.addColorStop(0.4, "rgba(29,140,248,0.0)"); gradientStroke.addColorStop(0, "rgba(29,140,248,0)"); //blue colors return { labels: dayGraphLabels, datasets: [ { label: "My First dataset", fill: true, backgroundColor: gradientStroke, borderColor: "#1f8ef1", borderWidth: 2, borderDash: [], borderDashOffset: 0.0, pointBackgroundColor: "#1f8ef1", pointBorderColor: "rgba(255,255,255,0)", pointHoverBackgroundColor: "#1f8ef1", pointBorderWidth: 20, pointHoverRadius: 4, pointHoverBorderWidth: 15, pointRadius: 4, data: dayGraphData, }, ], }; }, options: chart1_2_options, }; // All these are currently tracked. const variables = [ "backend_executions", "workflow_executions", "workflow_executions_aborted", "workflow_executions_success", "total_apps_created", "total_apps_loaded", "openapi_apps_created", "total_apps_deleted", "total_webhooks_ran", "total_workflows", "total_workflow_actions", "total_workflow_triggers", ]; const runUpdate = () => { for (var key in variables) { fetchdata(variables[key]); } }; // Refresh every 60 seconds const autoUpdate = 60000; const { start, stop } = useInterval({ duration: autoUpdate, startImmediate: false, callback: () => { runUpdate(); }, }); if (firstRequest) { setFirstRequest(false); //start(); //runUpdate(); } else if (!statsRan) { // FIXME: Run this under runUpdate schedule? // 1. Fix labels in dayGraphy.data // 2. Add data to the daygraph // Every time there's an update :) // This should probably be done in the backend.. bleh if ( stats["workflow_executions"] !== undefined && stats["workflow_executions"] !== null && stats["workflow_executions"].data !== undefined ) { setStatsRan(true); //console.log("NEW DATA?: ", stats) console.log("SET WORKFLOW: ", stats["workflow_executions"]); //var curday = startDate.getDate() // Index = what day are we on // 0 = today var newDayGraphLabels = []; var newDayGraphData = []; for (var i = dayAmount; i > 0; i--) { var enddate = new Date(); enddate.setDate(-i); enddate.setHours(23, 59, 59, 999); var startdate = new Date(); startdate.setDate(-i); startdate.setHours(0, 0, 0, 0); var endtime = enddate.getTime() / 1000; var starttime = startdate.getTime() / 1000; console.log( "START: ", starttime, "END: ", endtime, "Data: ", stats["workflow_executions"] ); for (var key in stats["workflow_executions"].data) { const item = stats["workflow_executions"]["data"][key]; console.log("ITEM: ", item.timestamp, endtime); console.log(endtime - starttime); if ( endtime - starttime > endtime - item.timestamp && endtime.timestamp >= 0 ) { console.log("HIT? "); } console.log(item.timestamp - endtime); //console.log(item.timestamp-endtime) break; if (item.timestamp > endtime && item.timestamp < starttime) { if (newDayGraphData[i - 1] === undefined) { newDayGraphData[i - 1] = 1; } else { newDayGraphData[i - 1] += 1; } //break } } newDayGraphLabels.push(i); } console.log(newDayGraphLabels); console.log(newDayGraphData); } } const newdata = Object.getOwnPropertyNames(stats).length > 0 ? (
Autoupdate every {autoUpdate / 1000} seconds {variables.map((data) => { if (stats[data] === undefined || stats[data] === null) { return null; } if (stats[data].total === undefined) { return null; } return (
{data}: {stats[data].total}
); })}
) : null; const data = (
{keys.length > 0 ? : null}
{widgetData === undefined || widgetData === null || widgetData === [] || widgetData.length === 0 ? null : }
); const dataWrapper = (
{data}
); return dataWrapper; }; export default Dashboard;