Merge branch 'job-nav' into 'master'
Job navigation rewrite See merge request Dunemask/qualiteer!2
This commit is contained in:
commit
311cc229e9
10 changed files with 242 additions and 67 deletions
|
@ -1,5 +1,6 @@
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import JobContext, { jobStatus } from "@qltr/jobs";
|
import JobContext, { jobStatus } from "@qltr/jobs";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import CheckIcon from "@mui/icons-material/Check";
|
import CheckIcon from "@mui/icons-material/Check";
|
||||||
import ClearIcon from "@mui/icons-material/Clear";
|
import ClearIcon from "@mui/icons-material/Clear";
|
||||||
|
@ -55,3 +56,14 @@ export const selectedPipelineBranches = (pipeline) =>
|
||||||
|
|
||||||
export const findPipelineJobByTestName = (pipeline, jobs, testName) =>
|
export const findPipelineJobByTestName = (pipeline, jobs, testName) =>
|
||||||
pipelineJobs(pipeline, jobs).find((j) => j.branchId === testName);
|
pipelineJobs(pipeline, jobs).find((j) => j.branchId === testName);
|
||||||
|
|
||||||
|
export function useJobNav() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const toJob = (jobId) => navigate(`/qualiteer/jobs#job-${jobId}`);
|
||||||
|
const toPipeline = (pipelineId) =>
|
||||||
|
navigate(`/qualiteer/jobs#pipeline-${pipelineId}`);
|
||||||
|
|
||||||
|
const toJobs = () => navigate(`/qualiteer/jobs`);
|
||||||
|
return { toJob, toPipeline, toJobs };
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default function Catalog() {
|
||||||
for (var test of tests) {
|
for (var test of tests) {
|
||||||
if (test.isPipeline) {
|
if (test.isPipeline) {
|
||||||
const pipeline = jobState.pipelines.find((p) =>
|
const pipeline = jobState.pipelines.find((p) =>
|
||||||
p.selectedBranches.includes(test.name)
|
p.selectedBranches.find((b) => b.name === test.name)
|
||||||
);
|
);
|
||||||
if (!pipeline) continue;
|
if (!pipeline) continue;
|
||||||
const pipelineJob = jobState.jobs.find(
|
const pipelineJob = jobState.jobs.find(
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import React, { useState, useContext } from "react";
|
import React, { useState, useContext } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import { usePipelineMappings } from "@qltr/queries";
|
import { usePipelineMappings } from "@qltr/queries";
|
||||||
import StoreContext from "@qltr/store";
|
import StoreContext from "@qltr/store";
|
||||||
import JobContext, { jobStatus } from "@qltr/jobs";
|
import JobContext, { jobStatus } from "@qltr/jobs";
|
||||||
import { useJobIconState, usePipelineIconState } from "@qltr/util/JobTools";
|
import {
|
||||||
|
useJobIconState,
|
||||||
|
usePipelineIconState,
|
||||||
|
useJobNav,
|
||||||
|
} 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";
|
||||||
|
@ -31,25 +35,23 @@ export default function CatalogBox(props) {
|
||||||
pipeline,
|
pipeline,
|
||||||
} = catalogTest;
|
} = catalogTest;
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { data: pipelineMappings, isLoading } = usePipelineMappings();
|
const { data: pipelineMappings, isLoading } = usePipelineMappings();
|
||||||
const { state: store } = useContext(StoreContext);
|
const { state: store } = useContext(StoreContext);
|
||||||
|
|
||||||
const { jobFactory } = useContext(JobContext);
|
const { jobFactory } = useContext(JobContext);
|
||||||
|
const jobNav = useJobNav();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const toggleOpen = () => setOpen(!open);
|
const toggleOpen = () => setOpen(!open);
|
||||||
|
|
||||||
const navigateToJob = () => {
|
const navigateToJob = () => {
|
||||||
if (pipeline) return navigate(`/qualiteer/jobs#p${pipeline.id}`);
|
if (pipeline) return jobNav.toPipeline(pipeline.id);
|
||||||
navigate(`/qualiteer/jobs#${job.jobId}`);
|
jobNav.toJob(job.jobId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const runTest = () => {
|
const runTest = () => {
|
||||||
if (isPipeline) return runPipelineTest();
|
if (isPipeline) return runPipelineTest();
|
||||||
const jobId = jobFactory({ testNames: [testName], isTriage: true });
|
const jobId = jobFactory({ testNames: [testName], isTriage: true });
|
||||||
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
if (store.focusJob) jobNav.toJob(jobId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const runPipelineTest = () => {
|
const runPipelineTest = () => {
|
||||||
|
@ -67,7 +69,7 @@ export default function CatalogBox(props) {
|
||||||
isTriage: true,
|
isTriage: true,
|
||||||
};
|
};
|
||||||
const pipeline = jobFactory(builderCache);
|
const pipeline = jobFactory(builderCache);
|
||||||
if (store.focusJob) navigate(`/qualiteer/jobs#p${pipeline.id}`);
|
if (store.focusJob) jobNav.toPipeline(pipeline.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const jobOnClick = (e) => {
|
const jobOnClick = (e) => {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState, useContext } from "react";
|
import { useState, useContext } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import { useCurrentlyFailing, useSilencedAlerts } from "@qltr/queries";
|
import { useCurrentlyFailing, useSilencedAlerts } from "@qltr/queries";
|
||||||
import StoreContext from "@qltr/store";
|
import StoreContext from "@qltr/store";
|
||||||
import JobContext from "@qltr/jobs";
|
import JobContext from "@qltr/jobs";
|
||||||
|
import { useJobNav } from "@qltr/util/JobTools";
|
||||||
import SilenceDialog from "../alerting/SilenceDialog.jsx";
|
import SilenceDialog from "../alerting/SilenceDialog.jsx";
|
||||||
import FailingBox from "./FailingBox.jsx";
|
import FailingBox from "./FailingBox.jsx";
|
||||||
|
|
||||||
|
@ -20,10 +20,10 @@ 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 { state: store, silenceRequest } = useContext(StoreContext);
|
const { state: store, silenceRequest } = useContext(StoreContext);
|
||||||
const { isLoading, data: failing } = useCurrentlyFailing();
|
const { isLoading, data: failing } = useCurrentlyFailing();
|
||||||
const { isSilencedLoading, data: silencedAlerts } = useSilencedAlerts();
|
const { isSilencedLoading, data: silencedAlerts } = useSilencedAlerts();
|
||||||
|
const jobNav = useJobNav();
|
||||||
const [silenceEntry, setSilenceEntry] = useState({ open: false });
|
const [silenceEntry, setSilenceEntry] = useState({ open: false });
|
||||||
|
|
||||||
const closeSilence = () => setSilenceEntry({ ...silenceEntry, open: false });
|
const closeSilence = () => setSilenceEntry({ ...silenceEntry, open: false });
|
||||||
|
@ -47,7 +47,7 @@ export default function Failing() {
|
||||||
const jobId = retryAll(store.failing);
|
const jobId = retryAll(store.failing);
|
||||||
|
|
||||||
if (!store.focusJob) return;
|
if (!store.focusJob) return;
|
||||||
navigate(`/qualiteer/jobs#${jobId}`);
|
jobNav.toJob(jobId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const failingTestsWithJobs = () => {
|
const failingTestsWithJobs = () => {
|
||||||
|
@ -61,7 +61,7 @@ export default function Failing() {
|
||||||
if (silence) test.silencedUntil = silence;
|
if (silence) test.silencedUntil = silence;
|
||||||
if (test.isPipeline) {
|
if (test.isPipeline) {
|
||||||
const pipeline = jobState.pipelines.find((p) =>
|
const pipeline = jobState.pipelines.find((p) =>
|
||||||
p.selectedBranches.includes(test.name)
|
p.selectedBranches.find((b) => b.name === test.name)
|
||||||
);
|
);
|
||||||
if (!pipeline) continue;
|
if (!pipeline) continue;
|
||||||
const pipelineJob = jobState.jobs.find(
|
const pipelineJob = jobState.jobs.find(
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import React, { useState, useContext } from "react";
|
import React, { useState, useContext } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import { usePipelineMappings } from "@qltr/queries";
|
import { usePipelineMappings } from "@qltr/queries";
|
||||||
import StoreContext from "@qltr/store";
|
import StoreContext from "@qltr/store";
|
||||||
import JobContext, { jobStatus } from "@qltr/jobs";
|
import JobContext, { jobStatus } from "@qltr/jobs";
|
||||||
import { useJobIconState, usePipelineIconState } from "@qltr/util/JobTools";
|
import {
|
||||||
|
useJobIconState,
|
||||||
|
usePipelineIconState,
|
||||||
|
useJobNav,
|
||||||
|
} 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";
|
||||||
|
@ -51,16 +54,14 @@ export default function FailingBox(props) {
|
||||||
recentResults,
|
recentResults,
|
||||||
failedMessage,
|
failedMessage,
|
||||||
isPipeline,
|
isPipeline,
|
||||||
jobStatus: testJobStatus,
|
|
||||||
job,
|
job,
|
||||||
pipeline,
|
pipeline,
|
||||||
} = failingTest;
|
} = failingTest;
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { data: pipelineMappings, isLoading } = usePipelineMappings();
|
const { data: pipelineMappings, isLoading } = usePipelineMappings();
|
||||||
const { jobFactory } = useContext(JobContext);
|
const { jobFactory } = useContext(JobContext);
|
||||||
|
|
||||||
const { state: store, updateStore, removeFailure } = useContext(StoreContext);
|
const { state: store, updateStore, removeFailure } = useContext(StoreContext);
|
||||||
|
const jobNav = useJobNav();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const toggleOpen = () => setOpen(!open);
|
const toggleOpen = () => setOpen(!open);
|
||||||
|
@ -93,18 +94,18 @@ export default function FailingBox(props) {
|
||||||
isTriage: true,
|
isTriage: true,
|
||||||
};
|
};
|
||||||
const pipeline = jobFactory(builderCache);
|
const pipeline = jobFactory(builderCache);
|
||||||
if (store.focusJob) navigate(`/qualiteer/jobs#p${pipeline.id}`);
|
if (store.focusJob) jobNav.toPipeline(pipeline.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const retryTest = () => {
|
const retryTest = () => {
|
||||||
if (isPipeline) return retryPipelineTest();
|
if (isPipeline) return retryPipelineTest();
|
||||||
const jobId = jobFactory({ testNames: [testName], isTriage: true });
|
const jobId = jobFactory({ testNames: [testName], isTriage: true });
|
||||||
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
if (store.focusJob) jobNav.toJob(jobId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToJob = () => {
|
const navigateToJob = () => {
|
||||||
if (pipeline) return navigate(`/qualiteer/jobs#p${pipeline.id}`);
|
if (pipeline) return jobNav.toPipeline(pipeline.id);
|
||||||
navigate(`/qualiteer/jobs#${job.jobId}`);
|
jobNav.toJob(job.jobId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const jobOnClick = () => {
|
const jobOnClick = () => {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import JobContext, { jobStatus } from "@qltr/jobs";
|
import JobContext, { jobStatus } from "@qltr/jobs";
|
||||||
import {
|
import {
|
||||||
selectedPipelineBranches,
|
selectedPipelineBranches,
|
||||||
pipelineJobs,
|
pipelineJobs,
|
||||||
findPipelineJobByTestName,
|
findPipelineJobByTestName,
|
||||||
useJobIconState,
|
useJobIconState,
|
||||||
|
useJobNav,
|
||||||
} from "@qltr/util/JobTools";
|
} from "@qltr/util/JobTools";
|
||||||
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
@ -18,11 +18,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||||
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 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 DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
|
||||||
|
|
||||||
import Menu from "@mui/material/Menu";
|
import Menu from "@mui/material/Menu";
|
||||||
|
@ -39,7 +35,8 @@ function JobPipelineDisplay(props) {
|
||||||
pipelineCancel,
|
pipelineCancel,
|
||||||
pipelineDestroy,
|
pipelineDestroy,
|
||||||
} = useContext(JobContext);
|
} = useContext(JobContext);
|
||||||
const navigate = useNavigate();
|
|
||||||
|
const jobNav = useJobNav();
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
|
@ -53,13 +50,9 @@ function JobPipelineDisplay(props) {
|
||||||
const selectJob = (testName) => () => {
|
const selectJob = (testName) => () => {
|
||||||
const job = findPipelineJobByTestName(pipeline, jobState.jobs, testName);
|
const job = findPipelineJobByTestName(pipeline, jobState.jobs, testName);
|
||||||
if (!job) return;
|
if (!job) return;
|
||||||
navigate(`/qualiteer/jobs#${job.jobId}`);
|
jobNav.toJob(job.jobId);
|
||||||
};
|
};
|
||||||
|
|
||||||
function navigateToJobs() {
|
|
||||||
navigate(`/qualiteer/jobs`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelPipeline() {
|
function cancelPipeline() {
|
||||||
pipelineCancel(pipeline.id);
|
pipelineCancel(pipeline.id);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +92,7 @@ function JobPipelineDisplay(props) {
|
||||||
<Toolbar disableGutters />
|
<Toolbar disableGutters />
|
||||||
<Box sx={{ flexGrow: 1, margin: "0 10px" }}>
|
<Box sx={{ flexGrow: 1, margin: "0 10px" }}>
|
||||||
<Toolbar disableGutters>
|
<Toolbar disableGutters>
|
||||||
<IconButton onClick={navigateToJobs}>
|
<IconButton onClick={jobNav.toJobs}>
|
||||||
<ArrowBackIcon />
|
<ArrowBackIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" sx={{ ml: "auto", mr: "auto" }}>
|
<Typography variant="h6" sx={{ ml: "auto", mr: "auto" }}>
|
||||||
|
|
155
src/views/jobs/JobPiplinePendingView.jsx
Normal file
155
src/views/jobs/JobPiplinePendingView.jsx
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
import React, { useContext, useState, useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import JobContext, { jobStatus } from "@qltr/jobs";
|
||||||
|
import StoreContext from "@qltr/store";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import AppBar from "@mui/material/AppBar";
|
||||||
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Menu from "@mui/material/Menu";
|
||||||
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
|
||||||
|
import JobLogView from "./JobLogView.jsx";
|
||||||
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||||
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
|
import DownloadIcon from "@mui/icons-material/Download";
|
||||||
|
import ReplayIcon from "@mui/icons-material/Replay";
|
||||||
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||||
|
import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
|
||||||
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
||||||
|
import ViewColumnIcon from "@mui/icons-material/ViewColumn";
|
||||||
|
|
||||||
|
export default function JobPipelinePendingView(props) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { job } = props;
|
||||||
|
const { jobFactory, jobCancel, jobDestroy } = useContext(JobContext);
|
||||||
|
const { state: store } = useContext(StoreContext);
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
function download(filename, text) {
|
||||||
|
var element = document.createElement("a");
|
||||||
|
element.setAttribute(
|
||||||
|
"href",
|
||||||
|
"data:text/plain;charset=utf-8," + encodeURIComponent(text)
|
||||||
|
);
|
||||||
|
element.setAttribute("download", filename);
|
||||||
|
element.style.display = "none";
|
||||||
|
document.body.appendChild(element);
|
||||||
|
element.click();
|
||||||
|
document.body.removeChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function retryJob() {
|
||||||
|
const jobId = jobFactory(job.builderCache);
|
||||||
|
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadLog() {
|
||||||
|
if (job.status === jobStatus.PENDING) return;
|
||||||
|
download(`${job.jobId}.txt`, job.log.join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelJob() {
|
||||||
|
jobCancel(job.jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteJob() {
|
||||||
|
jobDestroy(job.jobId);
|
||||||
|
navigateToJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuSelect = (cb) => () => {
|
||||||
|
handleClose();
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
function navigateToJobs() {
|
||||||
|
if (job.isPipeline) return navigate(`/qualiteer/jobs#p${job.pipelineId}`);
|
||||||
|
navigate("/qualiteer/jobs");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<AppBar
|
||||||
|
position="fixed"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "white",
|
||||||
|
boxShadow: "none",
|
||||||
|
color: "black",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Toolbar disableGutters />
|
||||||
|
<Box sx={{ flexGrow: 1, margin: "0 10px" }}>
|
||||||
|
<Toolbar disableGutters>
|
||||||
|
<IconButton onClick={navigateToJobs}>
|
||||||
|
<ArrowBackIcon />
|
||||||
|
</IconButton>
|
||||||
|
<Typography variant="h6" component="span" sx={{ ml: "auto" }}>
|
||||||
|
{job.name}
|
||||||
|
</Typography>
|
||||||
|
{job.isPipeline && (
|
||||||
|
<IconButton>
|
||||||
|
<ViewColumnIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
<IconButton onClick={handleClick} sx={{ ml: "auto" }}>
|
||||||
|
<MoreVertIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Toolbar>
|
||||||
|
</Box>
|
||||||
|
</AppBar>
|
||||||
|
<Toolbar disableGutters />
|
||||||
|
<JobLogView log={job.log} status={job.status} />
|
||||||
|
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
||||||
|
<MenuItem onClick={menuSelect(downloadLog)}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<DownloadIcon fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>Download Log</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
{!job.isPipeline && (
|
||||||
|
<MenuItem onClick={menuSelect(retryJob)}>
|
||||||
|
<ListItemIcon>
|
||||||
|
{job.status === jobStatus.OK || job.status === jobStatus.ERROR ? (
|
||||||
|
<ReplayIcon fontSize="small" />
|
||||||
|
) : (
|
||||||
|
<PlayArrowIcon fontSize="small" />
|
||||||
|
)}
|
||||||
|
</ListItemIcon>
|
||||||
|
<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.isPipeline && (
|
||||||
|
<MenuItem onClick={menuSelect(deleteJob)}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<DeleteIcon fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>Delete</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</Menu>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||||
import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
|
import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
|
||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
||||||
|
import ViewColumnIcon from "@mui/icons-material/ViewColumn";
|
||||||
|
|
||||||
export default function JobView(props) {
|
export default function JobView(props) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -93,10 +94,15 @@ export default function JobView(props) {
|
||||||
<IconButton onClick={navigateToJobs}>
|
<IconButton onClick={navigateToJobs}>
|
||||||
<ArrowBackIcon />
|
<ArrowBackIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" sx={{ ml: "auto", mr: "auto" }}>
|
<Typography variant="h6" component="span" sx={{ ml: "auto" }}>
|
||||||
{job.name}
|
{job.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton onClick={handleClick}>
|
{job.isPipeline && (
|
||||||
|
<IconButton>
|
||||||
|
<ViewColumnIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
<IconButton onClick={handleClick} sx={{ ml: "auto" }}>
|
||||||
<MoreVertIcon />
|
<MoreVertIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useContext, useEffect } from "react";
|
import React, { useContext, useEffect } from "react";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import JobContext from "@qltr/jobs";
|
import JobContext from "@qltr/jobs";
|
||||||
|
@ -15,14 +15,33 @@ export default function Jobs() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const jobViewPrefix = "#job-";
|
||||||
|
const pipelineViewPrefix = "#pipeline-";
|
||||||
|
const cachePrefix = "#cache-";
|
||||||
|
|
||||||
|
function jobHashLoader() {
|
||||||
|
const { hash } = location;
|
||||||
|
if (!hash) return {};
|
||||||
|
const jobIndex = hash.indexOf(jobViewPrefix);
|
||||||
|
const pipelineIndex = hash.indexOf(pipelineViewPrefix);
|
||||||
|
const cacheIndex = hash.indexOf(cachePrefix);
|
||||||
|
const jobId = jobIndex !== 0 ? null : hash.slice(jobViewPrefix.length);
|
||||||
|
const pipelineId =
|
||||||
|
pipelineIndex !== 0 ? null : hash.slice(pipelineViewPrefix.length);
|
||||||
|
const cacheData = cacheIndex !== 0 ? null : hash.slice(cachePrefix.length);
|
||||||
|
const job = !jobId ? null : jobState.jobs.find((j) => j.jobId === jobId);
|
||||||
|
const pipeline = !pipelineId
|
||||||
|
? null
|
||||||
|
: jobState.pipelines.find((p) => p.id === pipelineId);
|
||||||
|
const builderCache = !cacheData ? null : { fakedata: "yep" };
|
||||||
|
return { job, pipeline, builderCache };
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobHash = jobHashLoader();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const jobName = location.hash.slice(1);
|
const { job, pipeline, builderCache } = jobHash;
|
||||||
const pipelineId = jobName.slice(1);
|
if (!job && !pipeline && !builderCache) navigate("/qualiteer/jobs");
|
||||||
if (!jobName || !pipelineId) return;
|
|
||||||
const hasJob = jobState.pipelines.find((p) => p.id === pipelineId);
|
|
||||||
const hasPipeline = jobState.jobs.find((job) => job.name === jobName);
|
|
||||||
if (hasPipeline || hasJob) return;
|
|
||||||
if (jobName || pipelineId) navigate("/qualiteer/jobs");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -59,7 +78,7 @@ export default function Jobs() {
|
||||||
.map((v, i) => (
|
.map((v, i) => (
|
||||||
<a
|
<a
|
||||||
key={i}
|
key={i}
|
||||||
href={`/qualiteer/jobs#${v.name}`}
|
href={`/qualiteer/jobs#job-${v.jobId}`}
|
||||||
style={{ textDecoration: "none" }}
|
style={{ textDecoration: "none" }}
|
||||||
>
|
>
|
||||||
<JobBox job={v} />
|
<JobBox job={v} />
|
||||||
|
@ -69,7 +88,7 @@ export default function Jobs() {
|
||||||
<a
|
<a
|
||||||
key={i}
|
key={i}
|
||||||
style={{ textDecoration: "none" }}
|
style={{ textDecoration: "none" }}
|
||||||
href={`/qualiteer/jobs#p${p.id}`}
|
href={`/qualiteer/jobs#pipeline-${p.id}`}
|
||||||
>
|
>
|
||||||
<JobPipelineBox pipeline={p} />
|
<JobPipelineBox pipeline={p} />
|
||||||
</a>
|
</a>
|
||||||
|
@ -77,21 +96,8 @@ export default function Jobs() {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{location.hash[1] === "p"
|
{jobHash.pipeline && <JobPipelineDisplay pipeline={jobHash.pipeline} />}
|
||||||
? jobState.pipelines.find((p) => p.id === location.hash.slice(2)) && (
|
{jobHash.job && <JobView job={jobHash.job} />}
|
||||||
<JobPipelineDisplay
|
|
||||||
pipeline={jobState.pipelines.find(
|
|
||||||
(p) => p.id === location.hash.slice(2)
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
: jobState.jobs.find((job) => job.name === location.hash.slice(1)) && (
|
|
||||||
<JobView
|
|
||||||
job={jobState.jobs.find(
|
|
||||||
(job) => job.name === location.hash.slice(1)
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useContext, useState, useEffect } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import StoreContext from "@qltr/store";
|
import StoreContext from "@qltr/store";
|
||||||
import JobContext from "@qltr/jobs";
|
import JobContext from "@qltr/jobs";
|
||||||
|
import { useJobNav } from "@qltr/util/JobTools";
|
||||||
|
|
||||||
import Dialog from "@mui/material/Dialog";
|
import Dialog from "@mui/material/Dialog";
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
|
@ -27,9 +27,9 @@ import PipelineTrackSelector from "./PipelineTrackSelector.jsx";
|
||||||
import PipelineConfirm from "./PipelineConfirm.jsx";
|
import PipelineConfirm from "./PipelineConfirm.jsx";
|
||||||
|
|
||||||
export default function JobBuilder() {
|
export default function JobBuilder() {
|
||||||
const navigate = useNavigate();
|
|
||||||
const { state: store } = useContext(StoreContext);
|
const { state: store } = useContext(StoreContext);
|
||||||
const { jobFactory } = useContext(JobContext);
|
const { jobFactory } = useContext(JobContext);
|
||||||
|
const jobNav = useJobNav();
|
||||||
const [quickOpen, setQuickOpen] = useState(false);
|
const [quickOpen, setQuickOpen] = useState(false);
|
||||||
const [jobDialogOpen, setJobDialogOpen] = useState(false);
|
const [jobDialogOpen, setJobDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ export default function JobBuilder() {
|
||||||
setJobDialogOpen(false);
|
setJobDialogOpen(false);
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
const jobId = jobFactory(cache);
|
const jobId = jobFactory(cache);
|
||||||
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
if (store.focusJob) jobNav.toJob(jobId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pull info from url if possible?
|
// Pull info from url if possible?
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue