Stable Modification Point
This commit is contained in:
parent
d94796173e
commit
468437b5d0
19 changed files with 500 additions and 106 deletions
24
package-lock.json
generated
24
package-lock.json
generated
|
@ -18,6 +18,7 @@
|
|||
"path": "^0.12.7",
|
||||
"pg-promise": "^10.11.1",
|
||||
"postgres-migrations": "^5.3.0",
|
||||
"rabbiteer": "gitlab:Dunemask/rabbiteer",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"socket.io": "^4.4.1",
|
||||
"socket.io-client": "^4.4.1",
|
||||
|
@ -9289,20 +9290,6 @@
|
|||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
|
@ -26662,13 +26649,6 @@
|
|||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
|
@ -31327,7 +31307,7 @@
|
|||
},
|
||||
"rabbiteer": {
|
||||
"version": "git+ssh://git@gitlab.com/Dunemask/rabbiteer.git#6bb6cc26ca7ea7f4eb934529305ebb8ae0b26369",
|
||||
"from": "rabbiteer@gitlab:Dunemask/rabbiteer",
|
||||
"from": "rabbiteer@git+https://gitlab.com/Dunemask/rabbiteer.git",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"amqplib": "^0.8.0"
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
"start:dev": "nodemon dist/app.js",
|
||||
"start:dev:replit": "npm run start:dev & npm run start:react:replit",
|
||||
"start:react": "react-scripts start",
|
||||
"start:react:replit": "DANGEROUSLY_DISABLE_HOST_CHECK=true node --max-old-space-size=512 node_modules/.bin/react-scripts start",
|
||||
"start:react:replit": "CHOKIDAR_USEPOLLING=true CHOKIDAR_INTERVAL=8000 DANGEROUSLY_DISABLE_HOST_CHECK=true node node_modules/.bin/react-scripts start",
|
||||
"test": "node tests/index.js",
|
||||
"test:api": "node tests/api.js",
|
||||
"test:dev": "nodemon tests/index.js"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { useContext, useState } from "react";
|
||||
import * as React from "react";
|
||||
import StoreContext from "./ctx/StoreContext.jsx";
|
||||
import JobContext from "./ctx/JobContext.jsx";
|
||||
|
||||
import {
|
||||
Routes,
|
||||
Route,
|
||||
|
@ -36,25 +38,28 @@ import Catalog from "./views/Catalog.jsx";
|
|||
import Settings from "./views/Settings.jsx";
|
||||
import About from "./views/About.jsx";
|
||||
|
||||
const pages = ["failing", "alerting", "jobs", "catalog", "settings", "about"];
|
||||
const icons = [
|
||||
<Badge badgeContent={4} color="error">
|
||||
<WarningIcon />
|
||||
</Badge>,
|
||||
<NotificationsIcon />,
|
||||
<Badge badgeContent={4} color="primary">
|
||||
<WorkIcon />
|
||||
</Badge>,
|
||||
<FormatListBulletedIcon />,
|
||||
<SettingsIcon />,
|
||||
<InfoIcon />,
|
||||
];
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
export default function Views() {
|
||||
const { state: jobState } = useContext(JobContext);
|
||||
const { state: store } = useContext(StoreContext);
|
||||
|
||||
const pages = ["failing", "alerting", "jobs", "catalog", "settings", "about"];
|
||||
const icons = [
|
||||
<Badge badgeContent={store.failing.length} color="error">
|
||||
<WarningIcon />
|
||||
</Badge>,
|
||||
<NotificationsIcon />,
|
||||
<Badge badgeContent={jobState.jobs.length} color="primary">
|
||||
<WorkIcon />
|
||||
</Badge>,
|
||||
<FormatListBulletedIcon />,
|
||||
<SettingsIcon />,
|
||||
<InfoIcon />,
|
||||
];
|
||||
|
||||
const location = useLocation();
|
||||
const [drawerOpen, setDrawer] = React.useState(false);
|
||||
const [drawerOpen, setDrawer] = useState(false);
|
||||
|
||||
const toggleDrawer = () => setDrawer(!drawerOpen);
|
||||
const closeDrawer = () => setDrawer(false);
|
||||
|
@ -74,7 +79,11 @@ export default function Views() {
|
|||
location.pathname.charAt(1).toUpperCase() + location.pathname.slice(2);
|
||||
if (location.pathname !== "/failing") return pathStr;
|
||||
return (
|
||||
<SideBadge badgeContent={4} color="error" overlap="circular">
|
||||
<SideBadge
|
||||
badgeContent={store.failing.length}
|
||||
color="error"
|
||||
overlap="circular"
|
||||
>
|
||||
{pathStr}
|
||||
</SideBadge>
|
||||
);
|
||||
|
|
|
@ -15,9 +15,16 @@ const ACTIONS = {
|
|||
UPDATE: "u",
|
||||
DELETE: "d",
|
||||
};
|
||||
/**/
|
||||
const jobMock = Object.values(jobStatus).map((v, i) => ({
|
||||
name: `Job${i + 1}`,
|
||||
test: `someTestName${i + 1}`,
|
||||
status: v,
|
||||
exitcode: 0,
|
||||
}));
|
||||
|
||||
const initialState = {
|
||||
jobs: [],
|
||||
jobs: jobMock,
|
||||
};
|
||||
/*
|
||||
{
|
||||
|
@ -80,7 +87,22 @@ export const JobProvider = ({ children }) => {
|
|||
console.log("Would return all active job states");
|
||||
}
|
||||
|
||||
function jobBuilder() {}
|
||||
function mockRun() {
|
||||
dispatch({
|
||||
job: {
|
||||
name: "Job1",
|
||||
test: "someTestName",
|
||||
log: [],
|
||||
status: jobStatus.OK,
|
||||
exitcode: 0,
|
||||
},
|
||||
action: ACTIONS.CREATE,
|
||||
});
|
||||
}
|
||||
|
||||
function jobBuilder() {
|
||||
mockRun();
|
||||
}
|
||||
|
||||
const context = {
|
||||
state,
|
||||
|
|
|
@ -7,10 +7,27 @@ const ACTIONS = {
|
|||
UPDATE: "u",
|
||||
};
|
||||
|
||||
const silencedMock = new Array(10).fill(0).map((v, i) => ({
|
||||
name: `Test${i + 1}`,
|
||||
class: `SomeTestClass${i % 2 ? i - 1 : i / 2}`,
|
||||
method: "someMethod",
|
||||
id: Date.now(),
|
||||
silencedUntil: `2022-05-10T16:${2 + i}:33.810Z`,
|
||||
}));
|
||||
|
||||
const catalogMock = new Array(10).fill(0).map((v, i) => ({
|
||||
name: `Test${i + 1}`,
|
||||
class: `SomeTestClass${i % 2 ? i - 1 : i / 2}`,
|
||||
repo: "Repo",
|
||||
isCompound: i % 5 ? false : true,
|
||||
type: i % 3 ? "api" : "ui",
|
||||
}));
|
||||
|
||||
const failingMock = new Array(10).fill(0).map((v, i) => ({
|
||||
class: `SomeTestClass${i % 2 ? i - 1 : i / 2}`,
|
||||
name: `TestThatDoesOneThing${i + 1}`,
|
||||
timestamp: `2022-05-10T16:${2 + i}:33.810Z`,
|
||||
method: `SomeMethod`,
|
||||
silencedUntil: i % 4 ? null : `2022-05-10T16:${2 + i}:33.810Z`,
|
||||
frequency: "1hour",
|
||||
type: i % 3 ? "api" : "ui",
|
||||
|
@ -41,12 +58,14 @@ const failingMock = new Array(10).fill(0).map((v, i) => ({
|
|||
|
||||
const initialState = {
|
||||
intervals: [],
|
||||
catalog: [],
|
||||
failing: failngMock,
|
||||
catalog: catalogMock,
|
||||
failing: failingMock,
|
||||
silenced: silencedMock,
|
||||
regions: [],
|
||||
catalogSearch: "",
|
||||
focusJob: false,
|
||||
simplifiedControls: false,
|
||||
simplifiedControls: true,
|
||||
logAppDetails: true,
|
||||
defaultRegion: "us", // Local Store
|
||||
defaultPage: "failing", // Local Store
|
||||
};
|
||||
|
@ -65,9 +84,15 @@ const reducer = (state, action) => {
|
|||
export const StoreProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
function silenceRequest(silenceInfo) {
|
||||
const req = {name: silenceInfo.name ?? "*", class: silenceInfo.class ?? "*", method: silenceInfo.method ?? "*", silencedUntil: silenceInfo.silencedUntil };
|
||||
console.log("Would upsert silence", req);
|
||||
}
|
||||
|
||||
const context = {
|
||||
state,
|
||||
dispatch,
|
||||
silenceRequest,
|
||||
updateStore: (store) => dispatch({ type: ACTIONS.UPDATE, store }),
|
||||
};
|
||||
const contextValue = useMemo(() => context, [state, dispatch]);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { useState, useContext } from "react";
|
||||
import StoreContext from "../ctx/StoreContext.jsx";
|
||||
import SilencedBox from "./components/SilencedBox.jsx";
|
||||
import SilenceDialog from "./components/SilenceDialog.jsx";
|
||||
|
||||
import SpeedDial from "@mui/material/SpeedDial";
|
||||
import SpeedDialAction from "@mui/material/SpeedDialAction";
|
||||
|
@ -13,36 +15,78 @@ import DialogContentText from "@mui/material/DialogContentText";
|
|||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
|
||||
export default function Alerting() {
|
||||
const { state: store, updateStore } = useContext(StoreContext);
|
||||
const {
|
||||
state: store,
|
||||
updateStore,
|
||||
silenceRequest,
|
||||
} = useContext(StoreContext);
|
||||
|
||||
const [alertDialogOpen, setAlertDialogOpen] = useState(false);
|
||||
const quickAlertClick = () => setAlertDialogOpen(!alertDialogOpen);
|
||||
const [silenceEntry, setSilenceEntry] = useState({
|
||||
open: false,
|
||||
deleteOpen: false,
|
||||
});
|
||||
|
||||
function silenceAlert() {}
|
||||
const handleClose = (confirmed) => () => {
|
||||
quickAlertClick();
|
||||
if (!confirmed) return;
|
||||
silenceAlert();
|
||||
const closeSilence = () =>
|
||||
setSilenceEntry({ ...silenceEntry, open: false, deleteOpen: false });
|
||||
|
||||
const handleDeleteClose = (makeRequest) => () => {
|
||||
const silenceReq = { ...silenceEntry };
|
||||
closeSilence();
|
||||
if (!makeRequest) return;
|
||||
silenceRequest({ ...silenceReq, silencedUntil: null });
|
||||
};
|
||||
|
||||
const handleClose = (silenceReq) => {
|
||||
closeSilence();
|
||||
if (!silenceReq) return;
|
||||
silenceRequest(silenceReq);
|
||||
};
|
||||
|
||||
const quickAlertClick = () => {
|
||||
setSilenceEntry({ open: true, deleteOpen: false });
|
||||
};
|
||||
|
||||
const editSilence = (silence) => () => {
|
||||
setSilenceEntry({ ...silence, open: true, deleteOpen: false });
|
||||
};
|
||||
|
||||
const removeSilence = (silence) => () => {
|
||||
setSilenceEntry({ ...silence, deleteOpen: true, open: false });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="alerting">
|
||||
{store.silenced.map((v, i) => (
|
||||
<SilencedBox
|
||||
key={i}
|
||||
silenceEntry={v}
|
||||
editSilence={editSilence(v)}
|
||||
removeSilence={removeSilence(v)}
|
||||
/>
|
||||
))}
|
||||
<Dialog
|
||||
open={alertDialogOpen}
|
||||
onClose={handleClose()}
|
||||
open={silenceEntry.deleteOpen}
|
||||
onClose={handleDeleteClose()}
|
||||
sx={{ "& .MuiDialog-paper": { width: "80%", maxHeight: 435 } }}
|
||||
maxWidth="xs"
|
||||
>
|
||||
<DialogTitle>Silence Alert</DialogTitle>
|
||||
<DialogContent></DialogContent>
|
||||
<DialogTitle>Resume Alerting</DialogTitle>
|
||||
<DialogContent>Are you sure you want to resume alerting?</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose()}>Cancel</Button>
|
||||
<Button onClick={handleClose(true)} autoFocus>
|
||||
Silence
|
||||
<Button onClick={handleDeleteClose()}>Cancel</Button>
|
||||
<Button onClick={handleDeleteClose(true)} autoFocus>
|
||||
Remove
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<SilenceDialog
|
||||
keepMounted
|
||||
open={silenceEntry.open}
|
||||
onClose={handleClose}
|
||||
silence={silenceEntry}
|
||||
/>
|
||||
|
||||
<SpeedDial
|
||||
ariaLabel="Silence Alert"
|
||||
sx={{ position: "fixed", bottom: 16, right: 16 }}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect, useContext } from "react";
|
||||
import StoreContext from "../ctx/StoreContext.jsx";
|
||||
import JobContext from "../ctx/JobContext.jsx";
|
||||
import CatalogBox from "./components/CatalogBox.jsx";
|
||||
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
|
@ -35,6 +36,9 @@ export default function Catalog() {
|
|||
clearOnUnmount
|
||||
/>
|
||||
<h6>{store.catalogSearch}</h6>
|
||||
{store.catalog.map((v, i) => (
|
||||
<CatalogBox key={i} catalogTest={v} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useState, useContext } from "react";
|
||||
import StoreContext from "../ctx/StoreContext.jsx";
|
||||
import JobContext from "../ctx/JobContext.jsx";
|
||||
import SilenceDialog from "./components/SilenceDialog.jsx";
|
||||
|
||||
import SpeedDial from "@mui/material/SpeedDial";
|
||||
import SpeedDialAction from "@mui/material/SpeedDialAction";
|
||||
|
@ -20,9 +21,27 @@ import FailingBox from "./components/FailingBox.jsx";
|
|||
export default function Failing() {
|
||||
const { state: jobState, retryAll, activeJobStates } = useContext(JobContext);
|
||||
|
||||
const { state: store, updateStore } = useContext(StoreContext);
|
||||
const {
|
||||
state: store,
|
||||
updateStore,
|
||||
silenceRequest,
|
||||
} = useContext(StoreContext);
|
||||
const { failing } = store;
|
||||
|
||||
const [silenceEntry, setSilenceEntry] = useState({ open: false });
|
||||
|
||||
const closeSilence = () => setSilenceEntry({ ...silenceEntry, open: false });
|
||||
|
||||
const handleSilenceClose = (silenceReq) => {
|
||||
closeSilence();
|
||||
if (!silenceReq) return;
|
||||
silenceRequest(silenceReq);
|
||||
};
|
||||
|
||||
const editSilence = (silence) => () => {
|
||||
setSilenceEntry({ ...silence, open: true });
|
||||
};
|
||||
|
||||
/* TODO
|
||||
for(var j of activeJobStates()){
|
||||
const failingTest = failing.find((f)=>f.name===j.testName);
|
||||
|
@ -32,6 +51,7 @@ const failingTest = failing.find((f)=>f.name===j.testName);
|
|||
|
||||
const [retryAllOpen, setRetryAllOpen] = useState(false);
|
||||
const retryAllClick = () => setRetryAllOpen(!retryAllOpen);
|
||||
|
||||
const handleClose = (confirmed) => () => {
|
||||
retryAllClick();
|
||||
if (!confirmed) return;
|
||||
|
@ -41,7 +61,7 @@ const failingTest = failing.find((f)=>f.name===j.testName);
|
|||
return (
|
||||
<div className="failing">
|
||||
{failing.map((v, i) => (
|
||||
<FailingBox key={i} failingTest={v} />
|
||||
<FailingBox key={i} failingTest={v} silenceClick={editSilence(v)} />
|
||||
))}
|
||||
|
||||
<Dialog
|
||||
|
@ -63,7 +83,12 @@ const failingTest = failing.find((f)=>f.name===j.testName);
|
|||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<SilenceDialog
|
||||
keepMounted
|
||||
open={silenceEntry.open}
|
||||
onClose={handleSilenceClose}
|
||||
silence={silenceEntry}
|
||||
/>
|
||||
<SpeedDial
|
||||
ariaLabel="Retry All"
|
||||
sx={{ position: "fixed", bottom: 16, right: 16 }}
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
import { useState, useContext } from "react";
|
||||
import { useState, useContext, useEffect } from "react";
|
||||
import StoreContext from "../ctx/StoreContext.jsx";
|
||||
import JobContext from "../ctx/JobContext.jsx";
|
||||
import JobBox from "./components/JobBox.jsx";
|
||||
import JobTestSelector from "./components/JobTestSelector.jsx";
|
||||
|
||||
import Button from "@mui/material/Button";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
|
||||
import ClickAwayListener from "@mui/material/ClickAwayListener";
|
||||
import SpeedDial from "@mui/material/SpeedDial";
|
||||
|
@ -20,11 +29,8 @@ export default function Jobs() {
|
|||
} = useContext(JobContext);
|
||||
|
||||
const { state: store, updateStore } = useContext(StoreContext);
|
||||
|
||||
const [quickOpen, setQuickOpen] = useState(false);
|
||||
|
||||
const quickOpenClick = () => setQuickOpen(!quickOpen);
|
||||
const quickOpenClose = () => setQuickOpen(false);
|
||||
const [jobDialogOpen, setJobDialogOpen] = useState(false);
|
||||
|
||||
const actions = [
|
||||
{ name: "Suite", icon: <ViewCarouselIcon /> },
|
||||
|
@ -32,8 +38,41 @@ export default function Jobs() {
|
|||
{ name: "Manual", icon: <PageviewIcon /> },
|
||||
];
|
||||
|
||||
const quickOpenClick = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if(!store.simplifiedControls) return setQuickOpen(!quickOpen);
|
||||
setJobDialogOpen(true);
|
||||
}
|
||||
const quickOpenClose = () => setQuickOpen(false);
|
||||
|
||||
|
||||
const handleClickOpen = () => setJobDialogOpen(true);
|
||||
const handleClose = () => setJobDialogOpen(false);
|
||||
const [queued, setQueued] = useState([]);
|
||||
useEffect(() => {
|
||||
}, [jobState.jobs]);
|
||||
|
||||
return (
|
||||
<div className="jobs">
|
||||
{jobState.jobs.map((v, i) => (
|
||||
<JobBox key={i} job={v} />
|
||||
))}
|
||||
|
||||
<Dialog open={jobDialogOpen} onClose={handleClose} maxWidth="xs">
|
||||
<DialogTitle>New Job</DialogTitle>
|
||||
<DialogContent>
|
||||
<span>Some Selectors</span>
|
||||
<JobTestSelector queued={queued} availableTests={store.catalog} setQueued={setQueued} />
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose}>Cancel</Button>
|
||||
<Button onClick={handleClose} autoFocus>
|
||||
Start
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<ClickAwayListener onClickAway={quickOpenClose}>
|
||||
<SpeedDial
|
||||
ariaLabel="New Job"
|
||||
|
@ -47,6 +86,7 @@ export default function Jobs() {
|
|||
key={action.name}
|
||||
icon={action.icon}
|
||||
tooltipTitle={action.name}
|
||||
onClick={handleClickOpen}
|
||||
/>
|
||||
))}
|
||||
</SpeedDial>
|
||||
|
|
|
@ -29,7 +29,7 @@ export default function Settings(props) {
|
|||
const optionSettings = {
|
||||
region: {
|
||||
title: "Region",
|
||||
options: [],
|
||||
options: regions,
|
||||
current: store.defaultRegion,
|
||||
onSelect: (r) => updateStore({ defaultRegion: r }),
|
||||
},
|
||||
|
@ -106,7 +106,7 @@ export default function Settings(props) {
|
|||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem button divider>
|
||||
<ListItem button divider onClick={handleToggle("simplifiedControls")}>
|
||||
<ListItemText primary="Simplified Controls" />
|
||||
<Switch
|
||||
edge="end"
|
||||
|
@ -115,7 +115,7 @@ export default function Settings(props) {
|
|||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem button divider>
|
||||
<ListItem button divider onClick={handleToggle("focusJob")}>
|
||||
<ListItemText primary="Focus New Jobs" />
|
||||
<Switch
|
||||
edge="end"
|
||||
|
@ -124,6 +124,17 @@ export default function Settings(props) {
|
|||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem button divider onClick={handleToggle("logAppDetails")}>
|
||||
<ListItemText primary="Log App Details" />
|
||||
<Switch
|
||||
edge="end"
|
||||
onChange={handleToggle("logAppDetails")}
|
||||
checked={store.logAppDetails}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
|
||||
|
||||
<MultiOptionDialog
|
||||
id="multi-options-menu"
|
||||
keepMounted
|
||||
|
|
49
src/views/alt-comps/JobManualSelector.jsx
Normal file
49
src/views/alt-comps/JobManualSelector.jsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import ListItemAvatar from "@mui/material/ListItemAvatar";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
|
||||
export default function JobManualSelector(props){
|
||||
const {availableTests} = props;
|
||||
const [queued, setQueued] = useState([]);
|
||||
useEffect(()=>{
|
||||
},[availableTests]);
|
||||
|
||||
const queueTest = (test) => () => {
|
||||
const q = [...queued];
|
||||
const testIndex = q.indexOf(test);
|
||||
if(testIndex === -1) q.push(test);
|
||||
else q.splice(testIndex, 1);
|
||||
setQueued(q);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box style={{ overflow: "auto", maxHeight: 250 }}>
|
||||
<List>
|
||||
{availableTests.map((v, i) => (
|
||||
<ListItem
|
||||
key={i}
|
||||
secondaryAction={<Checkbox edge="end" checked={queued.includes(v)} />}
|
||||
disablePadding
|
||||
onClick={queueTest(v)}
|
||||
>
|
||||
<ListItemButton key={i}>
|
||||
<ListItemText
|
||||
primary={
|
||||
<span>
|
||||
{v.class}#<strong>{v.name}</strong>
|
||||
</span>
|
||||
}
|
||||
style={{ wordBreak: "break-word" }}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
);
|
||||
}
|
25
src/views/alt-comps/JobTestSelector.jsx
Normal file
25
src/views/alt-comps/JobTestSelector.jsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { useState, useEffect } from "react";
|
||||
|
||||
import Button from "@mui/material/Button";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
|
||||
export default function JobTestSelector(props){
|
||||
const {jobDialogOpen, handleClose, dialogTitle, testSelector} = props;
|
||||
return (
|
||||
<Dialog open={jobDialogOpen} onClose={handleClose} maxWidth="xs">
|
||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
||||
<DialogContent>
|
||||
{testSelector}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose}>Cancel</Button>
|
||||
<Button onClick={handleClose} autoFocus>
|
||||
Start
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
|
@ -9,7 +9,7 @@ import Typography from "@mui/material/Typography";
|
|||
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import NotificationsIcon from "@mui/icons-material/Notifications";
|
||||
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
import Stack from "@mui/material/Stack";
|
||||
|
@ -22,12 +22,12 @@ export default function CatalogBox(props) {
|
|||
class: testClass,
|
||||
repo: testRepo,
|
||||
isCompound,
|
||||
type: testType
|
||||
type: testType,
|
||||
} = catalogTest;
|
||||
|
||||
const { state: store, updateStore } = useContext(StoreContext);
|
||||
|
||||
const { state: jobState, jobBuilder} = useContext(JobContext);
|
||||
const { state: jobState, jobBuilder } = useContext(JobContext);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
|
@ -36,26 +36,30 @@ export default function CatalogBox(props) {
|
|||
function Actions() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<IconButton aria-label="play" component="span" color="primary">
|
||||
<NotificationsIcon />
|
||||
</IconButton>
|
||||
<IconButton color="error" aria-label="delete" component="span">
|
||||
<DeleteIcon />
|
||||
<IconButton color="success" aria-label="play" component="span">
|
||||
<PlayArrowIcon />
|
||||
</IconButton>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion expanded={open} disableGutters={true} onChange={toggleOpen}
|
||||
square>
|
||||
<Accordion
|
||||
expanded={open}
|
||||
disableGutters={true}
|
||||
onChange={toggleOpen}
|
||||
square
|
||||
>
|
||||
<AccordionSummary
|
||||
style={{
|
||||
backgroundColor: "rgba(0, 0, 0, .03)",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Typography component={"span"} style={{ wordBreak: "break-word" }}>
|
||||
<Typography
|
||||
component={"span"}
|
||||
style={{ wordBreak: "break-word", margin: "auto 0" }}
|
||||
>
|
||||
{`${testClass}#`}
|
||||
<Box fontWeight="bold" display="inline">
|
||||
{testName}
|
||||
|
@ -77,10 +81,12 @@ export default function CatalogBox(props) {
|
|||
>
|
||||
<Actions />
|
||||
</Stack>
|
||||
<AccordionDetails>
|
||||
<Typography>{JSON.stringify(catalogTest)}</Typography>
|
||||
</AccordionDetails>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography component={"span"} style={{ wordBreak: "break-word" }}>
|
||||
{JSON.stringify(catalogTest)}
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,10 +27,8 @@ export default function CatalogSearch(props) {
|
|||
placeholder="Search Catalog"
|
||||
inputProps={{ "aria-label": `search catalog` }}
|
||||
onChange={onChange}
|
||||
fullWidth
|
||||
/>
|
||||
<IconButton type="submit" sx={{ p: "10px" }} aria-label="search">
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
<Divider sx={{ height: 26, m: 0.5 }} orientation="vertical" />
|
||||
<IconButton
|
||||
sx={{ p: "10px", mr: 0.5 }}
|
||||
|
|
|
@ -27,7 +27,7 @@ import Box from "@mui/material/Box";
|
|||
const stopPropagation = (e) => e.stopPropagation() && e.preventDefault();
|
||||
|
||||
export default function FailingBox(props) {
|
||||
const { failingTest } = props;
|
||||
const { failingTest, silenceClick } = props;
|
||||
|
||||
const {
|
||||
class: testClass,
|
||||
|
@ -92,6 +92,7 @@ export default function FailingBox(props) {
|
|||
aria-label="silence"
|
||||
component="span"
|
||||
color={silencedUntil ? "primary" : "default"}
|
||||
onClick={silenceClick}
|
||||
>
|
||||
<NotificationsIcon />
|
||||
</IconButton>
|
||||
|
@ -152,14 +153,18 @@ export default function FailingBox(props) {
|
|||
direction="row"
|
||||
sx={{
|
||||
ml: "auto",
|
||||
display: { xs: "none", sm: "none", md: "flex", lg: "flex" },
|
||||
mb: "auto",
|
||||
mt: "auto",
|
||||
display: { xs: "none", sm: "none", md: "block", lg: "block" },
|
||||
}}
|
||||
>
|
||||
<Actions />
|
||||
</Stack>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography>{failedMessage}</Typography>
|
||||
<Typography component={"span"} style={{ wordBreak: "break-word" }}>
|
||||
{failedMessage}
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
|
67
src/views/components/JobBox.jsx
Normal file
67
src/views/components/JobBox.jsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import React, { useState, useContext } from "react";
|
||||
import StoreContext from "../../ctx/StoreContext.jsx";
|
||||
import JobContext, { jobStatus } from "../../ctx/JobContext.jsx";
|
||||
|
||||
import Accordion from "@mui/material/Accordion";
|
||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
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";
|
||||
|
||||
export default function JobBox(props) {
|
||||
const { job } = props;
|
||||
|
||||
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 (
|
||||
<Accordion expanded={false} disableGutters={true} square>
|
||||
<AccordionSummary
|
||||
style={{
|
||||
backgroundColor: "rgba(0, 0, 0, .03)",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component={"span"}
|
||||
style={{ wordBreak: "break-word", margin: "auto 0" }}
|
||||
>
|
||||
{name}
|
||||
</Typography>
|
||||
<Stack sx={{ ml: "auto" }}>
|
||||
<IconButton aria-label="retry" component="span">
|
||||
{jobIcon()}
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</AccordionSummary>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
47
src/views/components/JobTestSelector.jsx
Normal file
47
src/views/components/JobTestSelector.jsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
|
||||
export default function JobTestSelector(props){
|
||||
const {availableTests, queued, setQueued} = props;
|
||||
|
||||
useEffect(()=>{},[availableTests]);
|
||||
|
||||
const queueTest = (test) => () => {
|
||||
const q = [...queued];
|
||||
const testIndex = q.indexOf(test);
|
||||
if(testIndex === -1) q.push(test);
|
||||
else q.splice(testIndex, 1);
|
||||
setQueued(q);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box style={{ overflow: "auto", maxHeight: 250 }}>
|
||||
<List>
|
||||
{availableTests.map((v, i) => (
|
||||
<ListItem
|
||||
key={i}
|
||||
secondaryAction={<Checkbox edge="end" checked={queued.includes(v)} />}
|
||||
disablePadding
|
||||
onClick={queueTest(v)}
|
||||
>
|
||||
<ListItemButton key={i}>
|
||||
<ListItemText
|
||||
primary={
|
||||
<span>
|
||||
{v.class}#<strong>{v.name}</strong>
|
||||
</span>
|
||||
}
|
||||
style={{ wordBreak: "break-word" }}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useContext } from "react";
|
||||
import { useState, useContext, useEffect } from "react";
|
||||
import StoreContext from "../../ctx/StoreContext.jsx";
|
||||
|
||||
import Button from "@mui/material/Button";
|
||||
|
@ -6,6 +6,7 @@ import DialogTitle from "@mui/material/DialogTitle";
|
|||
import DialogContent from "@mui/material/DialogContent";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
export default function SilenceDialog(props) {
|
||||
const { silence, open, onClose } = props;
|
||||
|
@ -18,14 +19,15 @@ export default function SilenceDialog(props) {
|
|||
|
||||
const { state: store, updateStore } = useContext(StoreContext);
|
||||
|
||||
const upsertSilence = () => {
|
||||
console.log("Would upsert silence", silenceEntry);
|
||||
};
|
||||
|
||||
const handleCancel = () => onClose();
|
||||
|
||||
const handleOk = () => onClose(silenceEntry);
|
||||
|
||||
const updateSilence = (silenceType) => (e) => {
|
||||
silenceEntry[silenceType] = e.target.value;
|
||||
setSilenceEntry({ ...silenceEntry });
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
sx={{ "& .MuiDialog-paper": { width: "80%", maxHeight: 435 } }}
|
||||
|
@ -34,7 +36,30 @@ export default function SilenceDialog(props) {
|
|||
>
|
||||
<DialogTitle>Silence Alert</DialogTitle>
|
||||
<DialogContent>
|
||||
<span>{JSON.stringify(silenceEntry)}</span>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Test Name"
|
||||
variant="standard"
|
||||
defaultValue={silence.name ?? ""}
|
||||
onChange={updateSilence("name")}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Test Method"
|
||||
variant="standard"
|
||||
defaultValue={silence.method ?? ""}
|
||||
onChange={updateSilence("method")}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Test Class"
|
||||
variant="standard"
|
||||
defaultValue={silence.class ?? ""}
|
||||
onChange={updateSilence("class")}
|
||||
margin="normal"
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={handleCancel}>
|
||||
|
|
|
@ -7,12 +7,12 @@ import Typography from "@mui/material/Typography";
|
|||
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import NotificationsIcon from "@mui/icons-material/Notifications";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
|
||||
import Stack from "@mui/material/Stack";
|
||||
|
||||
export default function SilencingBox(props) {
|
||||
const { silenceEntry } = props;
|
||||
const { silenceEntry, editSilence, removeSilence } = props;
|
||||
|
||||
const {
|
||||
name: testName,
|
||||
|
@ -27,10 +27,20 @@ export default function SilencingBox(props) {
|
|||
function Actions() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<IconButton aria-label="modify" component="span" color="primary">
|
||||
<NotificationsIcon />
|
||||
<IconButton
|
||||
aria-label="modify"
|
||||
component="span"
|
||||
color="primary"
|
||||
onClick={editSilence}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<IconButton color="error" aria-label="delete" component="span">
|
||||
<IconButton
|
||||
color="error"
|
||||
aria-label="delete"
|
||||
component="span"
|
||||
onClick={removeSilence}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</React.Fragment>
|
||||
|
@ -64,7 +74,9 @@ export default function SilencingBox(props) {
|
|||
direction="row"
|
||||
sx={{
|
||||
ml: "auto",
|
||||
display: { xs: "none", sm: "none", md: "flex", lg: "flex" },
|
||||
mb: "auto",
|
||||
mt: "auto",
|
||||
display: { xs: "none", sm: "none", md: "block", lg: "block" },
|
||||
}}
|
||||
>
|
||||
<Actions />
|
Loading…
Add table
Add a link
Reference in a new issue