Refactor and more linking

This commit is contained in:
Dunemask 2022-08-12 13:08:00 +00:00
parent f17c7e01f5
commit 2db11ac3dd
24 changed files with 197 additions and 177 deletions

View file

@ -1,7 +1,7 @@
// Import Contexts // Import Contexts
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { JobProvider } from "./ctx/JobContext.jsx"; import { JobProvider } from "@qltr/jobs";
import { StoreProvider } from "./ctx/StoreContext.jsx"; import { StoreProvider } from "@qltr/store";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
// Import Views // Import Views
import Views from "./views/Views.jsx"; import Views from "./views/Views.jsx";

View file

@ -1,5 +1,5 @@
import React, { useReducer, createContext, useMemo } from "react"; import React, { useReducer, createContext, useMemo } from "react";
import Initiator from "../../lib/sockets/clients/Initiator.js"; import Initiator from "@qltr/initiator";
const JobContext = createContext(); const JobContext = createContext();
export const jobStatus = { export const jobStatus = {

56
src/util/JobTools.jsx Normal file
View file

@ -0,0 +1,56 @@
import { useContext } from "react";
import JobContext, { jobStatus } from "@qltr/jobs";
import CheckIcon from "@mui/icons-material/Check";
import ClearIcon from "@mui/icons-material/Clear";
import ViewColumnIcon from "@mui/icons-material/ViewColumn";
import PendingIcon from "@mui/icons-material/Pending";
import VisibilityIcon from "@mui/icons-material/Visibility";
import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
import ReplayIcon from "@mui/icons-material/Replay";
function statusIcon(status) {
switch (status) {
case jobStatus.OK:
return <CheckIcon color="success" />;
case jobStatus.ERROR:
return <ClearIcon color="error" />;
case jobStatus.PENDING:
return <PendingIcon color="info" />;
case jobStatus.ACTIVE:
return <VisibilityIcon color="primary" />;
case jobStatus.CANCELED:
return <DoNotDisturbIcon color="warning" />;
case jobStatus.QUEUED:
return <ViewColumnIcon color="secondary" />;
default:
return <ReplayIcon />;
}
}
export const pipelineJobs = (pipeline, jobs) =>
jobs.filter((j) => j.isPipeline && j.pipelineId === pipeline.id);
export const useJobIconState = (job) => statusIcon(job.status);
export const usePipelineIconState = (pipeline) => {
const { state: jobState } = useContext(JobContext);
const jobStatuses = pipelineJobs(pipeline, jobState.jobs).map(
(j) => j.status
);
if (jobStatuses.includes(jobStatus.ERROR)) return statusIcon(jobStatus.ERROR);
if (jobStatuses.includes(jobStatus.ACTIVE))
return statusIcon(jobStatus.ACTIVE);
if (jobStatuses.includes(jobStatus.PENDING))
return statusIcon(jobStatus.PENDING);
if (pipeline.isCanceled) return statusIcon(jobStatus.CANCELED);
return statusIcon(jobStatus.QUEUED);
};
export const selectedPipelineBranches = (pipeline) =>
pipeline.branches.map((b) =>
b.filter((t) => pipeline.selectedBranches.includes(t))
);
export const findPipelineJobByTestName = (pipeline, jobs, testName) =>
pipelineJobs(pipeline, jobs).find((j) => j.branchId === testName);

View file

@ -1,7 +1,7 @@
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { useCurrentlyFailing } from "../Queries.jsx"; import { useCurrentlyFailing } from "@qltr/queries";
import JobContext from "../ctx/JobContext.jsx"; import JobContext from "@qltr/jobs";
import StoreContext from "../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import AppBar from "@mui/material/AppBar"; import AppBar from "@mui/material/AppBar";
@ -28,7 +28,7 @@ import SentimentSatisfiedAltIcon from "@mui/icons-material/SentimentSatisfiedAlt
const drawerWidth = 250; const drawerWidth = 250;
export default function Navbar(props) { export default function Navbar() {
const { state: jobState } = useContext(JobContext); const { state: jobState } = useContext(JobContext);
const { state: store } = useContext(StoreContext); const { state: store } = useContext(StoreContext);
const { isLoading, data: failing } = useCurrentlyFailing(); const { isLoading, data: failing } = useCurrentlyFailing();

View file

@ -2,7 +2,7 @@ import { useContext } from "react";
import { Routes, Route, Navigate } from "react-router-dom"; import { Routes, Route, Navigate } from "react-router-dom";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar"; import Toolbar from "@mui/material/Toolbar";
import StoreContext from "../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
// Import Navbar // Import Navbar
import Navbar from "./Navbar.jsx"; import Navbar from "./Navbar.jsx";
// Import Pages // Import Pages

View file

@ -1,6 +1,6 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
import { useSilencedAlerts } from "../../Queries.jsx"; import { useSilencedAlerts } from "@qltr/queries";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import SilencedBox from "./SilencedBox.jsx"; import SilencedBox from "./SilencedBox.jsx";
import SilenceDialog from "./SilenceDialog.jsx"; import SilenceDialog from "./SilenceDialog.jsx";

View file

@ -1,5 +1,5 @@
import { useState, useContext, useEffect } from "react"; import { useState, useContext, useEffect } from "react";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import useMediaQuery from "@mui/material/useMediaQuery"; import useMediaQuery from "@mui/material/useMediaQuery";
import { useTheme } from "@mui/material/styles"; import { useTheme } from "@mui/material/styles";

View file

@ -1,5 +1,5 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import Accordion from "@mui/material/Accordion"; import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary"; import AccordionSummary from "@mui/material/AccordionSummary";
@ -8,7 +8,6 @@ import Typography from "@mui/material/Typography";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import DeleteIcon from "@mui/icons-material/Delete"; import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit"; import EditIcon from "@mui/icons-material/Edit";
import Stack from "@mui/material/Stack"; import Stack from "@mui/material/Stack";
export default function SilencingBox(props) { export default function SilencingBox(props) {

View file

@ -1,13 +1,14 @@
import { useEffect, useContext } from "react"; import { useEffect, useContext } from "react";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import JobContext from "../../ctx/JobContext.jsx"; import JobContext from "@qltr/jobs";
import CatalogBox from "./CatalogBox.jsx"; import CatalogBox from "./CatalogBox.jsx";
import CatalogSearch from "./CatalogSearch.jsx"; import CatalogSearch from "./CatalogSearch.jsx";
import { useCatalogTests } from "../../Queries.jsx"; import { useCatalogTests } from "@qltr/queries";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
export default function Catalog() { export default function Catalog() {
const { state: store, updateStore } = useContext(StoreContext); const { state: store, updateStore } = useContext(StoreContext);
const { state: jobState } = useContext(JobContext);
const { isLoading, data: tests } = useCatalogTests(); const { isLoading, data: tests } = useCatalogTests();
const handleSearchChange = (e) => const handleSearchChange = (e) =>
updateStore({ catalogSearch: e.target.value }); updateStore({ catalogSearch: e.target.value });
@ -20,6 +21,31 @@ export default function Catalog() {
}; };
}, []); }, []);
const catalogWithJobs = () => {
for (var test of tests) {
if (test.isPipeline) {
const pipeline = jobState.pipelines.find((p) =>
p.selectedBranches.includes(test.name)
);
if (!pipeline) continue;
const pipelineJob = jobState.jobs.find(
(j) =>
j.isPipeline &&
j.pipelineId === pipeline.id &&
j.branchId === test.name
);
if (!pipelineJob) test.pipeline = pipeline;
test.job = pipelineJob;
continue;
}
const job = jobState.jobs.find(
(j) => !j.isPipeline && j.builderCache.testNames.includes(test.name)
);
if (job) test.job = job;
}
return tests;
};
return ( return (
<div className="catalog"> <div className="catalog">
<CatalogSearch <CatalogSearch
@ -30,7 +56,9 @@ export default function Catalog() {
<h6>{store.catalogSearch}</h6> <h6>{store.catalogSearch}</h6>
{isLoading {isLoading
? null ? null
: tests.map((v, i) => <CatalogBox key={i} catalogTest={v} />)} : catalogWithJobs().map((v, i) => (
<CatalogBox key={i} catalogTest={v} />
))}
</div> </div>
); );
} }

View file

@ -1,8 +1,9 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { usePipelineMappings } from "../../Queries.jsx"; import { usePipelineMappings } from "@qltr/queries";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import JobContext from "../../ctx/JobContext.jsx"; import JobContext, { jobStatus } from "@qltr/jobs";
import { useJobIconState, usePipelineIconState } from "@qltr/util/JobTools";
import Accordion from "@mui/material/Accordion"; import Accordion from "@mui/material/Accordion";
import AccordionDetails from "@mui/material/AccordionDetails"; import AccordionDetails from "@mui/material/AccordionDetails";
@ -11,15 +12,11 @@ import Typography from "@mui/material/Typography";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import ViewColumnIcon from "@mui/icons-material/ViewColumn";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack"; import Stack from "@mui/material/Stack";
import { import { asTree, asBranches, as1d } from "@qltr/util/pipelines.js";
selectBranch,
asTree,
asBranches,
as1d,
} from "../../../lib/jobs/pipelines.js";
export default function CatalogBox(props) { export default function CatalogBox(props) {
const { catalogTest } = props; const { catalogTest } = props;
@ -30,24 +27,28 @@ export default function CatalogBox(props) {
repo: testRepo, repo: testRepo,
isPipeline, isPipeline,
type: testType, type: testType,
job,
pipeline,
} = catalogTest; } = catalogTest;
const navigate = useNavigate(); const navigate = useNavigate();
const { data: pipelineMappings, isLoading } = usePipelineMappings(); const { data: pipelineMappings, isLoading } = usePipelineMappings();
const { state: store, updateStore } = useContext(StoreContext); const { state: store } = useContext(StoreContext);
const { state: jobState, jobFactory } = useContext(JobContext); const { jobFactory } = useContext(JobContext);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const toggleOpen = () => setOpen(!open); const toggleOpen = () => setOpen(!open);
const runTest = (e) => { const navigateToJob = () => {
e.preventDefault(); if (pipeline) return navigate(`/qualiteer/jobs#p${pipeline.id}`);
e.stopPropagation(); navigate(`/qualiteer/jobs#${job.jobId}`);
console.log(catalogTest); };
const runTest = () => {
if (isPipeline) return runPipelineTest(); if (isPipeline) return runPipelineTest();
const jobId = jobFactory({ testNames: [testName] }); const jobId = jobFactory({ testNames: [testName], isTriage: true });
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`); if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
}; };
@ -63,6 +64,20 @@ export default function CatalogBox(props) {
if (store.focusJob) navigate(`/qualiteer/jobs#p${pipeline.id}`); if (store.focusJob) navigate(`/qualiteer/jobs#p${pipeline.id}`);
}; };
const jobOnClick = (e) => {
e.preventDefault();
e.stopPropagation();
if (pipeline) return navigateToJob();
if (!job) return runTest();
navigateToJob();
};
function jobIcon() {
if (pipeline) return usePipelineIconState(pipeline);
if (!job) return <PlayArrowIcon />;
return useJobIconState(job);
}
function Actions() { function Actions() {
return ( return (
<React.Fragment> <React.Fragment>
@ -70,9 +85,9 @@ export default function CatalogBox(props) {
color="success" color="success"
aria-label="play" aria-label="play"
component="span" component="span"
onClick={runTest} onClick={jobOnClick}
> >
<PlayArrowIcon /> {jobIcon()}
</IconButton> </IconButton>
</React.Fragment> </React.Fragment>
); );
@ -98,6 +113,11 @@ export default function CatalogBox(props) {
{`${testClass}#`} {`${testClass}#`}
<Box fontWeight="bold" display="inline"> <Box fontWeight="bold" display="inline">
{testName} {testName}
{isPipeline && (
<IconButton component="span">
<ViewColumnIcon />
</IconButton>
)}
</Box> </Box>
<br /> <br />
</Typography> </Typography>
@ -119,7 +139,7 @@ export default function CatalogBox(props) {
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails>
<Typography component={"span"} style={{ wordBreak: "break-word" }}> <Typography component={"span"} style={{ wordBreak: "break-word" }}>
{JSON.stringify(catalogTest)} {"Test info"}
</Typography> </Typography>
</AccordionDetails> </AccordionDetails>
</Accordion> </Accordion>

View file

@ -1,8 +1,8 @@
import { useState, useContext } from "react"; import { useState, useContext } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useCurrentlyFailing, useSilencedAlerts } from "../../Queries.jsx"; import { useCurrentlyFailing, useSilencedAlerts } from "@qltr/queries";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import JobContext from "../../ctx/JobContext.jsx"; import JobContext from "@qltr/jobs";
import SilenceDialog from "../alerting/SilenceDialog.jsx"; import SilenceDialog from "../alerting/SilenceDialog.jsx";
import FailingBox from "./FailingBox.jsx"; import FailingBox from "./FailingBox.jsx";
@ -14,20 +14,14 @@ import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent"; import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText"; import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle"; import DialogTitle from "@mui/material/DialogTitle";
import ReplayIcon from "@mui/icons-material/Replay"; import ReplayIcon from "@mui/icons-material/Replay";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
export default function Failing() { export default function Failing() {
const { state: jobState, retryAll } = useContext(JobContext); const { state: jobState, retryAll } = useContext(JobContext);
const navigate = useNavigate(); const navigate = useNavigate();
const { const { state: store, silenceRequest } = useContext(StoreContext);
state: store,
updateStore,
silenceRequest,
} = useContext(StoreContext);
const { isLoading, data: failing } = useCurrentlyFailing(); const { isLoading, data: failing } = useCurrentlyFailing();
const { isSilencedLoading, data: silencedAlerts } = useSilencedAlerts(); const { isSilencedLoading, data: silencedAlerts } = useSilencedAlerts();
const [silenceEntry, setSilenceEntry] = useState({ open: false }); const [silenceEntry, setSilenceEntry] = useState({ open: false });

View file

@ -1,8 +1,9 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { usePipelineMappings } from "../../Queries.jsx"; import { usePipelineMappings } from "@qltr/queries";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import JobContext, { jobStatus } from "../../ctx/JobContext.jsx"; import JobContext, { jobStatus } from "@qltr/jobs";
import { useJobIconState, usePipelineIconState } from "@qltr/util/JobTools";
import Accordion from "@mui/material/Accordion"; import Accordion from "@mui/material/Accordion";
import AccordionDetails from "@mui/material/AccordionDetails"; import AccordionDetails from "@mui/material/AccordionDetails";
@ -33,12 +34,7 @@ import Badge from "@mui/material/Badge";
import Stack from "@mui/material/Stack"; import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { import { asTree, asBranches, as1d } from "@qltr/util/pipelines.js";
selectBranch,
asTree,
asBranches,
as1d,
} from "../../../lib/jobs/pipelines.js";
const stopPropagation = (e) => e.stopPropagation() && e.preventDefault(); const stopPropagation = (e) => e.stopPropagation() && e.preventDefault();
@ -112,45 +108,13 @@ export default function FailingBox(props) {
const jobOnClick = () => { const jobOnClick = () => {
if (pipeline) return navigateToJob; if (pipeline) return navigateToJob;
if (!job) return retryTest; if (!job) return retryTest;
switch (job.status) { navigateToJob();
case jobStatus.OK:
return navigateToJob;
case jobStatus.ERROR:
return retryTest;
case jobStatus.PENDING:
return navigateToJob;
case jobStatus.ACTIVE:
return navigateToJob;
case jobStatus.CANCELED:
return navigateToJob;
case jobStatus.QUEUED:
return navigateToJob;
default:
return retryTest;
}
}; };
function jobIcon() { function jobIcon() {
if (pipeline && pipeline.isCanceled) if (pipeline) return usePipelineIconState(pipeline);
return <DoNotDisturbIcon color="warning" />;
if (pipeline) return <ViewColumnIcon color="secondary" />;
if (!job) return <ReplayIcon />; if (!job) return <ReplayIcon />;
switch (job.status) { return useJobIconState(job);
case jobStatus.OK:
return <CheckIcon color="success" />;
case jobStatus.ERROR:
return <ClearIcon color="error" />;
case jobStatus.PENDING:
return <PendingIcon color="info" />;
case jobStatus.ACTIVE:
return <VisibilityIcon color="primary" />;
case jobStatus.CANCELED:
return <DoNotDisturbIcon color="warning" />;
case jobStatus.QUEUED:
return <ViewColumnIcon color="secondary" />;
default:
return <ReplayIcon />;
}
} }
function Actions() { function Actions() {

View file

@ -1,21 +1,12 @@
import React, { useState, useContext } from "react"; import React from "react";
import StoreContext from "../../ctx/StoreContext.jsx";
import JobContext, { jobStatus } from "../../ctx/JobContext.jsx";
import { useJobIconState } from "@qltr/util/JobTools";
import Accordion from "@mui/material/Accordion"; import Accordion from "@mui/material/Accordion";
import AccordionDetails from "@mui/material/AccordionDetails";
import AccordionSummary from "@mui/material/AccordionSummary"; import AccordionSummary from "@mui/material/AccordionSummary";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import CheckIcon from "@mui/icons-material/Check";
import ClearIcon from "@mui/icons-material/Clear";
import ViewColumnIcon from "@mui/icons-material/ViewColumn";
import PendingIcon from "@mui/icons-material/Pending";
import VisibilityIcon from "@mui/icons-material/Visibility";
import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack"; import Stack from "@mui/material/Stack";
export default function JobBox(props) { export default function JobBox(props) {
@ -23,25 +14,6 @@ export default function JobBox(props) {
const { name, status } = job; const { name, status } = job;
function jobIcon() {
switch (status) {
case jobStatus.OK:
return <CheckIcon color="success" />;
case jobStatus.ERROR:
return <ClearIcon color="error" />;
case jobStatus.PENDING:
return <PendingIcon color="info" />;
case jobStatus.ACTIVE:
return <VisibilityIcon color="primary" />;
case jobStatus.CANCELED:
return <DoNotDisturbIcon color="warning" />;
case jobStatus.QUEUED:
return <ViewColumnIcon color="secondary" />;
default:
return <ReplayIcon />;
}
}
return ( return (
<Accordion expanded={false} disableGutters={true} square> <Accordion expanded={false} disableGutters={true} square>
<AccordionSummary <AccordionSummary
@ -58,7 +30,7 @@ export default function JobBox(props) {
</Typography> </Typography>
<Stack sx={{ ml: "auto" }}> <Stack sx={{ ml: "auto" }}>
<IconButton aria-label="retry" component="span"> <IconButton aria-label="retry" component="span">
{jobIcon()} {useJobIconState(job)}
</IconButton> </IconButton>
</Stack> </Stack>
</AccordionSummary> </AccordionSummary>

View file

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { jobStatus } from "../../ctx/JobContext.jsx"; import { jobStatus } from "@qltr/jobs";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";

View file

@ -1,6 +1,7 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import JobContext, { jobStatus } from "../../ctx/JobContext.jsx"; import JobContext, { jobStatus } from "@qltr/jobs";
import { usePipelineIconState } from "@qltr/util/JobTools";
import Accordion from "@mui/material/Accordion"; import Accordion from "@mui/material/Accordion";
import AccordionDetails from "@mui/material/AccordionDetails"; import AccordionDetails from "@mui/material/AccordionDetails";
@ -20,10 +21,7 @@ import Stack from "@mui/material/Stack";
export default function JobPipelineBox(props) { export default function JobPipelineBox(props) {
const { pipeline } = props; const { pipeline } = props;
const pipelineIcon = usePipelineIconState(pipeline);
function jobIcon() {
return <ViewColumnIcon />;
}
return ( return (
<Accordion expanded={false} disableGutters={true} square> <Accordion expanded={false} disableGutters={true} square>
@ -41,7 +39,7 @@ export default function JobPipelineBox(props) {
</Typography> </Typography>
<Stack sx={{ ml: "auto" }}> <Stack sx={{ ml: "auto" }}>
<IconButton aria-label="" component="span"> <IconButton aria-label="" component="span">
{jobIcon()} {pipelineIcon}
</IconButton> </IconButton>
</Stack> </Stack>
</AccordionSummary> </AccordionSummary>

View file

@ -1,6 +1,12 @@
import React, { useContext } from "react"; import React, { useContext } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import JobContext, { jobStatus } from "../../ctx/JobContext.jsx"; import JobContext, { jobStatus } from "@qltr/jobs";
import {
selectedPipelineBranches,
pipelineJobs,
findPipelineJobByTestName,
useJobIconState,
} from "@qltr/util/JobTools";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import AppBar from "@mui/material/AppBar"; import AppBar from "@mui/material/AppBar";
@ -44,20 +50,8 @@ function JobPipelineDisplay(props) {
setAnchorEl(null); setAnchorEl(null);
}; };
const pipelineJobs = jobState.jobs.filter(
(j) => j.isPipeline && j.pipelineId === pipeline.id
);
const selectedBranches = () => {
return pipeline.branches.map((b) => {
return b.filter((t) => pipeline.selectedBranches.includes(t));
});
};
const findJob = (testName) =>
pipelineJobs.find((j) => j.branchId === testName);
const selectJob = (testName) => () => { const selectJob = (testName) => () => {
const job = findJob(testName); const job = findPipelineJobByTestName(pipeline, jobState.jobs, testName);
if (!job) return; if (!job) return;
navigate(`/qualiteer/jobs#${job.jobId}`); navigate(`/qualiteer/jobs#${job.jobId}`);
}; };
@ -80,31 +74,16 @@ function JobPipelineDisplay(props) {
}; };
function pipelineActive() { function pipelineActive() {
return pipelineJobs.find( return pipelineJobs(pipeline, jobState.jobs).find(
(j) => j.status === jobStatus.ACTIVE || j.status === jobStatus.PENDING (j) => j.status === jobStatus.ACTIVE || j.status === jobStatus.PENDING
); );
} }
function jobIcon(name) { function jobIcon(name) {
const job = findJob(name);
const status = job ? job.status : null;
if (pipeline.isCanceled) return <DoNotDisturbIcon color="warning" />; if (pipeline.isCanceled) return <DoNotDisturbIcon color="warning" />;
switch (status) { const job = findPipelineJobByTestName(pipeline, jobState.jobs, name);
case jobStatus.OK: if (!job) return <ViewColumnIcon color="secondary" />;
return <CheckIcon color="success" />; return useJobIconState(job);
case jobStatus.ERROR:
return <ClearIcon color="error" />;
case jobStatus.PENDING:
return <PendingIcon color="info" />;
case jobStatus.ACTIVE:
return <VisibilityIcon color="primary" />;
case jobStatus.CANCELED:
return <DoNotDisturbIcon color="warning" />;
case jobStatus.QUEUED:
return <ViewColumnIcon color="secondary" />;
default:
return <ViewColumnIcon color="secondary" />;
}
} }
return ( return (
@ -133,7 +112,7 @@ function JobPipelineDisplay(props) {
</Box> </Box>
</AppBar> </AppBar>
<Toolbar disableGutters /> <Toolbar disableGutters />
{selectedBranches().map((track, i) => ( {selectedPipelineBranches(pipeline).map((track, i) => (
<React.Fragment key={i}> <React.Fragment key={i}>
<Typography variant="h6">{i + 1}</Typography> <Typography variant="h6">{i + 1}</Typography>
<Box> <Box>

View file

@ -1,7 +1,7 @@
import React, { useContext, useState, useEffect } from "react"; import React, { useContext, useState, useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import JobContext, { jobStatus } from "../../ctx/JobContext.jsx"; import JobContext, { jobStatus } from "@qltr/jobs";
import StoreContext from "../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import AppBar from "@mui/material/AppBar"; import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar"; import Toolbar from "@mui/material/Toolbar";

View file

@ -1,7 +1,7 @@
import React, { useState, useContext, useEffect } from "react"; import React, { useState, useContext, useEffect } from "react";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import JobContext from "../../ctx/JobContext.jsx"; import JobContext from "@qltr/jobs";
import JobBox from "./JobBox.jsx"; import JobBox from "./JobBox.jsx";
import JobPipelineBox from "./JobPipelineBox.jsx"; import JobPipelineBox from "./JobPipelineBox.jsx";
import JobView from "./JobView.jsx"; import JobView from "./JobView.jsx";

View file

@ -1,7 +1,7 @@
import React, { useContext, useState, useEffect } from "react"; import React, { useContext, useState, useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import StoreContext from "../../../ctx/StoreContext.jsx"; import StoreContext from "@qltr/store";
import JobContext from "../../../ctx/JobContext.jsx"; import JobContext from "@qltr/jobs";
import Dialog from "@mui/material/Dialog"; import Dialog from "@mui/material/Dialog";
import Toolbar from "@mui/material/Toolbar"; import Toolbar from "@mui/material/Toolbar";

View file

@ -1,5 +1,5 @@
import React, { useContext } from "react"; import React from "react";
import { usePipelineMappings } from "../../../Queries.jsx"; import { usePipelineMappings } from "@qltr/queries";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import DialogActions from "@mui/material/DialogActions"; import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent"; import DialogContent from "@mui/material/DialogContent";

View file

@ -13,7 +13,7 @@ import {
asTree, asTree,
asBranches, asBranches,
as1d, as1d,
} from "../../../../lib/jobs/pipelines.js"; } from "@qltr/util/pipelines.js";
function PipelineTrackSelector(props) { function PipelineTrackSelector(props) {
const { cache, setCache, back, next } = props; const { cache, setCache, back, next } = props;

View file

@ -1,5 +1,6 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import path from "node:path";
export default () => { export default () => {
return defineConfig({ return defineConfig({
@ -10,16 +11,25 @@ export default () => {
port: 443, port: 443,
}, },
proxy: { proxy: {
'/api': 'http://localhost:52000', "/api": "http://localhost:52000",
'/socket.io': { "/socket.io": {
target: 'ws://localhost:52000', target: "ws://localhost:52000",
ws: true ws: true,
} },
} },
}, },
build: { build: {
outDir: "./build", outDir: "./build",
}, },
base: "/qualiteer/", base: "/qualiteer/",
resolve: {
alias: {
"@qltr/util": path.resolve("./src/util/"),
"@qltr/queries": path.resolve("./src/util/queries"),
"@qltr/jobs": path.resolve("./src/ctx/JobContext.jsx"),
"@qltr/store": path.resolve("./src/ctx/StoreContext.jsx"),
"@qltr/initiator": path.resolve("./lib/sockets/clients/Initiator.js"),
},
},
}); });
}; };