Refactored frontend

This commit is contained in:
Dunemask 2022-07-12 21:48:05 +00:00
parent 37613e4de1
commit 6386294887
24 changed files with 54 additions and 529 deletions

View file

@ -0,0 +1,94 @@
import { useState, useContext } from "react";
import StoreContext from "../../ctx/StoreContext.jsx";
import JobContext from "../../ctx/JobContext.jsx";
import SilenceDialog from "../alerting/SilenceDialog.jsx";
import FailingBox from "./FailingBox.jsx";
import SpeedDial from "@mui/material/SpeedDial";
import SpeedDialAction from "@mui/material/SpeedDialAction";
import SpeedDialIcon from "@mui/material/SpeedDialIcon";
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 DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import ReplayIcon from "@mui/icons-material/Replay";
export default function Failing() {
const { state: jobState, retryAll, activeJobStates } = useContext(JobContext);
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 });
};
const [retryAllOpen, setRetryAllOpen] = useState(false);
const retryAllClick = () => setRetryAllOpen(!retryAllOpen);
const handleClose = (confirmed) => () => {
retryAllClick();
if (!confirmed) return;
retryAll(store.failing);
};
return (
<div className="failing">
{failing.map((v, i) => (
<FailingBox key={i} failingTest={v} silenceClick={editSilence(v)} />
))}
<Dialog
open={retryAllOpen}
onClose={handleClose()}
sx={{ "& .MuiDialog-paper": { width: "80%", maxHeight: 435 } }}
maxWidth="xs"
>
<DialogTitle>Retry all failing tests?</DialogTitle>
<DialogContent>
<DialogContentText>
This will create x jobs and run y tests
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose()}>Cancel</Button>
<Button onClick={handleClose(true)} autoFocus>
Yes
</Button>
</DialogActions>
</Dialog>
<SilenceDialog
keepMounted
open={silenceEntry.open}
onClose={handleSilenceClose}
silence={silenceEntry}
/>
<SpeedDial
ariaLabel="Retry All"
sx={{ position: "fixed", bottom: 16, right: 16 }}
icon={<ReplayIcon />}
onClick={retryAllClick}
open={false}
/>
</div>
);
}

View file

@ -0,0 +1,247 @@
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 Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import IconButton from "@mui/material/IconButton";
import DeleteIcon from "@mui/icons-material/Delete";
import NotificationsIcon from "@mui/icons-material/Notifications";
import ReplayIcon from "@mui/icons-material/Replay";
import PhotoCameraIcon from "@mui/icons-material/PhotoCamera";
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 Badge from "@mui/material/Badge";
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
const stopPropagation = (e) => e.stopPropagation() && e.preventDefault();
export default function FailingBox(props) {
const { failingTest, silenceClick } = props;
const {
class: testClass,
name: testName,
timestamp,
silencedUntil,
type,
dailyFails,
screenshot: screenshotUrl,
recentResults,
failedMessage,
isCompound,
jobStatus: testJobStatus,
} = failingTest;
const { state: jobState, retrySingle } = useContext(JobContext);
const { state: store, updateStore, removeFailure } = useContext(StoreContext);
const [open, setOpen] = useState(false);
const toggleOpen = () => setOpen(!open);
const [removeOpen, setRemoveOpen] = useState(false);
const removeClick = () => setRemoveOpen(!removeOpen);
const handleRemoveClose = (confirmed) => (e) => {
stopPropagation(e);
setRemoveOpen(false);
if (!confirmed) return;
removeFailure(failingTest);
};
function badgeColor() {
if (dailyFails === 1) return "primary";
else if (dailyFails === 2) return "secondary";
else if (dailyFails < 6) return "warning";
return "error";
}
const retryTest = () => retrySingle(failingTest);
const jobOnClick = () => {
switch (testJobStatus) {
case jobStatus.OK:
return null;
case jobStatus.ERROR:
return retryTest;
case jobStatus.PENDING:
return null;
case jobStatus.ACTIVE:
return null;
case jobStatus.CANCELED:
return retryTest;
case jobStatus.QUEUED:
return null;
default:
return retryTest;
}
};
function jobIcon() {
switch (testJobStatus) {
case jobStatus.OK:
return <CheckIcon color="success" />;
case jobStatus.ERROR:
return <ClearIcon color="error" />;
case jobStatus.PENDING:
return <PendingIcon color="info" />;
case jobStatus.ACTIVE:
return <VisibilityIcon color="primary" />;
case jobStatus.CANCELED:
return <DoNotDisturbIcon color="warning" />;
case jobStatus.QUEUED:
return <ViewColumnIcon color="secondary" />;
default:
return <ReplayIcon />;
}
}
function Actions() {
return (
<React.Fragment>
<a href={screenshotUrl}>
<IconButton aria-label="photo" component="span">
<PhotoCameraIcon />
</IconButton>
</a>
<IconButton aria-label="retry" component="span" onClick={jobOnClick()}>
{jobIcon()}
</IconButton>
<IconButton
aria-label="silence"
component="span"
color={silencedUntil ? "primary" : "default"}
onClick={silenceClick}
>
<NotificationsIcon />
</IconButton>
<IconButton
color="error"
aria-label="delete"
component="span"
onClick={removeClick}
>
<DeleteIcon />
</IconButton>
</React.Fragment>
);
}
return (
<Accordion
expanded={open}
onChange={toggleOpen}
disableGutters={true}
square
>
<AccordionSummary
style={{
backgroundColor: "rgba(0, 0, 0, .03)",
flexWrap: "wrap",
}}
>
<Badge
sx={{ mr: 2 }}
color={badgeColor()}
badgeContent={dailyFails}
anchorOrigin={{
vertical: "top",
horizontal: "left",
}}
></Badge>
<Badge
sx={{ mr: 2 }}
style={{ whiteSpace: "nowrap", left: "3.125rem" }}
badgeContent={new Date(timestamp).toLocaleTimeString("en-US")}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
></Badge>
<Dialog
open={removeOpen}
onClose={handleRemoveClose()}
sx={{ "& .MuiDialog-paper": { width: "80%", maxHeight: 435 } }}
maxWidth="xs"
>
<DialogTitle>Remove failure?</DialogTitle>
<DialogContent>
<DialogContentText>
This will remove 1 test from the database
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleRemoveClose()}>Cancel</Button>
<Button onClick={handleRemoveClose(true)} autoFocus>
Yes
</Button>
</DialogActions>
</Dialog>
<Typography component={"span"} style={{ wordBreak: "break-word" }}>
{`${testClass}#`}
<Box fontWeight="bold" display="inline">
{testName}{" "}
</Box>
<br />
<div>
<span className="recent-results">
{recentResults.map(
(v, i) =>
(v && <CheckIcon key={i} color="success" />) || (
<ClearIcon key={i} color="error" />
)
)}
</span>
{isCompound && <ViewColumnIcon />}
</div>
</Typography>
<Stack
onClick={stopPropagation}
sx={{ ml: "auto", display: { md: "none", lg: "none", xl: "none" } }}
>
<Actions />
</Stack>
<Stack
onClick={stopPropagation}
direction="row"
sx={{
ml: "auto",
mb: "auto",
mt: "auto",
whiteSpace: "nowrap",
display: { xs: "none", sm: "none", md: "block", lg: "block" },
}}
>
<Actions />
</Stack>
</AccordionSummary>
<AccordionDetails>
<Typography component={"span"} style={{ wordBreak: "break-word" }}>
{failedMessage}
</Typography>
</AccordionDetails>
</Accordion>
);
}