Upgrades people!
This commit is contained in:
parent
f84234150f
commit
8ad56e8d38
40 changed files with 483 additions and 379 deletions
|
@ -1,24 +1,24 @@
|
|||
// Import Contexts
|
||||
import { QueryClient, QueryClientProvider} from '@tanstack/react-query'
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { JobProvider } from "./ctx/JobContext.jsx";
|
||||
import { StoreProvider } from "./ctx/StoreContext.jsx";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
// Import Views
|
||||
import Views from "./views/Views.jsx";
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<div className="qualiteer">
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<StoreProvider>
|
||||
<JobProvider>
|
||||
<BrowserRouter>
|
||||
<Views />
|
||||
</BrowserRouter>
|
||||
</JobProvider>
|
||||
</StoreProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<StoreProvider>
|
||||
<JobProvider>
|
||||
<BrowserRouter>
|
||||
<Views />
|
||||
</BrowserRouter>
|
||||
</JobProvider>
|
||||
</StoreProvider>
|
||||
</QueryClientProvider>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
const QUALITEER_URL = "https://qualiteer.elijahparker3.repl.co/api";
|
||||
|
||||
const useMock = false;
|
||||
const useMock = true;
|
||||
|
||||
const asMock = (data) => ({ data });
|
||||
|
||||
const fetchApi = (subPath)=> async ()=> fetch(`${QUALITEER_URL}${subPath}`).then((res)=>res.json());
|
||||
const fetchApi = (subPath) => async () =>
|
||||
fetch(`${QUALITEER_URL}${subPath}`).then((res) => res.json());
|
||||
|
||||
export const useCatalogTests = () => useMock ? asMock([]): useQuery(['catalogTests'], fetchApi("/catalog/tests")
|
||||
)
|
||||
export const useCatalogTests = () =>
|
||||
useMock ? asMock([]) : useQuery(["catalogTests"], fetchApi("/catalog/tests"));
|
||||
|
||||
export const usePipelineMappings = () => useMock ? asMock([
|
||||
["primary", "secondary1", "tertiary1"],
|
||||
["primary", "secondary1", "tertiary2"],
|
||||
["primary", "secondary2", "tertiary3"],
|
||||
]) : useQuery(['pipelineMappings'], fetchApi("/catalog/pipeline-mappings")
|
||||
)
|
||||
export const usePipelineMappings = () =>
|
||||
useMock
|
||||
? asMock([
|
||||
["primary", "secondary1", "tertiary1"],
|
||||
["primary", "secondary1", "tertiary2"],
|
||||
["primary", "secondary2", "tertiary3"],
|
||||
])
|
||||
: useQuery(["pipelineMappings"], fetchApi("/catalog/pipeline-mappings"));
|
||||
|
||||
export const useSilencedAlerts = () => useMock? asMock([]) : useQuery(['silenced'], fetchApi("/alerting/silenced")
|
||||
)
|
||||
export const useSilencedAlerts = () =>
|
||||
useMock ? asMock([]) : useQuery(["silenced"], fetchApi("/alerting/silenced"));
|
||||
|
||||
export const useCurrentlyFailing = () => useMock? asMock([]) : useQuery(['failing'], fetchApi("/results/failing")
|
||||
)
|
||||
export const useCurrentlyFailing = () =>
|
||||
useMock ? asMock([]) : useQuery(["failing"], fetchApi("/results/failing"));
|
||||
|
|
|
@ -15,7 +15,7 @@ const ACTIONS = {
|
|||
CREATE: "c",
|
||||
UPDATE: "u",
|
||||
DELETE: "d",
|
||||
PIPELINE: "p"
|
||||
PIPELINE: "p",
|
||||
};
|
||||
|
||||
const url = "https://qualiteer.elijahparker3.repl.co/";
|
||||
|
@ -44,7 +44,9 @@ const reducer = (state, action) => {
|
|||
return { ...state, jobs };
|
||||
|
||||
case ACTIONS.UPDATE:
|
||||
jobIndex = jobs.findIndex((j) => j.jobId === (action.job.jobId ?? action.jobId));
|
||||
jobIndex = jobs.findIndex(
|
||||
(j) => j.jobId === (action.job.jobId ?? action.jobId)
|
||||
);
|
||||
jobs[jobIndex] = { ...jobs[jobIndex], ...action.job };
|
||||
return { ...state, jobs };
|
||||
|
||||
|
@ -54,7 +56,7 @@ const reducer = (state, action) => {
|
|||
return { ...state, jobs };
|
||||
|
||||
case ACTIONS.PIPELINE:
|
||||
return {...state, pipelines};
|
||||
return { ...state, pipelines };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -69,25 +71,26 @@ export const JobProvider = ({ children }) => {
|
|||
dispatch({ type: ACTIONS.CREATE, job: { ...job, log: [] } });
|
||||
const jobDelete = (jobId) => dispatch({ type: ACTIONS.DELETE, jobId });
|
||||
|
||||
const updatePipelines = (pipelines) => dispatch({type: ACTIONS.pipeline, pipelines});
|
||||
|
||||
const updatePipelines = (pipelines) =>
|
||||
dispatch({ type: ACTIONS.pipeline, pipelines });
|
||||
|
||||
function retryAll(failing) {
|
||||
// Query Full Locator
|
||||
console.log("Would retry all failing tests!");
|
||||
return jobFactory({ testNames: ["single"] });
|
||||
}
|
||||
|
||||
function pipelineComponentJob(jobPipeline, testName){
|
||||
function pipelineComponentJob(jobPipeline, testName) {
|
||||
const i = new Initiator(url);
|
||||
const jobId = `j${Date.now()}`;
|
||||
const job = {
|
||||
const job = {
|
||||
name: jobId,
|
||||
status: jobStatus.PENDING,
|
||||
jobId,
|
||||
isPipeline: true,
|
||||
builderCache: {},
|
||||
initiator: i,
|
||||
}
|
||||
};
|
||||
const request = {
|
||||
testName,
|
||||
image: "node",
|
||||
|
@ -95,9 +98,9 @@ export const JobProvider = ({ children }) => {
|
|||
name: jobId,
|
||||
pipelineTriggers: "secondary",
|
||||
};
|
||||
|
||||
|
||||
jobCreate(job);
|
||||
const onLog = (d) => {
|
||||
const onLog = (d) => {
|
||||
const job = state.jobs.find((j) => j.jobId === jobId);
|
||||
job.log.push(d);
|
||||
job.status = jobStatus.ACTIVE;
|
||||
|
@ -112,41 +115,48 @@ export const JobProvider = ({ children }) => {
|
|||
};
|
||||
const onPipelineTrigger = (trigger) => {
|
||||
console.log("Got trigger", trigger);
|
||||
}
|
||||
const started = i.newPipelineJob(request, onLog, onClose, onPipelineTrigger);
|
||||
started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId));
|
||||
|
||||
};
|
||||
const started = i.newPipelineJob(
|
||||
request,
|
||||
onLog,
|
||||
onClose,
|
||||
onPipelineTrigger
|
||||
);
|
||||
started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId));
|
||||
}
|
||||
|
||||
|
||||
function pipelineFactory(builderCache) {
|
||||
console.log("Would create pipeline with cache");
|
||||
console.log(builderCache);
|
||||
return pipelineComponentJob(state.pipelines, builderCache.branches[0][0]);
|
||||
// return jobId;
|
||||
// return jobId;
|
||||
/*return jobFactory({testNames: ["primary"]});*/
|
||||
}
|
||||
|
||||
|
||||
function jobCancel(jobId){
|
||||
const job = state.jobs.find((j)=>j.jobId===jobId);
|
||||
|
||||
if(job.initiator.sk) job.initiator.sk.close();
|
||||
function jobCancel(jobId) {
|
||||
const job = state.jobs.find((j) => j.jobId === jobId);
|
||||
|
||||
if (job.initiator.sk) job.initiator.sk.close();
|
||||
job.status = jobStatus.CANCELED;
|
||||
jobUpdate({ ...job }, jobId);
|
||||
|
||||
}
|
||||
|
||||
function jobDestroy(jobId){
|
||||
const job = state.jobs.find((j)=>j.jobId===jobId);
|
||||
|
||||
if(job.initiator.sk && job.status !== jobStatus.OK && job.status !== jobStatus.ERROR && job.status !== jobStatus.CANCELED){
|
||||
job.initiator.sk.close();
|
||||
function jobDestroy(jobId) {
|
||||
const job = state.jobs.find((j) => j.jobId === jobId);
|
||||
|
||||
if (
|
||||
job.initiator.sk &&
|
||||
job.status !== jobStatus.OK &&
|
||||
job.status !== jobStatus.ERROR &&
|
||||
job.status !== jobStatus.CANCELED
|
||||
) {
|
||||
job.initiator.sk.close();
|
||||
}
|
||||
jobDelete(jobId);
|
||||
}
|
||||
|
||||
function jobFactory(builderCache) {
|
||||
if(builderCache.tree) return pipelineFactory(builderCache);
|
||||
if (builderCache.tree) return pipelineFactory(builderCache);
|
||||
// Find test
|
||||
const i = new Initiator(url);
|
||||
const jobId = `j${Date.now()}`;
|
||||
|
@ -184,7 +194,7 @@ export const JobProvider = ({ children }) => {
|
|||
|
||||
const started = i.newJob(request, onLog, onClose);
|
||||
started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId));
|
||||
|
||||
|
||||
return jobId;
|
||||
}
|
||||
|
||||
|
@ -197,7 +207,7 @@ export const JobProvider = ({ children }) => {
|
|||
retryAll,
|
||||
jobFactory,
|
||||
jobCancel,
|
||||
jobDestroy
|
||||
jobDestroy,
|
||||
};
|
||||
const contextValue = useMemo(() => context, [state, dispatch]);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useContext, useState } from "react";
|
||||
import {useCurrentlyFailing} from "../Queries.jsx";
|
||||
import { useCurrentlyFailing } from "../Queries.jsx";
|
||||
import JobContext from "../ctx/JobContext.jsx";
|
||||
import StoreContext from "../ctx/StoreContext.jsx";
|
||||
|
||||
|
@ -24,18 +24,22 @@ import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted";
|
|||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
import WarningIcon from "@mui/icons-material/Warning";
|
||||
import InfoIcon from "@mui/icons-material/Info";
|
||||
import SentimentSatisfiedAltIcon from '@mui/icons-material/SentimentSatisfiedAlt';
|
||||
import SentimentSatisfiedAltIcon from "@mui/icons-material/SentimentSatisfiedAlt";
|
||||
|
||||
const drawerWidth = 250;
|
||||
|
||||
export default function Navbar(props) {
|
||||
const { state: jobState } = useContext(JobContext);
|
||||
const { state: store } = useContext(StoreContext);
|
||||
const {isLoading, data: failing} = useCurrentlyFailing();
|
||||
const { isLoading, data: failing } = useCurrentlyFailing();
|
||||
const pages = store.pages;
|
||||
const icons = [
|
||||
<Badge badgeContent={(failing ?? []).length} color="error">
|
||||
{(failing ?? []).length > 0 ? <WarningIcon />: <SentimentSatisfiedAltIcon/>}
|
||||
{(failing ?? []).length > 0 ? (
|
||||
<WarningIcon />
|
||||
) : (
|
||||
<SentimentSatisfiedAltIcon />
|
||||
)}
|
||||
</Badge>,
|
||||
<NotificationsIcon />,
|
||||
<Badge badgeContent={jobState.jobs.length} color="primary">
|
||||
|
|
|
@ -33,7 +33,9 @@ export default function Views() {
|
|||
<Route
|
||||
exact
|
||||
path="/"
|
||||
element={<Navigate to={`/qualiteer/${store.defaultPage}`} replace />}
|
||||
element={
|
||||
<Navigate to={`/qualiteer/${store.defaultPage}`} replace />
|
||||
}
|
||||
/>
|
||||
<Route path="/qualiteer/failing" element={<Failing />} />
|
||||
<Route path="/qualiteer/alerting" element={<Alerting />} />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useContext } from "react";
|
||||
import {useSilencedAlerts} from "../../Queries.jsx";
|
||||
import { useSilencedAlerts } from "../../Queries.jsx";
|
||||
import StoreContext from "../../ctx/StoreContext.jsx";
|
||||
import SilencedBox from "./SilencedBox.jsx";
|
||||
import SilenceDialog from "./SilenceDialog.jsx";
|
||||
|
@ -18,13 +18,9 @@ import Typography from "@mui/material/Typography";
|
|||
import Box from "@mui/material/Box";
|
||||
|
||||
export default function Alerting() {
|
||||
const {
|
||||
updateStore,
|
||||
silenceRequest,
|
||||
} = useContext(StoreContext);
|
||||
const { updateStore, silenceRequest } = useContext(StoreContext);
|
||||
|
||||
|
||||
const {isLoading, data: silenced} = useSilencedAlerts();
|
||||
const { isLoading, data: silenced } = useSilencedAlerts();
|
||||
|
||||
const [silenceEntry, setSilenceEntry] = useState({
|
||||
open: false,
|
||||
|
@ -61,36 +57,41 @@ const {isLoading, data: silenced} = useSilencedAlerts();
|
|||
|
||||
return (
|
||||
<div className="alerting">
|
||||
{isLoading? null : silenced.map((v, i) => (
|
||||
<SilencedBox
|
||||
key={i}
|
||||
silenceEntry={v}
|
||||
editSilence={editSilence(v)}
|
||||
removeSilence={removeSilence(v)}
|
||||
/>
|
||||
))}
|
||||
|
||||
{(silenced ?? []).length === 0? (
|
||||
<React.Fragment>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{flexFlow: "wrap"}}
|
||||
>
|
||||
{isLoading
|
||||
? null
|
||||
: silenced.map((v, i) => (
|
||||
<SilencedBox
|
||||
key={i}
|
||||
silenceEntry={v}
|
||||
editSilence={editSilence(v)}
|
||||
removeSilence={removeSilence(v)}
|
||||
/>
|
||||
))}
|
||||
|
||||
<Typography variant="h4">No alerts silenced! </Typography> </Box>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{flexFlow: "wrap"}}
|
||||
> <Typography variant="h5">Click the '+' to create a new one!
|
||||
</Typography>
|
||||
</Box>
|
||||
{(silenced ?? []).length === 0 ? (
|
||||
<React.Fragment>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{ flexFlow: "wrap" }}
|
||||
>
|
||||
<Typography variant="h4">No alerts silenced! </Typography>{" "}
|
||||
</Box>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{ flexFlow: "wrap" }}
|
||||
>
|
||||
{" "}
|
||||
<Typography variant="h5">
|
||||
Click the '+' to create a new one!
|
||||
</Typography>
|
||||
</Box>
|
||||
</React.Fragment>
|
||||
): null}
|
||||
|
||||
) : null}
|
||||
|
||||
<Dialog
|
||||
open={silenceEntry.deleteOpen}
|
||||
onClose={handleDeleteClose()}
|
||||
|
|
|
@ -3,13 +3,12 @@ import StoreContext from "../../ctx/StoreContext.jsx";
|
|||
import JobContext from "../../ctx/JobContext.jsx";
|
||||
import CatalogBox from "./CatalogBox.jsx";
|
||||
import CatalogSearch from "./CatalogSearch.jsx";
|
||||
import {useCatalogTests} from "../../Queries.jsx";
|
||||
import { useCatalogTests } from "../../Queries.jsx";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
export default function Catalog() {
|
||||
|
||||
const { state: store, updateStore } = useContext(StoreContext);
|
||||
const {isLoading, data: tests} = useCatalogTests();
|
||||
const { isLoading, data: tests } = useCatalogTests();
|
||||
const handleSearchChange = (e) =>
|
||||
updateStore({ catalogSearch: e.target.value });
|
||||
|
||||
|
@ -29,9 +28,9 @@ export default function Catalog() {
|
|||
clearOnUnmount
|
||||
/>
|
||||
<h6>{store.catalogSearch}</h6>
|
||||
{isLoading ? null : tests.map((v, i) => (
|
||||
<CatalogBox key={i} catalogTest={v} />
|
||||
))}
|
||||
{isLoading
|
||||
? null
|
||||
: tests.map((v, i) => <CatalogBox key={i} catalogTest={v} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ export default function CatalogBox(props) {
|
|||
|
||||
const { state: store, updateStore } = useContext(StoreContext);
|
||||
|
||||
const { state: jobState, jobFactory} = useContext(JobContext);
|
||||
const { state: jobState, jobFactory } = useContext(JobContext);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
|
@ -38,7 +38,7 @@ export default function CatalogBox(props) {
|
|||
const runTest = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const jobId = jobFactory({testNames: [testName]});
|
||||
const jobId = jobFactory({ testNames: [testName] });
|
||||
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useState, useContext } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {useCurrentlyFailing, useSilencedAlerts} from "../../Queries.jsx";
|
||||
import { useCurrentlyFailing, useSilencedAlerts } from "../../Queries.jsx";
|
||||
import StoreContext from "../../ctx/StoreContext.jsx";
|
||||
import JobContext from "../../ctx/JobContext.jsx";
|
||||
import SilenceDialog from "../alerting/SilenceDialog.jsx";
|
||||
|
@ -28,8 +28,8 @@ export default function Failing() {
|
|||
updateStore,
|
||||
silenceRequest,
|
||||
} = useContext(StoreContext);
|
||||
const {isLoading, data: failing} = useCurrentlyFailing();
|
||||
const {isSilencedLoading, data:silencedAlerts} = useSilencedAlerts();
|
||||
const { isLoading, data: failing } = useCurrentlyFailing();
|
||||
const { isSilencedLoading, data: silencedAlerts } = useSilencedAlerts();
|
||||
const [silenceEntry, setSilenceEntry] = useState({ open: false });
|
||||
|
||||
const closeSilence = () => setSilenceEntry({ ...silenceEntry, open: false });
|
||||
|
@ -58,22 +58,28 @@ export default function Failing() {
|
|||
|
||||
const failingTestsWithJobs = () => {
|
||||
const silences = silencedAlerts ?? [];
|
||||
for(var test of failing){
|
||||
if(test.isCompound) continue;
|
||||
const job = jobState.jobs.find((j)=>j.builderCache.testNames.includes(test.name))
|
||||
if(job) test.job = job;
|
||||
const silence = silences.find((s)=>s.name === test.name || s.class === test.class)
|
||||
for (var test of failing) {
|
||||
if (test.isCompound) continue;
|
||||
const job = jobState.jobs.find((j) =>
|
||||
j.builderCache.testNames.includes(test.name)
|
||||
);
|
||||
if (job) test.job = job;
|
||||
const silence = silences.find(
|
||||
(s) => s.name === test.name || s.class === test.class
|
||||
);
|
||||
|
||||
if(silence) test.silencedUntil = silence;
|
||||
if (silence) test.silencedUntil = silence;
|
||||
}
|
||||
return failing;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="failing">
|
||||
{isLoading? null :failingTestsWithJobs().map((v, i) => (
|
||||
<FailingBox key={i} failingTest={v} silenceClick={editSilence(v)} />
|
||||
))}
|
||||
{isLoading
|
||||
? null
|
||||
: failingTestsWithJobs().map((v, i) => (
|
||||
<FailingBox key={i} failingTest={v} silenceClick={editSilence(v)} />
|
||||
))}
|
||||
|
||||
<Dialog
|
||||
open={retryAllOpen}
|
||||
|
@ -100,25 +106,21 @@ export default function Failing() {
|
|||
onClose={handleSilenceClose}
|
||||
silence={silenceEntry}
|
||||
/>
|
||||
{(failing ?? []).length === 0? ( <Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Typography variant="h4">No tests failing!</Typography>
|
||||
</Box>
|
||||
|
||||
|
||||
): null}
|
||||
{(failing ?? []).length === 0 ? (
|
||||
<Box display="flex" alignItems="center" justifyContent="center">
|
||||
<Typography variant="h4">No tests failing!</Typography>
|
||||
</Box>
|
||||
) : null}
|
||||
|
||||
{(failing ?? []).length ===0? null :(
|
||||
<SpeedDial
|
||||
ariaLabel="Retry All"
|
||||
sx={{ position: "fixed", bottom: 16, right: 16 }}
|
||||
icon={<ReplayIcon />}
|
||||
onClick={retryAllClick}
|
||||
open={false}
|
||||
/>)}
|
||||
{(failing ?? []).length === 0 ? null : (
|
||||
<SpeedDial
|
||||
ariaLabel="Retry All"
|
||||
sx={{ position: "fixed", bottom: 16, right: 16 }}
|
||||
icon={<ReplayIcon />}
|
||||
onClick={retryAllClick}
|
||||
open={false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export default function FailingBox(props) {
|
|||
failedMessage,
|
||||
isCompound,
|
||||
jobStatus: testJobStatus,
|
||||
job
|
||||
job,
|
||||
} = failingTest;
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
@ -78,12 +78,12 @@ export default function FailingBox(props) {
|
|||
}
|
||||
|
||||
const retryTest = () => {
|
||||
const jobId = jobFactory({testNames: [testName], isTriage: true});
|
||||
const jobId = jobFactory({ testNames: [testName], isTriage: true });
|
||||
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
||||
};
|
||||
|
||||
const jobOnClick = () => {
|
||||
if(!job) return retryTest;
|
||||
if (!job) return retryTest;
|
||||
switch (job.status) {
|
||||
case jobStatus.OK:
|
||||
return null;
|
||||
|
@ -103,7 +103,7 @@ export default function FailingBox(props) {
|
|||
};
|
||||
|
||||
function jobIcon() {
|
||||
if(!job) return <ReplayIcon />;
|
||||
if (!job) return <ReplayIcon />;
|
||||
switch (job.status) {
|
||||
case jobStatus.OK:
|
||||
return <CheckIcon color="success" />;
|
||||
|
|
|
@ -21,13 +21,11 @@ import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
|
|||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
||||
|
||||
|
||||
|
||||
export default function JobView(props) {
|
||||
const navigate = useNavigate();
|
||||
const { job } = props;
|
||||
const { jobFactory, jobCancel, jobDestroy } = useContext(JobContext);
|
||||
const {state: store} = useContext(StoreContext);
|
||||
const { state: store } = useContext(StoreContext);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = (event) => {
|
||||
|
@ -51,8 +49,8 @@ export default function JobView(props) {
|
|||
}
|
||||
|
||||
function retryJob() {
|
||||
const jobId = jobFactory(job.builderCache);
|
||||
if(store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
||||
const jobId = jobFactory(job.builderCache);
|
||||
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
||||
}
|
||||
|
||||
function downloadLog() {
|
||||
|
@ -60,12 +58,11 @@ export default function JobView(props) {
|
|||
download(`${job.jobId}.txt`, job.log.join("\n"));
|
||||
}
|
||||
|
||||
function cancelJob(){
|
||||
function cancelJob() {
|
||||
jobCancel(job.jobId);
|
||||
|
||||
}
|
||||
|
||||
function deleteJob(){
|
||||
function deleteJob() {
|
||||
jobDestroy(job.jobId);
|
||||
navigateToJobs();
|
||||
}
|
||||
|
@ -107,7 +104,7 @@ export default function JobView(props) {
|
|||
<Toolbar disableGutters />
|
||||
<JobLogView log={job.log} status={job.status} />
|
||||
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
||||
<MenuItem onClick={menuSelect(downloadLog)}>
|
||||
<MenuItem onClick={menuSelect(downloadLog)}>
|
||||
<ListItemIcon>
|
||||
<DownloadIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
|
@ -115,16 +112,27 @@ export default function JobView(props) {
|
|||
</MenuItem>
|
||||
<MenuItem onClick={menuSelect(retryJob)}>
|
||||
<ListItemIcon>
|
||||
{job.status === jobStatus.OK || job.status === jobStatus.ERROR ? <ReplayIcon fontSize="small" /> : <PlayArrowIcon fontSize="small"/>}
|
||||
{job.status === jobStatus.OK || job.status === jobStatus.ERROR ? (
|
||||
<ReplayIcon fontSize="small" />
|
||||
) : (
|
||||
<PlayArrowIcon fontSize="small" />
|
||||
)}
|
||||
</ListItemIcon>
|
||||
<ListItemText> {job.status === jobStatus.ERROR? "Retry" : "Duplicate"}</ListItemText>
|
||||
<ListItemText>
|
||||
{" "}
|
||||
{job.status === jobStatus.ERROR ? "Retry" : "Duplicate"}
|
||||
</ListItemText>
|
||||
</MenuItem>
|
||||
{job.status === jobStatus.OK || job.status === jobStatus.ERROR || job.status === jobStatus.CANCELED? null : <MenuItem onClick={menuSelect(cancelJob)}>
|
||||
<ListItemIcon>
|
||||
<DoNotDisturbIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Cancel</ListItemText>
|
||||
</MenuItem>}
|
||||
{job.status === jobStatus.OK ||
|
||||
job.status === jobStatus.ERROR ||
|
||||
job.status === jobStatus.CANCELED ? null : (
|
||||
<MenuItem onClick={menuSelect(cancelJob)}>
|
||||
<ListItemIcon>
|
||||
<DoNotDisturbIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Cancel</ListItemText>
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={menuSelect(deleteJob)}>
|
||||
<ListItemIcon>
|
||||
<DeleteIcon fontSize="small" />
|
||||
|
|
|
@ -21,37 +21,42 @@ export default function Jobs() {
|
|||
|
||||
return (
|
||||
<div className="jobs">
|
||||
{jobState.jobs.length === 0? (
|
||||
<React.Fragment>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{flexFlow: "wrap"}}
|
||||
>
|
||||
|
||||
<Typography variant="h4">No jobs found! </Typography> </Box>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{flexFlow: "wrap"}}
|
||||
> <Typography variant="h5">Click the '+' to start a new one!
|
||||
</Typography>
|
||||
</Box>
|
||||
{jobState.jobs.length === 0 ? (
|
||||
<React.Fragment>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{ flexFlow: "wrap" }}
|
||||
>
|
||||
<Typography variant="h4">No jobs found! </Typography>{" "}
|
||||
</Box>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{ flexFlow: "wrap" }}
|
||||
>
|
||||
{" "}
|
||||
<Typography variant="h5">
|
||||
Click the '+' to start a new one!
|
||||
</Typography>
|
||||
</Box>
|
||||
</React.Fragment>
|
||||
): null}
|
||||
) : null}
|
||||
<JobBuilder />
|
||||
{location.hash === "" &&
|
||||
jobState.jobs.filter((j)=>!j.isPipeline).map((v, i) => (
|
||||
<a
|
||||
key={i}
|
||||
href={`/qualiteer/jobs#${v.name}`}
|
||||
style={{ textDecoration: "none" }}
|
||||
>
|
||||
<JobBox job={v} />
|
||||
</a>
|
||||
))}
|
||||
jobState.jobs
|
||||
.filter((j) => !j.isPipeline)
|
||||
.map((v, i) => (
|
||||
<a
|
||||
key={i}
|
||||
href={`/qualiteer/jobs#${v.name}`}
|
||||
style={{ textDecoration: "none" }}
|
||||
>
|
||||
<JobBox job={v} />
|
||||
</a>
|
||||
))}
|
||||
{jobState.jobs.find((job) => job.name === location.hash.slice(1)) && (
|
||||
<JobView
|
||||
job={jobState.jobs.find((job) => job.name === location.hash.slice(1))}
|
||||
|
|
|
@ -6,7 +6,6 @@ import DialogContent from "@mui/material/DialogContent";
|
|||
function PipelineConfirm(props) {
|
||||
const { cache, back, start } = props;
|
||||
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DialogContent>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useContext } from "react";
|
||||
import {usePipelineMappings} from "../../../Queries.jsx";
|
||||
import { usePipelineMappings } from "../../../Queries.jsx";
|
||||
import Button from "@mui/material/Button";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
|
@ -11,27 +11,27 @@ import Stack from "@mui/material/Stack";
|
|||
|
||||
function PipelineSelector(props) {
|
||||
const { cache, setCache, cancel, next } = props;
|
||||
const {isLoading, data: pipelineMappings} = usePipelineMappings();
|
||||
const { isLoading, data: pipelineMappings } = usePipelineMappings();
|
||||
|
||||
const primaryMappings = () =>{
|
||||
if(isLoading) return {};
|
||||
const primaryMappings = () => {
|
||||
if (isLoading) return {};
|
||||
const primaryMappings = {};
|
||||
for (var pm of pipelineMappings) {
|
||||
if (!(pm[0] in primaryMappings)) primaryMappings[pm[0]] = [];
|
||||
primaryMappings[pm[0]].push(pm);
|
||||
}
|
||||
return primaryMappings;
|
||||
for (var pm of pipelineMappings) {
|
||||
if (!(pm[0] in primaryMappings)) primaryMappings[pm[0]] = [];
|
||||
primaryMappings[pm[0]].push(pm);
|
||||
}
|
||||
return primaryMappings;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const selectPrimary = (primarySelectedMappings) => () => {
|
||||
setCache({ primarySelectedMappings });
|
||||
};
|
||||
|
||||
const clickNext = () =>{
|
||||
if(!cache.primarySelectedMappings ) return console.log("No mapping selected")
|
||||
next()
|
||||
}
|
||||
const clickNext = () => {
|
||||
if (!cache.primarySelectedMappings)
|
||||
return console.log("No mapping selected");
|
||||
next();
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
@ -53,11 +53,17 @@ function PipelineSelector(props) {
|
|||
<Typography
|
||||
component={"span"}
|
||||
style={{ wordBreak: "break-word", margin: "auto 0" }}
|
||||
color={(cache.primarySelectedMappings ?? [[]])[0][0] ===k ? "primary": null}
|
||||
color={
|
||||
(cache.primarySelectedMappings ?? [[]])[0][0] === k
|
||||
? "primary"
|
||||
: null
|
||||
}
|
||||
>
|
||||
{k}
|
||||
</Typography>
|
||||
<Stack sx={{ ml: "auto" }}>{(primaryMappings[k] ?? []).length}</Stack>
|
||||
<Stack sx={{ ml: "auto" }}>
|
||||
{(primaryMappings[k] ?? []).length}
|
||||
</Stack>
|
||||
</AccordionSummary>
|
||||
</Accordion>
|
||||
))}
|
||||
|
|
|
@ -8,40 +8,48 @@ import AccordionSummary from "@mui/material/AccordionSummary";
|
|||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import {selectBranch, asTree, asBranches, as1d} from "../../../../lib/jobs/pipelines.js";
|
||||
import {
|
||||
selectBranch,
|
||||
asTree,
|
||||
asBranches,
|
||||
as1d,
|
||||
} from "../../../../lib/jobs/pipelines.js";
|
||||
|
||||
function PipelineTrackSelector(props) {
|
||||
const { cache, setCache, back, next } = props;
|
||||
const {primarySelectedMappings: primaries} = cache;
|
||||
const { primarySelectedMappings: primaries } = cache;
|
||||
const addTrack = (test) => {
|
||||
const tracks = cache.tracks ?? []; tracks.push(selectBranch(primaries, test));
|
||||
setCache({ ...cache, tracks});
|
||||
const tracks = cache.tracks ?? [];
|
||||
tracks.push(selectBranch(primaries, test));
|
||||
setCache({ ...cache, tracks });
|
||||
};
|
||||
|
||||
const removeTrack = (test) => {
|
||||
const {tracks} = cache;
|
||||
for(var i in tracks){
|
||||
const { tracks } = cache;
|
||||
for (var i in tracks) {
|
||||
const index = tracks[i].indexOf(test);
|
||||
if(index===-1) continue;
|
||||
tracks[i] = tracks[i].slice(0,index);
|
||||
if (index === -1) continue;
|
||||
tracks[i] = tracks[i].slice(0, index);
|
||||
}
|
||||
setCache({ ...cache, tracks});
|
||||
setCache({ ...cache, tracks });
|
||||
};
|
||||
|
||||
const selectTrack = (test) => () => {
|
||||
if(as1d(cache.tracks).includes(test)) return removeTrack(test);
|
||||
if (as1d(cache.tracks).includes(test)) return removeTrack(test);
|
||||
addTrack(test);
|
||||
};
|
||||
|
||||
const getColor = (test) => as1d(cache.tracks).includes(test)
|
||||
? "primary"
|
||||
: null;
|
||||
const getColor = (test) =>
|
||||
as1d(cache.tracks).includes(test) ? "primary" : null;
|
||||
|
||||
const nextClick = () => {
|
||||
|
||||
setCache({...cache, branches: asBranches(primaries), tree: asTree(cache.tracks)});
|
||||
setCache({
|
||||
...cache,
|
||||
branches: asBranches(primaries),
|
||||
tree: asTree(cache.tracks),
|
||||
});
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue