845 lines
23 KiB
React
845 lines
23 KiB
React
import React, { useState, useEffect } from 'react';
|
|
|
|
import theme from '../theme.jsx';
|
|
import classNames from "classnames";
|
|
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
|
|
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'
|
|
import { toast } from "react-toastify"
|
|
|
|
import {
|
|
DatePicker,
|
|
DateTimePicker,
|
|
LocalizationProvider,
|
|
} from '@mui/x-date-pickers'
|
|
|
|
import {
|
|
OpenInNew as OpenInNewIcon,
|
|
} from '@mui/icons-material';
|
|
|
|
import {
|
|
CircularProgress,
|
|
Link,
|
|
Tooltip,
|
|
TextField,
|
|
IconButton,
|
|
Button,
|
|
Typography,
|
|
Grid,
|
|
Paper,
|
|
Chip,
|
|
Checkbox,
|
|
} from "@mui/material";
|
|
|
|
import {
|
|
BarChart,
|
|
GridlineSeries,
|
|
Gridline,
|
|
} from 'reaviz';
|
|
|
|
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
|
|
|
|
const LineChartWrapper = ({keys, inputname, height, width}) => {
|
|
const [hovered, setHovered] = useState("");
|
|
const inputdata = keys.data === undefined ? keys : keys.data
|
|
|
|
return (
|
|
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: 30, marginTop: 15, backgroundColor: theme.palette.platformColor, overflow: "hidden", }}>
|
|
<Typography variant="h6" style={{marginBotton: 15, }}>
|
|
{inputname}
|
|
</Typography>
|
|
<BarChart
|
|
width={"100%"}
|
|
height={height}
|
|
data={inputdata}
|
|
gridlines={
|
|
<GridlineSeries line={<Gridline direction="all" />} />
|
|
}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
|
|
const AppStats = (defaultprops) => {
|
|
const { globalUrl, selectedOrganization, userdata, isCloud, inputWorkflows,clickedFromOrgTab } = defaultprops;
|
|
|
|
const [keys, setKeys] = useState([])
|
|
const [searches, setSearches] = useState([]);
|
|
const [appRuns, setAppruns] = useState(undefined);
|
|
const [appRunCosts, setApprunCosts] = useState(undefined);
|
|
const [workflowRuns, setWorkflowRuns] = useState(undefined);
|
|
const [subflowRuns, setSubflowRuns] = useState(undefined);
|
|
|
|
const [endTime, setEndTime] = useState("")
|
|
const [startTime, setStartTime] = useState("")
|
|
const [statistics, setStatistics] = useState(undefined);
|
|
const [filteredStatistics, setFilteredStatistics] = useState(undefined);
|
|
|
|
const [apprunCost, setApprunCost] = useState(0)
|
|
const [monthToDateCost, setMonthToDateCost] = useState(0)
|
|
const [monthTotalCost, setMonthTotalCost] = useState(0)
|
|
|
|
const [workflows, setWorkflows] = useState(inputWorkflows === undefined ? [] : inputWorkflows)
|
|
const [resultRows, setResultRows] = useState([])
|
|
const [resultLoading, setResultLoading] = useState(true)
|
|
|
|
const includedExecutions = selectedOrganization.sync_features.app_executions !== undefined ? selectedOrganization.sync_features.app_executions.limit : 0
|
|
|
|
useEffect(() => {
|
|
if (workflows === undefined || workflows === null || workflows.length === 0) {
|
|
getAvailableWorkflows()
|
|
}
|
|
}, [])
|
|
|
|
|
|
|
|
const getWorkflowStats = async (workflow, startTime, endTime) => {
|
|
if (!userdata.support) {
|
|
return workflow
|
|
}
|
|
|
|
if (workflow.id === undefined || workflow.id === null || workflow.id === "") {
|
|
return workflow
|
|
}
|
|
|
|
var starttime = ""
|
|
var endtime = ""
|
|
try {
|
|
starttime = startTime === undefined || startTime === null || startTime === "" ? "" : new Date(startTime).toISOString()
|
|
endtime = endTime === undefined || endTime === null || endTime == "" ? "" : new Date(endTime).toISOString()
|
|
} catch(err) {
|
|
console.log("Error converting start/end time", err)
|
|
toast("Bad start/endtime. Please try again")
|
|
return
|
|
}
|
|
|
|
var url = `${globalUrl}/api/v1/workflows/${workflow.id}/executions/count`
|
|
|
|
if (starttime !== "") {
|
|
url += `?start_time=${starttime}`
|
|
}
|
|
|
|
if (endtime !== "") {
|
|
if (starttime !== "") {
|
|
url += `&end_time=${endtime}`
|
|
} else {
|
|
url += `?end_time=${endtime}`
|
|
}
|
|
}
|
|
|
|
const response = await fetch(url, {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
},
|
|
credentials: "include",
|
|
}).catch((error) => {
|
|
console.log("Error getting workflow stats: " + error);
|
|
return workflow
|
|
})
|
|
|
|
if (response.status !== 200) {
|
|
console.log("Status not 200 for workflow stats URL: " + url);
|
|
return workflow
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (data === undefined || data === null) {
|
|
console.log("No data for workflow stats URL: " + url);
|
|
return workflow
|
|
}
|
|
|
|
if (data.success === false) {
|
|
console.log("No success for workflow stats URL: " + url, data.reason);
|
|
return workflow
|
|
}
|
|
|
|
workflow.runcount = data.count
|
|
return workflow
|
|
}
|
|
|
|
const loadWorkflowStats = (foundWorkflows, startTime, endTime) => {
|
|
if (!userdata.support) {
|
|
console.log("Not support")
|
|
|
|
return
|
|
}
|
|
|
|
if (foundWorkflows === undefined || foundWorkflows === null || foundWorkflows.length === 0) {
|
|
console.log("Not workflows")
|
|
return
|
|
}
|
|
|
|
// Only do latest 20
|
|
setResultLoading(true)
|
|
const promises = foundWorkflows.slice(0, 50).map(wf => getWorkflowStats(wf, startTime, endTime));
|
|
|
|
const allData = Promise.all(promises);
|
|
|
|
allData.then((data) => {
|
|
var total = 0
|
|
for (var i = 0; i < data.length; i++) {
|
|
if (data[i].runcount !== undefined) {
|
|
total += data[i].runcount
|
|
} else {
|
|
data[i].runcount = 0
|
|
}
|
|
}
|
|
|
|
data[0].runcount = total
|
|
// Sort data by runcount
|
|
data.sort((a, b) => (a.runcount < b.runcount) ? 1 : -1)
|
|
setResultRows(data)
|
|
setResultLoading(false)
|
|
})
|
|
}
|
|
|
|
const getAvailableWorkflows = () => {
|
|
fetch(globalUrl + "/api/v1/workflows", {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
},
|
|
credentials: "include",
|
|
})
|
|
.then((response) => {
|
|
if (response.status !== 200) {
|
|
console.log("Status not 200 for workflows :O!");
|
|
return;
|
|
}
|
|
return response.json();
|
|
})
|
|
.then((responseJson) => {
|
|
if (responseJson !== undefined) {
|
|
var foundWorkflows = [{"id": "", "name": "All Workflows",}]
|
|
foundWorkflows.push(...responseJson)
|
|
setWorkflows(foundWorkflows)
|
|
|
|
loadWorkflowStats(foundWorkflows)
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.log("Error getting workflows: " + error);
|
|
})
|
|
}
|
|
|
|
// Cost in old contracts: 0.0009
|
|
// Old contracts also always included 150.000 executions
|
|
const invocationCost = includedExecutions === 150000 || includedExecutions === 250000 ? 0.0009 : typecost_single
|
|
const defaultAmount = 10000
|
|
|
|
useEffect(() => {
|
|
if (statistics === undefined || statistics === null) {
|
|
return
|
|
}
|
|
|
|
if (statistics["daily_statistics"] === undefined || statistics["daily_statistics"] === null) {
|
|
setFilteredStatistics(statistics)
|
|
return
|
|
}
|
|
|
|
// Calculate month to date cost
|
|
var mtd_cost = 0
|
|
for (let key in statistics["daily_statistics"]) {
|
|
const item = statistics["daily_statistics"][key]
|
|
if (item["date"] === undefined) {
|
|
continue
|
|
}
|
|
|
|
const date = new Date(item["date"])
|
|
const today = new Date()
|
|
if (date.getMonth() === today.getMonth()) {
|
|
mtd_cost += (item["app_executions"] * invocationCost)
|
|
}
|
|
}
|
|
|
|
if (isCloud && mtd_cost !== monthToDateCost) {
|
|
// Find how many days there have been in the current month
|
|
const today = new Date()
|
|
const daysInMonth = new Date(today.getFullYear(), today.getMonth()+1, 0).getDate()
|
|
// Find what day we are on
|
|
const day = today.getDate()
|
|
// Find how many days are left in the month
|
|
const daysLeft = daysInMonth - day
|
|
|
|
// Calculate the cost of the entire month
|
|
var monthTotalCost = mtd_cost/day*daysInMonth
|
|
monthTotalCost -= defaultAmount*invocationCost
|
|
monthTotalCost -= includedExecutions*invocationCost
|
|
|
|
// Remove included amount
|
|
//const defaultAmount = 10000
|
|
mtd_cost -= defaultAmount*invocationCost
|
|
mtd_cost -= includedExecutions*invocationCost
|
|
|
|
if (monthTotalCost > 0) {
|
|
setMonthTotalCost(monthTotalCost.toFixed(2))
|
|
}
|
|
|
|
if (mtd_cost > 0) {
|
|
setMonthToDateCost(mtd_cost.toFixed(2))
|
|
}
|
|
}
|
|
|
|
// Make a date at the 1st of the current month
|
|
var foundstarttime = (new Date())
|
|
foundstarttime.setDate(1)
|
|
if (startTime !== "" && startTime !== undefined && startTime !== null) {
|
|
foundstarttime = startTime
|
|
}
|
|
|
|
// Set to tomorrow by default
|
|
var foundendtime = (new Date())
|
|
foundendtime.setDate(foundendtime.getDate() + 1)
|
|
|
|
// Check if endtime is after the daily statistics["date"] string
|
|
if (endTime !== "" && endTime !== undefined && endTime !== null) {
|
|
foundendtime = endTime
|
|
}
|
|
|
|
// Check if start time is before the daily statistics["date"] string
|
|
var newlist = []
|
|
for (let key in statistics["daily_statistics"]) {
|
|
const item = statistics["daily_statistics"][key]
|
|
if (item["date"] === undefined) {
|
|
continue
|
|
}
|
|
|
|
const date = new Date(item["date"])
|
|
if (date >= foundstarttime) {
|
|
if (date <= foundendtime) {
|
|
newlist.push(item)
|
|
}
|
|
}
|
|
}
|
|
|
|
// If newlist is empty, set the timestamp to 1 year back and check if there are any statistics there
|
|
// If foundstarttime is more than 30 days back, don't do this
|
|
/*
|
|
if (newlist.length === 0 && foundstarttime.getDate() > 30) {
|
|
// Set the timestamp to be back
|
|
foundstarttime.setFullYear(foundstarttime.getFullYear() - 1)
|
|
setStartTime(foundstarttime)
|
|
|
|
console.log("IN HERE")
|
|
}
|
|
*/
|
|
|
|
var tmpstats = JSON.parse(JSON.stringify(statistics))
|
|
|
|
var workflowexecutions = 0
|
|
var appexecutions = 0
|
|
var estimatedcost = 0
|
|
if (newlist.length > 0) {
|
|
tmpstats["daily_statistics"] = newlist
|
|
|
|
for (let key in newlist) {
|
|
const item = newlist[key]
|
|
if (item["workflow_executions"] === undefined) {
|
|
continue
|
|
}
|
|
|
|
workflowexecutions += item["workflow_executions"]
|
|
appexecutions += item["app_executions"]
|
|
|
|
estimatedcost += (item["app_executions"] * invocationCost)
|
|
}
|
|
|
|
tmpstats["monthly_workflow_executions"] = workflowexecutions
|
|
tmpstats["monthly_app_executions"] = appexecutions
|
|
}
|
|
|
|
// Make estimatedcost have max 2 decimals
|
|
if (isCloud) {
|
|
// Exclude includedExecutions*month
|
|
// const includedExecutions = 150000
|
|
//estimatedcost -= (includedExecutions * invocationCost)
|
|
|
|
setApprunCost(estimatedcost.toFixed(2))
|
|
}
|
|
|
|
setFilteredStatistics(tmpstats)
|
|
handleDataSetting(tmpstats, "day")
|
|
|
|
|
|
if (workflows !== undefined && workflows !== null && workflows.length > 0) {
|
|
var foundWorkflows = [{"id": "", "name": "All Workflows",}]
|
|
var tmpworkflows = workflows.filter((workflow) => workflow.id !== undefined && workflow.id !== null && workflow.id !== "")
|
|
foundWorkflows.push(...tmpworkflows)
|
|
|
|
loadWorkflowStats(foundWorkflows, startTime, endTime)
|
|
}
|
|
|
|
}, [statistics, startTime, endTime])
|
|
|
|
const handleStartTimeChange = (date) => {
|
|
setStartTime(date)
|
|
}
|
|
|
|
const handleEndTimeChange = (date) => {
|
|
setEndTime(date)
|
|
}
|
|
|
|
const handleDataSetting = (inputdata, grouping) => {
|
|
if (inputdata === undefined || inputdata === null) {
|
|
return
|
|
}
|
|
|
|
const dailyStats = inputdata.daily_statistics
|
|
if (dailyStats === undefined || dailyStats === null) {
|
|
return
|
|
}
|
|
|
|
var appRuns = {
|
|
"key": "App Runs",
|
|
"data": []
|
|
}
|
|
|
|
var workflowRuns = {
|
|
"key": "Workflow Runs (includes subflows)",
|
|
"data": []
|
|
}
|
|
|
|
var subflowRuns = {
|
|
"key": "Subflow Runs",
|
|
"data": []
|
|
}
|
|
|
|
var appcostRuns = {
|
|
"key": "Cost of App Runs",
|
|
"data": []
|
|
}
|
|
|
|
for (let key in dailyStats) {
|
|
// Always skips first one as it has accumulated data in it
|
|
if (key === 0) {
|
|
continue
|
|
}
|
|
|
|
const item = dailyStats[key]
|
|
if (item["date"] === undefined) {
|
|
console.log("No date: ", item)
|
|
continue
|
|
}
|
|
|
|
// Check if app_executions key in item
|
|
if (item["app_executions"] !== undefined && item["app_executions"] !== null) {
|
|
appRuns["data"].push({
|
|
key: new Date(item["date"]),
|
|
data: item["app_executions"]
|
|
})
|
|
|
|
// Add number
|
|
appcostRuns["data"].push({
|
|
key: new Date(item["date"]),
|
|
data: (item["app_executions"] * invocationCost).toFixed(2)
|
|
})
|
|
}
|
|
|
|
// Check if workflow_executions key in item
|
|
if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) {
|
|
workflowRuns["data"].push({
|
|
key: new Date(item["date"]),
|
|
data: item["workflow_executions"]
|
|
})
|
|
}
|
|
|
|
if (item["subflow_executions"] !== undefined && item["subflow_executions"] !== null) {
|
|
subflowRuns["data"].push({
|
|
key: new Date(item["date"]),
|
|
data: item["subflow_executions"]
|
|
})
|
|
}
|
|
}
|
|
|
|
// Adds data for today
|
|
if (inputdata["daily_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) {
|
|
appRuns["data"].push({
|
|
key: new Date(),
|
|
data: inputdata["daily_app_executions"]
|
|
})
|
|
|
|
appcostRuns["data"].push({
|
|
key: new Date(),
|
|
data: (inputdata["daily_app_executions"] * invocationCost).toFixed(2)
|
|
})
|
|
}
|
|
|
|
if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) {
|
|
workflowRuns["data"].push({
|
|
key: new Date(),
|
|
data: inputdata["daily_workflow_executions"]
|
|
})
|
|
}
|
|
|
|
if (inputdata["daily_subflow_executions"] !== undefined && inputdata["daily_subflow_executions"] !== null) {
|
|
subflowRuns["data"].push({
|
|
key: new Date(),
|
|
data: inputdata["daily_subflow_executions"]
|
|
})
|
|
}
|
|
|
|
setSubflowRuns(subflowRuns)
|
|
setWorkflowRuns(workflowRuns)
|
|
setAppruns(appRuns)
|
|
setApprunCosts(appcostRuns)
|
|
}
|
|
|
|
const getStats = () => {
|
|
fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
},
|
|
credentials: "include",
|
|
})
|
|
.then((response) => {
|
|
if (response.status !== 200) {
|
|
console.log("Status not 200 for workflows :O!: ", response.status);
|
|
return;
|
|
}
|
|
|
|
return response.json();
|
|
})
|
|
.then((responseJson) => {
|
|
if (responseJson["success"] === false) {
|
|
return
|
|
}
|
|
|
|
setStatistics(responseJson)
|
|
handleDataSetting(responseJson, "day")
|
|
})
|
|
.catch((error) => {
|
|
console.log("error: ", error)
|
|
});
|
|
}
|
|
|
|
useEffect(() => {
|
|
getStats()
|
|
}, [])
|
|
|
|
const paperStyle = {
|
|
textAlign: "center",
|
|
padding: 40,
|
|
margin: 5,
|
|
backgroundColor: theme.palette.platformColor,
|
|
border: "1px solid rgba(255,255,255,0.3)",
|
|
maxWidth: 300,
|
|
}
|
|
|
|
const columns: GridColDef[] = [
|
|
{
|
|
field: 'workflow.name',
|
|
headerName: 'Workflow Name',
|
|
width: 350,
|
|
renderCell: (params) => {
|
|
|
|
return (
|
|
<span style={{cursor: "pointer", }} onClick={() => {
|
|
}}>
|
|
{params.row.name}
|
|
</span>
|
|
)
|
|
}
|
|
},
|
|
{
|
|
field: 'workflow.runcount',
|
|
headerName: 'Workflow Runs in selected period',
|
|
width: 250,
|
|
renderCell: (params) => {
|
|
|
|
return (
|
|
<span style={{cursor: "pointer", }} onClick={() => {
|
|
}}>
|
|
{params.row.runcount}
|
|
</span>
|
|
)
|
|
}
|
|
},
|
|
{
|
|
field: 'triggers',
|
|
headerName: 'Triggers',
|
|
width: 100,
|
|
renderCell: (params) => {
|
|
if (params.row.id === "") {
|
|
return null
|
|
}
|
|
|
|
const cnt = params.row.triggers === undefined || params.row.triggers === null ? 0 : params.row.triggers.length
|
|
|
|
return (
|
|
<span style={{cursor: "pointer", }} onClick={() => {
|
|
}}>
|
|
{cnt}
|
|
</span>
|
|
)
|
|
}
|
|
},
|
|
{
|
|
field: 'actions',
|
|
headerName: 'Actions',
|
|
width: 100,
|
|
renderCell: (params) => {
|
|
if (params.row.id === "") {
|
|
return null
|
|
}
|
|
|
|
const cnt = params.row.actions === undefined || params.row.actions === null ? 0 : params.row.actions.length
|
|
|
|
return (
|
|
<span style={{cursor: "pointer", }} onClick={() => {
|
|
}}>
|
|
{cnt}
|
|
</span>
|
|
)
|
|
}
|
|
},
|
|
/*{
|
|
field: 'last editor',
|
|
headerName: 'Last Editor',
|
|
width: 100,
|
|
renderCell: (params) => {
|
|
if (params.row.id === "") {
|
|
return null
|
|
}
|
|
|
|
const lastEditor = params.row.lasteditor === undefined || params.row.lasteditor === null ? "" : params.row.lasteditor
|
|
|
|
return (
|
|
<span style={{cursor: "pointer", }} onClick={() => {
|
|
}}>
|
|
{lastEditor}
|
|
</span>
|
|
)
|
|
}
|
|
},*/
|
|
{
|
|
field: 'explore',
|
|
headerName: 'Explore',
|
|
width: 100,
|
|
renderCell: (params) => {
|
|
if (params.row.id === "") {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<span style={{backgroundColor: "inherit", display: "flex", }}>
|
|
<Link disabled={params.row.id === ""} href={`/workflows/${params.row.id}`} target="_blank" rel="noopener noreferrer">
|
|
<OpenInNewIcon fontSize="small" style={{marginTop: 7, }} />
|
|
</Link>
|
|
</span>
|
|
)
|
|
}
|
|
},
|
|
]
|
|
|
|
const data = (
|
|
<div className="content" style={{width: "100%", margin: "auto", }}>
|
|
<Typography variant="body1" style={{margin: "auto", marginLeft: 10, marginBottom: 20, }} color="textSecondary">
|
|
All shown statistics are gathered from <a
|
|
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
|
|
target="_blank"
|
|
style={{ textDecoration: "none", color: "#f85a3e",}}
|
|
>Your Organisation Statistics. </a>
|
|
It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. <b>The billing tracker is in Beta, and is always calculated manually before being invoiced.</b>
|
|
</Typography>
|
|
|
|
<div style={{display: "flex", textAlign: "center",}}>
|
|
{filteredStatistics !== undefined ?
|
|
<div style={{flex: 1, display: "flex", textAlign: "center",}}>
|
|
<Tooltip title={
|
|
<Typography variant="body1" style={{padding: 10, }}>
|
|
The cost of app runs in the selected period based on {filteredStatistics.monthly_app_executions} App Runs. These numbers do not exclude your included 10.000/month or {includedExecutions} App Runs per month. App Run cost: ${invocationCost}.
|
|
</Typography>
|
|
}>
|
|
<Paper style={paperStyle}>
|
|
<Typography variant="h4">
|
|
${selectedOrganization.lead_info.customer === false && selectedOrganization.lead_info.pov === false ?
|
|
0
|
|
:
|
|
apprunCost
|
|
}
|
|
</Typography>
|
|
<Typography variant="h6">
|
|
Period Cost
|
|
</Typography>
|
|
</Paper>
|
|
</Tooltip>
|
|
<Tooltip title={
|
|
<Typography variant="body1" style={{padding: 10, }}>
|
|
App runs in the selected period
|
|
</Typography>
|
|
}>
|
|
<Paper style={paperStyle}>
|
|
<Typography variant="h4">
|
|
{filteredStatistics.monthly_app_executions === null || filteredStatistics.monthly_app_executions === undefined ? 0 : filteredStatistics.monthly_app_executions}
|
|
</Typography>
|
|
<Typography variant="h6">
|
|
App Runs
|
|
</Typography>
|
|
</Paper>
|
|
</Tooltip>
|
|
<Tooltip title={
|
|
<Typography variant="body1" style={{padding: 10, }}>
|
|
Workflow runs in the selected period
|
|
</Typography>
|
|
}>
|
|
<Paper style={paperStyle}>
|
|
<Typography variant="h4">
|
|
{filteredStatistics.monthly_workflow_executions === null || filteredStatistics.monthly_workflow_executions === undefined ? 0 : filteredStatistics.monthly_workflow_executions}
|
|
</Typography>
|
|
<Typography variant="h6">
|
|
Workflow Runs
|
|
</Typography>
|
|
</Paper>
|
|
</Tooltip>
|
|
<Tooltip title={
|
|
<Typography variant="body1" style={{padding: 10, }}>
|
|
Estimated cost to be billed at the end of the current month. Subtracted contractually included app runs. Actual cost month to date: ${monthToDateCost}. App Run cost: ${invocationCost}.
|
|
</Typography>
|
|
}>
|
|
<Paper style={{
|
|
textAlign: "center",
|
|
padding: 40,
|
|
margin: 5,
|
|
marginLeft: clickedFromOrgTab? null:90,
|
|
backgroundColor: theme.palette.platformColor,
|
|
border: "1px solid rgba(255,255,255,0.3)",
|
|
maxWidth: 300,
|
|
}}>
|
|
<Typography variant="h4">
|
|
${monthTotalCost}
|
|
</Typography>
|
|
<Typography variant="h6">
|
|
Estimated cost
|
|
</Typography>
|
|
</Paper>
|
|
</Tooltip>
|
|
</div>
|
|
: null}
|
|
|
|
<LocalizationProvider dateAdapter={AdapterDayjs} style={{flex: 1, }}>
|
|
<div style={{display: "flex", flexDirection: "column", }}>
|
|
<DateTimePicker
|
|
sx={{
|
|
marginTop: 1,
|
|
marginLeft: 1,
|
|
minWidth: 240,
|
|
maxWidth: 240,
|
|
}}
|
|
ampm={false}
|
|
label="Search from"
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
value={startTime}
|
|
onChange={handleStartTimeChange}
|
|
renderInput={(params) => <TextField {...params} />}
|
|
/>
|
|
<DateTimePicker
|
|
sx={{
|
|
marginTop: 1,
|
|
marginLeft: 1,
|
|
minWidth: 240,
|
|
maxWidth: 240,
|
|
}}
|
|
ampm={false}
|
|
label="Search until"
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
value={endTime}
|
|
onChange={handleEndTimeChange}
|
|
renderInput={(params) => <TextField {...params} />}
|
|
/>
|
|
</div>
|
|
</LocalizationProvider>
|
|
|
|
</div>
|
|
|
|
{appRuns === undefined ?
|
|
null
|
|
:
|
|
<LineChartWrapper keys={appRuns} height={300} width={"100%"} inputname={"Daily App Runs"}/>
|
|
}
|
|
|
|
{workflowRuns === undefined ?
|
|
null
|
|
:
|
|
<LineChartWrapper keys={workflowRuns} height={300} width={"100%"} inputname={"Daily Workflow Runs (including subflows)"}/>
|
|
}
|
|
|
|
{subflowRuns === undefined ?
|
|
null
|
|
:
|
|
<LineChartWrapper keys={subflowRuns} height={300} width={"100%"} inputname={"Subflow Runs"}/>
|
|
}
|
|
|
|
{/*appRunCosts === undefined ?
|
|
null
|
|
:
|
|
<LineChartWrapper keys={appRunCosts} height={300} width={"100%"} inputname={"Apprun cost - Cost per day"}/>
|
|
*/}
|
|
|
|
|
|
<div style={{height: 150+resultRows.length * 25, padding: "10px 0px 10px 0px", }}>
|
|
{resultLoading ?
|
|
<div style={{margin: "auto", alignItems: "center", width: 350, height: "100%", }}>
|
|
<Typography variant="body2" color="textSecondary" component="p" style={{textAlign: "center", marginTop: 50, marginBottom: 15, }}>
|
|
Loading usage for selected period (may take a while)
|
|
</Typography>
|
|
<CircularProgress style={{}} />
|
|
</div>
|
|
:
|
|
<DataGrid
|
|
rows={resultRows}
|
|
columns={columns}
|
|
pageSize={100}
|
|
rowsPerPageOptions={[10, 20, 50, 100]}
|
|
checkboxSelection
|
|
disableSelectionOnClick
|
|
onPageSizeChange={(newPageSize) => {
|
|
//setRowsPerPage(newPageSize)
|
|
//submitSearch(workflowId, status, startTime, endTime, rowCursor, newPageSize)
|
|
}}
|
|
// event for when clicking next page
|
|
// Hide page changer
|
|
onPageChange={(params) => {
|
|
console.log("page params: ", params)
|
|
}}
|
|
onSelectionModelChange={(newSelection) => {
|
|
console.log("newSelection: ", newSelection)
|
|
//console.log("newSelection: ", newSelection)
|
|
//setSelectedWorkflowExecutionsIndexes(newSelection)
|
|
//var found = []
|
|
//for (var i = 0; i < newSelection.length; i++) {
|
|
// // Find the workflow in the resultRows
|
|
// var selected = resultRows.find((workflow) => {
|
|
// return workflow.id === newSelection[i]
|
|
// })
|
|
|
|
// if (selected === undefined || selected === null) {
|
|
// continue
|
|
// }
|
|
|
|
// found.push(selected)
|
|
//}
|
|
|
|
//setSelectedWorkflowExecutions(found)
|
|
}}
|
|
// Track which items are selected
|
|
/>
|
|
}
|
|
</div>
|
|
</div>
|
|
)
|
|
|
|
const dataWrapper = (
|
|
<div style={{ maxWidth: 1366, margin: "auto" }}>{data}</div>
|
|
);
|
|
|
|
return dataWrapper;
|
|
}
|
|
|
|
export default AppStats;
|