From 02000d7e000ed7fc9fd5759cc69d56f629e4b995 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Mon, 18 Aug 2025 13:56:42 +0530 Subject: [PATCH 1/5] chore: bump shuffle-shared --- backend/go-app/go.mod | 2 +- functions/onprem/orborus/go.mod | 2 +- functions/onprem/worker/go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 2861454a..9929130c 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -24,7 +24,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.96 + github.com/shuffle/shuffle-shared v0.8.99 github.com/shuffle/singul v0.0.15 golang.org/x/crypto v0.38.0 google.golang.org/api v0.236.0 diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index c34b4b32..8e32a56d 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -10,7 +10,7 @@ require ( github.com/docker/docker v28.2.2+incompatible github.com/docker/go-connections v0.5.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.96 + github.com/shuffle/shuffle-shared v0.8.99 k8s.io/api v0.33.1 k8s.io/apimachinery v0.33.1 ) diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index 083d46a1..7b39554b 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -10,7 +10,7 @@ require ( github.com/docker/docker v28.2.2+incompatible github.com/gorilla/mux v1.8.1 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.96 + github.com/shuffle/shuffle-shared v0.8.99 k8s.io/api v0.33.1 k8s.io/apimachinery v0.33.1 k8s.io/client-go v0.33.1 From 45ca47c28227e69bfb532142a997bea2cc409cef Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 18 Aug 2025 17:03:54 +0200 Subject: [PATCH 2/5] Many small fixes :) --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 3 +- frontend/src/components/EditWorkflow.jsx | 401 +++++++++++++++++---- frontend/src/components/EnvironmentTab.jsx | 57 +-- frontend/src/components/PartnerHeader.jsx | 4 +- frontend/src/theme.jsx | 92 ++++- frontend/src/views/LoginPage.jsx | 86 ++++- 7 files changed, 529 insertions(+), 116 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 9929130c..aa007e28 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -5,7 +5,7 @@ go 1.24.0 toolchain go1.24.3 //replace github.com/frikky/schemaless => ../../../schemaless -//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 4e4cce58..4279e19f 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4099,8 +4099,9 @@ func runInitEs(ctx context.Context) { time.Sleep(30 * time.Second) } - // FIXME: This should ONLY run on one backend instance + shuffle.InitOpensearchIndexes() + // FIXME: This should ONLY run on one backend instance. This may cause interference. schedules, err := shuffle.GetAllSchedules(ctx, "ALL") if err != nil { log.Printf("[WARNING] Failed getting schedules during service init: %s", err) diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index dbd8d3ea..00b9608d 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -63,6 +63,7 @@ import { Add as AddIcon, Remove as RemoveIcon, EditNote as EditNoteIcon, + AutoAwesome as AutoAwesomeIcon } from "@mui/icons-material"; const EditWorkflow = (props) => { @@ -72,6 +73,7 @@ const EditWorkflow = (props) => { const {themeMode, brandColor} = useContext(Context) const theme = getTheme(themeMode, brandColor) const [submitLoading, setSubmitLoading] = React.useState(false); + const [aiGenerateLoading, setAiGenerateLoading] = React.useState(false); const [showMoreClicked, setShowMoreClicked] = React.useState(isEditing !== false ? true : false); const [innerWorkflow, setInnerWorkflow] = React.useState(workflow) @@ -220,7 +222,7 @@ const EditWorkflow = (props) => {
- + {newWorkflow ? "New" : "Editing"} Workflow @@ -298,93 +300,316 @@ const EditWorkflow = (props) => { paddingLeft: 30, backgroundColor: themeMode === "dark" ? "#262626" : theme.palette.DialogStyle.backgroundColor, }}> - + + + Generate using the name, description, usecases and tags provided. Required: Name + Description + + } + > + + + + +
+ ) : ( + + // If new workflow, don't close it + if (isEditing) { + setModalOpen(false) + } + }} + color="primary" + > + {submitLoading ? : "Save Changes"} + + )}
@@ -409,6 +634,29 @@ const EditWorkflow = (props) => { id="Enter-Workflow-Name" /> + {newWorkflow === true ? + { + innerWorkflow.default_return_value = event.target.value + setInnerWorkflow(innerWorkflow) + setUpdate(Math.random()) + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + defaultValue={innerWorkflow.default_return_value} + placeholder="Please describe your workflow below so the AI can generate it." + rows="3" + multiline + label="Description" + margin="dense" + fullWidth + /> + : null} +
{usecases !== null && usecases !== undefined && usecases.length > 0 ? @@ -1285,7 +1533,7 @@ const EditWorkflow = (props) => { - {newWorkflow === true ? + {/*newWorkflow === true ? Relevant Workflows @@ -1311,9 +1559,8 @@ const EditWorkflow = (props) => { } - : null} - - {/*newWorkflow === true && name.length > 2 ? + : null*/} + {/*newWorkflow === true && name.length > 2 ?
{ //toast("No Orborus necessary for environment cloud. Create and use a different environment to run executions on-premises.",) return } - + if ( props.userdata.active_org === undefined || props.userdata.active_org === null ) { return; } - + const elementName = "copy_element_shuffle"; var auth = environment.auth === "" ? "cb5st3d3Z!3X3zaJ*Pc" : environment.auth - - // Escape exclamation marks for copying - auth = auth.replace("\\!", "!").replace(/!/g, "\\!") - + // Escape exclamation marks for copying + auth = auth.replace("\\!", "!").replace(/!/g, "\\!") const newUrl = globalUrl === "https://shuffler.io" ? "https://shuffle-backend-stbuwivzoq-nw.a.run.app" : globalUrl; - + var skipPipeline = false if (commandController.pipelines === true) { skipPipeline = true } - + var addProxy = false if (commandController.proxies === true) { addProxy = true } - + if (installationTab === 1) { return (`docker run -d \\ --restart=always \\ @@ -428,14 +426,32 @@ const EnvironmentTab = memo((props) => { -e SHUFFLE_SWARM_CONFIG=run \\ -e SHUFFLE_LOGS_DISABLED=true \\ -e BASE_URL="${newUrl}" \\${addProxy ? ` - -e HTTPS_PROXY=IP:PORT \\` : ""}${skipPipeline ? ` - -e SHUFFLE_SKIP_PIPELINES=true \\` : ""} + -e HTTPS_PROXY=IP:PORT \\` : ""}${skipPipeline ? ` + -e SHUFFLE_SKIP_PIPELINES=true \\` : ""} ghcr.io/shuffle/shuffle-orborus:latest `) } else if (installationTab === 2) { - return `https://shuffler.io/docs/configuration#kubernetes` + return `helm install shuffle-orborus oci://ghcr.io/shuffle/charts/shuffle \\ + --namespace shuffle --create-namespace \\ + --set opensearch.enabled=false \\ + --set backend.replicaCount=0 \\ + --set frontend.replicaCount=0 \\ + --set 'orborus.extraEnvVars[0].name=AUTH' \\ + --set 'orborus.extraEnvVars[0].value=${auth}' \\ + --set 'orborus.extraEnvVars[1].name=BASE_URL' \\ + --set 'orborus.extraEnvVars[1].value=${newUrl}' \\ + --set 'orborus.extraEnvVars[2].name=ENVIRONMENT_NAME' \\ + --set 'orborus.extraEnvVars[2].value=${environment.Name}' \\ + --set 'orborus.extraEnvVars[3].name=ORG' \\ + --set 'orborus.extraEnvVars[3].value=${environment.org_id}' \\ + --set shuffle.org="${environment.org_id}"${addProxy ? ` \\ + --set 'orborus.extraEnvVars[4].name=HTTPS_PROXY' \\ + --set 'orborus.extraEnvVars[4].value=IP:PORT'` : ""}${skipPipeline ? ` \\ + --set 'orborus.extraEnvVars[${addProxy ? 5 : 4}].name=SHUFFLE_SKIP_PIPELINES' \\ + --set 'orborus.extraEnvVars[${addProxy ? 5 : 4}].value=true'` : ""} \\ + --set persistence.storageClass=standard` } - + const commandData = `docker rm shuffle-orborus --force; \\\ndocker run -d \\ --restart=always \\ --name="shuffle-orborus" \\ @@ -445,12 +461,13 @@ const EnvironmentTab = memo((props) => { -e ENVIRONMENT_NAME="${environment.Name}" \\ -e ORG="${props.userdata.active_org.id}" \\ -e BASE_URL="${newUrl}" \\${addProxy ? ` - -e HTTPS_PROXY=IP:PORT \\` : ""}${skipPipeline ? ` - -e SHUFFLE_SKIP_PIPELINES=true \\` : ""} + -e HTTPS_PROXY=IP:PORT \\` : ""}${skipPipeline ? ` + -e SHUFFLE_SKIP_PIPELINES=true \\` : ""} ghcr.io/shuffle/shuffle-orborus:latest` - + return commandData - }; + }; + const submitEnvironment = (data) => { // FIXME - add some check here ROFL @@ -1475,7 +1492,7 @@ const EnvironmentTab = memo((props) => { {installationTab === 2 ? - Check our Kubernetes documentation for more information on how to run Shuffle on Kubernetes. The status of the node will change when connected. + Simply connect to your Kubernetes cluster and run the following command: : @@ -1489,7 +1506,6 @@ const EnvironmentTab = memo((props) => { "2. Run this command on the server you want to run workflows or store Pipeline data on"} - {installationTab === 2 ? null :
{ }} />
-
- } +
{installationTab === 2 ? null : diff --git a/frontend/src/components/PartnerHeader.jsx b/frontend/src/components/PartnerHeader.jsx index 8cb84c0a..3a6a910b 100644 --- a/frontend/src/components/PartnerHeader.jsx +++ b/frontend/src/components/PartnerHeader.jsx @@ -741,7 +741,7 @@ const PartnerHeader = (props) => {
From 315d65898b8ae5acf5f251ed9bcd5059f06fe66a Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 18 Aug 2025 17:17:33 +0200 Subject: [PATCH 3/5] Added Opensearch index control and rollover config control --- backend/go-app/go.mod | 4 +- backend/go-app/go.sum | 4 +- frontend/src/components/HealthBarChart.jsx | 103 ++++++-- frontend/src/components/HealthPage.jsx | 286 ++++++++++++++++----- 4 files changed, 317 insertions(+), 80 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index aa007e28..7c857d01 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -5,7 +5,7 @@ go 1.24.0 toolchain go1.24.3 //replace github.com/frikky/schemaless => ../../../schemaless -replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared +//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi @@ -24,7 +24,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.8.99 + github.com/shuffle/shuffle-shared v0.9.0 github.com/shuffle/singul v0.0.15 golang.org/x/crypto v0.38.0 google.golang.org/api v0.236.0 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 51d36d6e..163f7931 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -359,8 +359,8 @@ github.com/sendgrid/sendgrid-go v3.16.1+incompatible h1:zWhTmB0Y8XCDzeWIm2/BIt1G github.com/sendgrid/sendgrid-go v3.16.1+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shuffle/shuffle-shared v0.8.91 h1:ZcxSKhns96w1GQG/EjEJSLBvuEA3KVjvORAetV3W4bM= -github.com/shuffle/shuffle-shared v0.8.91/go.mod h1:PFLG4eaxz8zQc9Lbc0Ok/nG8rNsaBkZ0TNar+VU+J/g= +github.com/shuffle/shuffle-shared v0.9.0 h1:hUNtW3qHkji2cUVyR1cIXAz6qxpAHHP5hBmSAe3pIhs= +github.com/shuffle/shuffle-shared v0.9.0/go.mod h1:PFLG4eaxz8zQc9Lbc0Ok/nG8rNsaBkZ0TNar+VU+J/g= github.com/shuffle/singul v0.0.15 h1:a5Qro1BKs9a+mWE289oYTkDeVFoKEd/H9qu9XJGsVxo= github.com/shuffle/singul v0.0.15/go.mod h1:z1RRZsymTYxsB8WMQwwswypruy4j5EugGPFEZspTEA4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/frontend/src/components/HealthBarChart.jsx b/frontend/src/components/HealthBarChart.jsx index a4f05273..a5d1dec4 100644 --- a/frontend/src/components/HealthBarChart.jsx +++ b/frontend/src/components/HealthBarChart.jsx @@ -1,22 +1,93 @@ -import React from 'react'; -import { Bar } from 'react-chartjs-2'; +import React, { useState } from 'react'; const HealthBarChart = (props) => { - const { globalUrl, filteredData, options, onBarClick } = props; - - return ( - { - if (elements && elements.length > 0) { - onBarClick(elements); - } + const { filteredData, onBarClick } = props; + const [hoveredBar, setHoveredBar] = useState(null); + + if (!filteredData || !Array.isArray(filteredData)) { + console.error('Invalid chart data format'); + return null; + } + + const formatData = () => { + return filteredData.map((item) => ({ + label: item.date, + value: item.avgRunFinished, + color: item.color, + executionIds: item.executionIds, + })); + }; + + const handleBarClick = (data) => { + if (onBarClick) { + onBarClick(data); + } + }; + + const chartData = formatData(); + + const barWidth = `calc((100% - ${(chartData.length - 1) * 5}px) / ${chartData.length})`; + const barGap = '5px'; + + return ( +
+ {/* Chart Container */} +
+ {chartData.map((item, index) => ( +
- ); + onMouseEnter={() => setHoveredBar(item)} + onMouseLeave={() => setHoveredBar(null)} + onClick={() => handleBarClick(item)} + /> + ))} +
+ + {/* Tooltip */} + {hoveredBar && ( +
+
Date: {hoveredBar.label}
+
Uptime: {hoveredBar.value}%
+
Executions: {hoveredBar.executionIds.length}
+
+ )} +
+ ); }; export default HealthBarChart; diff --git a/frontend/src/components/HealthPage.jsx b/frontend/src/components/HealthPage.jsx index fcadb776..660719e0 100644 --- a/frontend/src/components/HealthPage.jsx +++ b/frontend/src/components/HealthPage.jsx @@ -1,31 +1,35 @@ import React, { useEffect, useState, useCallback } from 'react'; -import { Bar } from 'react-chartjs-2'; -//import healthData1 from '../healthstats1.json'; -import { toast } from "react-toastify" -import CheckOutlinedIcon from '@mui/icons-material/CheckOutlined'; +import { toast } from "react-toastify"; +import { + CheckOutlined as CheckOutlinedIcon, +} from '@mui/icons-material'; + import { Button, ButtonGroup, Typography, + LinearProgress, } from "@mui/material"; + import HealthBarChart from '../components/HealthBarChart.jsx'; +import LiveExecutionsChart from '../components/LiveExecutionsGraph.jsx'; const HealthPage = (props) => { - const { globalUrl, userdata } = props; + const { userdata, globalUrl } = props; const [healthData, setHealthData] = useState(null); const [selectedRange, setSelectedRange] = useState('30d'); + const [liveExecutionsData, setLiveExecutionsData] = useState([]); const [filteredData, setFilteredData] = useState([]); const [averageUptime, setAverageUptime] = useState(0); + const [liveExecutionsRange, setLiveExecutionsRange] = useState('1h'); // Default to 1h + const [isHealthLoading, setIsHealthLoading] = useState(false); // Loading state for HealthBarChart + const [isLiveExecutionsLoading, setIsLiveExecutionsLoading] = useState(false); // Loading state for LiveExecutionsChart - const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - //const globalUrl = `https://shuffler.io` - - console.log("HEALTHPAGE 1") + const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); const fetchHealthStats = useCallback(async () => { - //const after = new Date().getTime() - 90 * 24 * 60 * 60 + setIsHealthLoading(true); // Start loading for HealthBarChart try { - //const response = await fetch(`${globalUrl}/api/v1/health/stats?after=${after}`, { const response = await fetch(`${globalUrl}/api/v1/health/stats`, { method: "GET", headers: { @@ -42,31 +46,118 @@ const HealthPage = (props) => { } catch (error) { console.error("Error fetching health stats:", error); toast.error("Failed loading health stats"); + } finally { + setIsHealthLoading(false); // Stop loading for HealthBarChart + } + }, [globalUrl]); + + const fetchLiveExecutions = useCallback(async (range = '1h') => { + setIsLiveExecutionsLoading(true); // Start loading for LiveExecutionsChart + try { + const fetchOptions = { + method: "GET", + credentials: "include", + }; + + if (window.location.host !== "localhost:3002") { + fetchOptions.headers = { + "Content-Type": "application/json", + Accept: "application/json", + }; + } + + const now = Math.floor(Date.now() / 1000); + // let after = now - 3600; // Default to 1h + let mode = "" + + switch (range) { + case '1h': + mode = "1h" + break; + case '7h': + mode = "7h" + break; + case '1d': + mode = "1d" + break; + case '7d': + mode = "7d" + break; + case 'month': + mode = "month" + break; + default: + mode = "1h" + } + + if (!userdata.support_access) { + return + } + + const response = await fetch( + `${globalUrl}/api/v1/health/executions/live?mode=${mode}`, + fetchOptions + ); + + if (!response.ok) { + throw new Error("Failed to fetch live executions"); + } + + const data = await response.json(); + console.log("Raw live executions data:", data); + + if (Array.isArray(data)) { + const formattedData = data + .map(item => ({ + ...item, + // failed: Number(item.failed) || 0, + executing: Number(item.executing) || 0, + finished: Number(item.finished) || 0, + aborted: Number(item.aborted) || 0, + created_at: Number(item.created_at) || 0 + })) + .sort((a, b) => a.created_at - b.created_at); + + console.log("Formatted live executions data:", formattedData); + setLiveExecutionsData(formattedData); + } else { + console.error("Received invalid data format:", data); + setLiveExecutionsData([]); + } + } catch (error) { + console.error("Error fetching live executions:", error); + toast.error("Failed loading live executions data"); + } finally { + setIsLiveExecutionsLoading(false); // Stop loading for LiveExecutionsChart } }, [globalUrl]); useEffect(() => { fetchHealthStats(); - }, [fetchHealthStats]); + fetchLiveExecutions(liveExecutionsRange); + + const interval = setInterval(() => fetchLiveExecutions(liveExecutionsRange), 60000); + return () => clearInterval(interval); + }, [fetchHealthStats, fetchLiveExecutions, liveExecutionsRange]); const extractRunFinished = (data, range) => { if (!data || !Array.isArray(data)) return []; - + const currentDate = new Date().getTime(); const rangeInMillis = { '24hr': 24 * 60 * 60 * 1000, '7day': 7 * 24 * 60 * 60 * 1000, '30d': 30 * 24 * 60 * 60 * 1000, '90d': 90 * 24 * 60 * 60 * 1000 - } - const filteredData = data.filter(item => currentDate - item.updated * 1000 <= rangeInMillis[range]) - - const aggregatedData = new Map() - + }; + const filteredData = data.filter(item => currentDate - item.updated * 1000 <= rangeInMillis[range]); + + const aggregatedData = new Map(); + filteredData.forEach(item => { const timestamp = item.updated * 1000; // Convert Unix timestamp to milliseconds let key; - + switch (range) { case '24hr': const date = new Date(timestamp); @@ -83,7 +174,7 @@ const HealthPage = (props) => { default: key = new Date(timestamp).toLocaleDateString(); } - + // Check if date already exists in the map if (aggregatedData.has(key)) { // Update aggregated values @@ -100,13 +191,13 @@ const HealthPage = (props) => { }); } }); - + // Calculate averages and assign colors const result = Array.from(aggregatedData.entries()).map(([key, { totalEntries, totalRunFinished, executionIds }]) => { const avg = totalEntries > 0 ? totalRunFinished / totalEntries : 0; const FinalAvg = avg * 100; let color; - + if (FinalAvg >= 100) { color = '#00F670'; } else if (FinalAvg >= 98.50 && FinalAvg <= 99.99) { @@ -114,7 +205,7 @@ const HealthPage = (props) => { } else if (FinalAvg <= 98.49) { color = '#FF354C'; } - + return { date: range === '24hr' ? `${key}:00` : key, avgRunFinished: FinalAvg, @@ -122,11 +213,10 @@ const HealthPage = (props) => { executionIds }; }); - + return result; }; - - + useEffect(() => { if (healthData) { const newData = extractRunFinished(healthData, selectedRange); @@ -160,19 +250,19 @@ const HealthPage = (props) => { const labels = filteredData.map((value, i) => { if (selectedRange === '24hr') { const [datePart, hourPart] = value.date.split(' '); - const [day, month, year] = datePart.split('/'); + const [month, day, year] = datePart.split('/'); const monthIndex = parseInt(month, 10) - 1; const date = new Date(year, monthIndex, day); let formattedDate = `${date.toLocaleString('en-US', { month: 'short', day: '2-digit' })}`; - // Add hour part if available if (hourPart) { formattedDate += `, ${hourPart.split(':').slice(0, 2).join(':')}`; } + return `${formattedDate} \nUptime: ${value.avgRunFinished.toFixed(2)}%`; } else if (selectedRange === '7day') { const [datePart, hourPart] = value.date.split(' '); - const [day, month, year] = datePart.split('/'); + const [month, day, year] = datePart.split('/'); const monthIndex = parseInt(month, 10) - 1; const date = new Date(year, monthIndex, day); let formattedDate = `${date.toLocaleString('en-US', { month: 'short', day: '2-digit' })}`; @@ -185,12 +275,12 @@ const HealthPage = (props) => { } else { const dateParts = value.date.split('/'); // Assuming the date format is "DD/MM/YYYY" - const date = new Date(`${dateParts[2]}-${dateParts[1]}-${dateParts[0]}`); // Reformat the date string to "YYYY-MM-DD" + const date = new Date(`${dateParts[2]}-${dateParts[0]}-${dateParts[1]}`); // Reformat the date string to "YYYY-MM-DD" + return `${date.toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' })} \nUptime: ${value.avgRunFinished.toFixed(2)}%`; } }); - if (selectedRange === '24hr') { barThickness = 35; } @@ -200,12 +290,6 @@ const HealthPage = (props) => { else if (selectedRange === '30d') { barThickness = 25; } - // let width = 800; // Default chart width - // if (selectedRange === '7day') { - // width = 400; // Adjust chart width for 7 days - // } else if (selectedRange === '30d') { - // width = 600; // Adjust chart width for 30 days - // } const datasets = [{ label: "", @@ -213,14 +297,11 @@ const HealthPage = (props) => { backgroundColor: filteredData.map(item => item.color), borderWidth: 1, barThickness: barThickness, - // barPercentage: barPercentage, }]; return { labels, datasets }; }; - console.log("HEALTHPAGE 2") - const options = { legend: { display: false @@ -253,12 +334,11 @@ const HealthPage = (props) => { }, afterLabel: function (tooltipItem, data) { const label = data.labels[tooltipItem.index]; - // console.log(label) const uptime = label.match(/Uptime:\s*(\d+(?:\.\d+)?)/)[1]; // Extract uptime value using regex - return `Success Rate: ${uptime}%`; // Customize the uptime display + return `Test-Workflow Health: ${uptime}%`; // Customize the uptime display }, title: function () { - return 'Fully Oprational'; // Hide the tooltip title + return 'Fully Operational'; // Hide the tooltip title } } } @@ -284,32 +364,118 @@ const HealthPage = (props) => { } }; + const healthBarData = updateChartData() + return ( -
- +
+ {/* Health Bar Chart Section */} - - - - - + + + + + -
-
- -
- Workflow Health - Operational + {/* Loading Bar for HealthBarChart */} + {isHealthLoading && ( + + )} + +
+
+
+ +
+ Workflow Health + Operational +
-
- {averageUptime.toFixed(2)}% - Success Rate +
+ {averageUptime.toFixed(2)}% + Success Rate
- +
+ + {userdata.support_access && ( +
+
+ Live Executions + + + + + + {/* */} + +
+ {/* Loading Bar for LiveExecutionsChart */} + {isLiveExecutionsLoading && ( + + )} + +
+ )} + +
); }; From 3bf5ac7400c20207265508c76abbf7b7d0e8af66 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 18 Aug 2025 17:20:51 +0200 Subject: [PATCH 4/5] Fix backend failure again --- backend/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 9e27ffbe..16ffeff0 100755 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -27,7 +27,7 @@ FROM alpine:latest as certs RUN apk add --update ca-certificates # Sets up the final image -FROM alpine:3.21.2 +FROM alpine:3.22.1 # FIXME: Install cgo because CGO_ENABLED=1 during build RUN apk add --no-cache libc6-compat From 0bf0736d07fdf16803c9a17308dc961d05a94bc4 Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 18 Aug 2025 19:15:47 +0200 Subject: [PATCH 5/5] Rebuild with all components --- frontend/src/components/LiveExecutionsGraph.jsx | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 frontend/src/components/LiveExecutionsGraph.jsx diff --git a/frontend/src/components/LiveExecutionsGraph.jsx b/frontend/src/components/LiveExecutionsGraph.jsx new file mode 100644 index 00000000..ac572e84 --- /dev/null +++ b/frontend/src/components/LiveExecutionsGraph.jsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const LiveExecutionsGraph = ({ executions }) => { + return null +} + +export default LiveExecutionsGraph